GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Pull Request — master (#34)
by
unknown
04:51
created

Repository::findAll()   B

Complexity

Conditions 4
Paths 5

Size

Total Lines 28
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 19
CRAP Score 4

Importance

Changes 0
Metric Value
dl 0
loc 28
ccs 19
cts 19
cp 1
rs 8.5806
c 0
b 0
f 0
cc 4
eloc 16
nc 5
nop 0
crap 4
1
<?php
2
3
namespace JamesMoss\Flywheel;
4
5
/**
6
 * Repository
7
 *
8
 * Analageous to a table in a traditional RDBMS, a repository is a siloed
9
 * collection where documents live.
10
 */
11
class Repository
12
{
13
    protected $name;
14
    protected $path;
15
    protected $formatter;
16
    protected $queryClass;
17
    protected $documentClass;
18
19
    /**
20
     * Constructor
21
     *
22
     * @param string $name   The name of the repository. Must match /[A-Za-z0-9_-]{1,63}+/
23
     * @param Config $config The config to use for this repo
24
     */
25 32
    public function __construct($name, Config $config)
26
    {
27
        // Setup class properties
28 32
        $this->name          = $name;
29 32
        $this->path          = $config->getPath() . DIRECTORY_SEPARATOR . $name;
30 32
        $this->formatter     = $config->getOption('formatter');
31 32
        $this->queryClass    = $config->getOption('query_class');
32 32
        $this->documentClass = $config->getOption('document_class');
33
34
        // Ensure the repo name is valid
35 32
        $this->validateName($this->name);
36
37
        // Ensure directory exists and we can write there
38 30
        if (!is_dir($this->path)) {
39 9
            if (!@mkdir($this->path, 0777, true)) {
40 1
                throw new \RuntimeException(sprintf('`%s` doesn\'t exist and can\'t be created.', $this->path));
41
            }
42 29
        } else if (!is_writable($this->path)) {
43 1
            throw new \RuntimeException(sprintf('`%s` is not writable.', $this->path));
44
        }
45 28
    }
46
47
    /**
48
     * Returns the name of this repository
49
     *
50
     * @return string The name of the repo
51
     */
52 3
    public function getName()
53
    {
54 3
        return $this->name;
55
    }
56
57
    /**
58
     * Returns the filesystem path of this repository.
59
     *
60
     * @return string The path where documents are stored.
61
     */
62
    public function getPath()
63
    {
64
        return $this->path;
65
    }
66
67
    /**
68
     * A factory method that initialises and returns an instance of a Query object.
69
     *
70
     * @return Query A new Query class for this repo.
71
     */
72 1
    public function query()
73
    {
74 1
        $className = $this->queryClass;
75
76 1
        return new $className($this);
77
    }
78
79
    /**
80
     * Returns all the documents within this repo.
81
     *
82
     * @return array An array of Documents.
83
     */
84 15
    public function findAll()
85
    {
86 15
        $ext       = $this->formatter->getFileExtension();
87 15
        $files     = $this->getAllFiles();
88 15
        $documents = array();
89
90 15
        foreach ($files as $file) {
91 15
            $fp       = fopen($file, 'r');
92
            
93 15
            $contents = null;
94 15
            if(($filesize = filesize($file)) > 0) {
95 15
                $contents = fread($fp, $filesize);
96 15
            }
97
            
98 15
            fclose($fp);
99
100 15
            $data = $this->formatter->decode($contents);
101
102 15
            if (null !== $data) {
103 15
                $doc = new $this->documentClass((array) $data);
104 15
                $doc->setId($this->getIdFromPath($file, $ext));
105
106 15
                $documents[] = $doc;
107 15
            }
108 15
        }
109
110 15
        return $documents;
111
    }
112
113
    /**
114
     * Returns a single document based on it's ID
115
     *
116
     * @param  string $id The ID of the document to find
117
     *
118
     * @return Document|boolean  The document if it exists, false if not.
119
     */
120
    public function findById($id)
121
    {
122
        if(!file_exists($path = $this->getPathForDocument($id))) {
123
            return false;
124
        }
125
126
        $fp       = fopen($path, 'r');
127
        $contents = fread($fp, filesize($path));
128
        fclose($fp);
129
130
        $data = $this->formatter->decode($contents);
131
132
        if($data === null) {
133
            return false;
134
        }
135
136
        $ext = $this->formatter->getFileExtension();
137
138
        $doc = new $this->documentClass((array) $data);
139
        $doc->setId($this->getIdFromPath($path, $ext));
140
141
        return $doc;
142
    }
143
144
    /**
145
     * Store a Document in the repository.
146
     *
147
     * @param Document $document The document to store
148
     *
149
     * @return bool True if stored, otherwise false
150
     */
151 4
    public function store(DocumentInterface $document)
152
    {
153 4
        $id = $document->getId();
154
155
        // Generate an id if none has been defined
156 4
        if (is_null($id)) {
157
            $id = $document->setId($this->generateId());
158
        }
159
160 4
        if (!$this->validateId($id)) {
161
            throw new \Exception(sprintf('`%s` is not a valid document ID.', $id));
162
        }
163
164 4
        $path = $this->getPathForDocument($id);
165 4
        $data = get_object_vars($document);
166 4
        $data = $this->formatter->encode($data);
167
168 4
        if(!$this->write($path, $data)) {
169
            return false;
170
        }
171
172 4
        return $id;
173
    }
174
175
    /**
176
     * Store a Document in the repository, but only if it already
177
     * exists. The document must have an ID.
178
     *
179
     * @param Document $document The document to store
180
     *
181
     * @return bool True if stored, otherwise false
182
     */
183 1
    public function update(DocumentInterface $document)
184
    {
185 1
        if (!$document->getId()) {
186
            return false;
187
        }
188
189 1
        $oldPath = $this->getPathForDocument($document->getInitialId());
190
191 1
        if(!file_exists($oldPath)) {
192
            return false;
193
        }
194
195
        // If the ID has changed we need to delete the old document.
196 1
        if($document->getId() !== $document->getInitialId()) {
197 1
            if(file_exists($oldPath)) {
198 1
                unlink($oldPath);
199 1
            }
200 1
        }
201
202 1
        return $this->store($document);
203
    }
204
205
    /**
206
     * Delete a document from the repository using its ID.
207
     *
208
     * @param mixed $id The ID of the document (or the document itself) to delete
209
     *
210
     * @return boolean True if deleted, false if not.
211
     */
212 3
    public function delete($id)
213
    {
214 3
        if ($id instanceof DocumentInterface) {
215
            $id = $id->getId();
216
        }
217
218 3
        $path = $this->getPathForDocument($id);
219
220 3
        return unlink($path);
221
    }
222
223
    /**
224
     * Get the filesystem path for a document based on it's ID.
225
     *
226
     * @param string $id The ID of the document.
227
     *
228
     * @return string The full filesystem path of the document.
229
     */
230 4
    public function getPathForDocument($id)
231
    {
232 4
        if(!$this->validateId($id)) {
233
            throw new \Exception(sprintf('`%s` is not a valid document ID.', $id));
234
        }
235
236 4
        return $this->path . DIRECTORY_SEPARATOR . $this->getFilename($id);
237
    }
238
239
    /**
240
     * Gets just the filename for a document based on it's ID.
241
     *
242
     * @param string $id The ID of the document.
243
     *
244
     * @return string The filename of the document, including extension.
245
     */
246 4
    public function getFilename($id)
247
    {
248 4
        return $id . '.' . $this->formatter->getFileExtension();
249
    }
250
251
    /**
252
     * Get an array containing the path of all files in this repository
253
     *
254
     * @return array An array, item is a file
255
     */
256 14
    public function getAllFiles()
257
    {
258 14
        $ext       = $this->formatter->getFileExtension();
259 14
        $filesystemIterator = new \FilesystemIterator($this->path, \FilesystemIterator::SKIP_DOTS);
260 14
        $files = new \RegexIterator($filesystemIterator, "/\\.{$ext}$/");
261
262 14
        return $files;
263
    }
264
265
    /**
266
     * Writes data to the filesystem.
267
     *
268
     * @todo  Abstract this into a filesystem layer.
269
     *
270
     * @param  string $path     The absolute file path to write to
271
     * @param  string $contents The contents of the file to write
272
     *
273
     * @return boolean          Returns true if write was successful, false if not.
274
     */
275 4
    protected function write($path, $contents)
276
    {
277 4
        $fp = fopen($path, 'w');
278 4
        if(!flock($fp, LOCK_EX)) {
279
            return false;
280
        }
281 4
        $result = fwrite($fp, $contents);
282 4
        flock($fp, LOCK_UN);
283 4
        fclose($fp);
284
285 4
        return $result !== false;
286
    }
287
288
    /**
289
     * Validates the name of the repo to ensure it can be stored in the
290
     * filesystem.
291
     *
292
     * @param string $name The name to validate against
293
     *
294
     * @return bool Returns true if valid. Throws an exception if not.
295
     */
296 32
    protected function validateName($name)
297
    {
298 32
        if (!preg_match('/^[0-9A-Za-z\_\-]{1,63}$/', $name)) {
299 2
            throw new \Exception(sprintf('`%s` is not a valid repository name.', $name));
300
        }
301
302 30
        return true;
303
    }
304
305
    /**
306
     * Checks to see if a document ID is valid
307
     *
308
     * @param  string $id The ID to check
309
     *
310
     * @return bool     True if valid, otherwise false
311
     */
312 4
    protected function validateId($id)
313
    {
314 4
        return (boolean)preg_match('/^[^\\/\\?\\*:;{}\\\\\\n]+$/us', $id);
315
    }
316
317
    /**
318
     * Generates a random, unique ID for a document. The result is returned in
319
     * base62. This keeps it shorted but still human readable if shared in URLs.
320
     *
321
     * @return string The generated ID.
322
     */
323
    protected function generateId()
324
    {
325
        static $choices = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
326
        $id = '';
327
        while (strlen($id) < 9) {
328
            $id .= $choices[ mt_rand(0, strlen($choices) - 1) ];
329
        }
330
        return $id;
331
    }
332
333
    /**
334
     * Get a document's ID base on its filesystem path
335
     *
336
     * @param  string $path The full path to the file (including file extension)
337
     * @param  string $ext  The file extension (without the period)
338
     *
339
     * @return string       The ID of the document
340
     */
341 14
    protected function getIdFromPath($path, $ext)
342
    {
343 14
        return basename($path, '.' . $ext);
344
    }
345
346
}
347