Passed
Push — main ( 23d81d...4ea6b8 )
by Marc
03:52
created

AbstractIndexer::parseDirectory()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 3
c 1
b 0
f 0
nc 2
nop 1
dl 0
loc 5
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
        if ((\is_dir($this->parentDir.DS.'cache'.DS.'indexes')) === false) {
46
            $dir = $this->parentDir.DS.'cache'.DS.'indexes';
47
            if (\mkdir($dir, MODE, true) === false) {
48
                throw new InternalException("Unable to create cache/indexes directory [$dir]"); // @codeCoverageIgnore
49
            }
50
            $this->reindex();
51
        }
52
    }
53
54
    /**
55
     * Load the given index file.
56
     * @param string $filename
57
     * @throws InternalException
58
     * @throws InvalidArgumentException
59
     * @return array<string, Element>
60
     */
61
    protected function loadIndex(string $filename): array {
62
        if (\file_exists($filename) === false) {
63
            throw new InvalidArgumentException("Index file does not exist [$filename]. Call 'redindex()'"); // @codeCoverageIgnore
64
        }
65
        if (($data = \file_get_contents($filename)) === false) {
66
            throw new InternalException("file_get_contents() failed [$filename]"); // @codeCoverageIgnore
67
        }
68
        if (($data = \unserialize($data)) === false) {
69
            throw new InternalException("unserialize() failed [$filename]"); // @codeCoverageIgnore
70
        }
71
        return $data;
72
    }
73
74
    /**
75
     * Recursively scans a folder heirarchy returning the all the files and folders
76
     * in an array.
77
     * @return array<int, string>
78
     * @throws InternalException
79
     */
80
    protected function scanDirectory(string $rootDir): array {
81
        static $files = [];
82
        if (($handle = \opendir($rootDir)) === false) {
83
            throw new InternalException("opendir() failed [$rootDir]"); // @codeCoverageIgnore
84
        }
85
        while (($entry = \readdir($handle)) !== false) {
86
            $path = $rootDir.DS.$entry;
87
            if (\is_dir($path)) {
88
                if ($entry === '.' || $entry === '..') {
89
                    continue;
90
                }
91
                $this->scanDirectory($path);
92
                continue;
93
            }
94
            $files[] = $path;
95
        }
96
        \closedir($handle);
97
        return $files;
98
    }
99
100
    /**
101
     * @return array<int, string>
102
     * @throws InternalException
103
     */
104
    protected function parseDirectory(string $pattern): array {
105
        if (($files = \glob($pattern, GLOB_NOSORT)) === false) {
106
            throw new InternalException("glob() failed [$pattern]"); // @codeCoverageIgnore
107
        }
108
        return $files;
109
    }
110
111
    /**
112
     * Writes data to an index file, creating the file if necessary.
113
     * @param string $filepath
114
     * @param array<mixed> $index
115
     * @throws InternalException
116
     */
117
    protected function writeIndex(string $filepath, array $index): void {
118
        $index = \serialize($index);
119
        if (\file_put_contents($filepath, print_r($index, true)) === false) {
120
            throw new InternalException("file_put_contents() failed [$filepath]"); // @codeCoverageIgnore
121
        }
122
    }
123
124
    /**
125
     * Creates and populates an index Element class.
126
     * @param string $key The index key
127
     * @param string $path The filepath
128
     * @param string $section 'pages', 'posts', 'categories' or 'tags'
129
     * @param string $optional When populating with variable arguments, use the
130
     * following <b>named parameters<b>:
131
     * <ul>
132
     *   <li>type:</li>
133
     *   <li>category:</li>
134
     *   <li>username:</li>
135
     *   <li>date:</li>
136
     *   <li>tags:</li>
137
     * </ul>
138
     * @return Element stdClass
139
     */
140
    protected function createElementClass(string $key, string $path, string $section, string ...$optional): Element {
141
        $obj = new Element();
142
        $obj->key = $key;
0 ignored issues
show
Bug introduced by
The property key does not seem to exist on html_go\indexing\Element.
Loading history...
143
        $obj->path = $path;
0 ignored issues
show
Bug introduced by
The property path does not seem to exist on html_go\indexing\Element.
Loading history...
144
        $obj->section = $section;
1 ignored issue
show
Bug introduced by
The property section does not seem to exist on html_go\indexing\Element.
Loading history...
145
        $obj->type = $this->checkSetOrDefault($optional, 'type', EMPTY_VALUE);
1 ignored issue
show
Bug introduced by
The property type does not seem to exist on html_go\indexing\Element.
Loading history...
146
        $obj->category = $this->checkSetOrDefault($optional, 'category', EMPTY_VALUE);
1 ignored issue
show
Bug introduced by
The property category does not seem to exist on html_go\indexing\Element.
Loading history...
147
        $obj->username = $this->checkSetOrDefault($optional, 'username', EMPTY_VALUE);
1 ignored issue
show
Bug introduced by
The property username does not seem to exist on html_go\indexing\Element.
Loading history...
148
        $obj->date = $this->checkSetOrDefault($optional, 'date', EMPTY_VALUE);
1 ignored issue
show
Bug introduced by
The property date does not seem to exist on html_go\indexing\Element.
Loading history...
149
150
        $tags = [];
151
        $tagList = EMPTY_VALUE;
152
        if (isset($optional['tags'])) {
153
            $tagList = $optional['tags'];
154
        }
155
        if (!empty($tagList)) {
156
            $tags = \explode(',', $tagList);
157
        }
158
        $obj->tags = $tags;
1 ignored issue
show
Bug introduced by
The property tags does not seem to exist on html_go\indexing\Element.
Loading history...
159
        return $obj;
160
    }
161
162
    /**
163
     * Checks if the given key is set in the given array. If so, returns the value,
164
     * otherwise returns the default value.
165
     * @param array<mixed> $ar
166
     * @param string $key
167
     * @param mixed $default
168
     * @return mixed
169
     */
170
    private function checkSetOrDefault(array $ar, string $key, mixed $default): mixed {
171
        if (isset($ar[$key])) {
172
            return $ar[$key];
173
        }
174
        return $default;
175
    }
176
177
    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...
178
}
179