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.

Repository   B
last analyzed

Complexity

Total Complexity 36

Size/Duplication

Total Lines 331
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 2

Test Coverage

Coverage 71.93%

Importance

Changes 0
Metric Value
wmc 36
lcom 2
cbo 2
dl 0
loc 331
rs 8.8
c 0
b 0
f 0
ccs 82
cts 114
cp 0.7193

17 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 21 4
A getName() 0 4 1
A getPath() 0 4 1
A query() 0 6 1
A findAll() 0 23 3
A findById() 0 23 3
B store() 0 23 4
B update() 0 21 5
A delete() 0 10 2
A getPathForDocument() 0 8 2
A getFilename() 0 4 1
A getAllFiles() 0 8 1
A write() 0 12 2
A validateName() 0 8 2
A validateId() 0 4 1
A generateId() 0 9 2
A getIdFromPath() 0 4 1
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 15
            $contents = fread($fp, filesize($file));
93 15
            fclose($fp);
94
95 15
            $data = $this->formatter->decode($contents);
96
97 15
            if (null !== $data) {
98 15
                $doc = new $this->documentClass((array) $data);
99 15
                $doc->setId($this->getIdFromPath($file, $ext));
100
101 15
                $documents[] = $doc;
102 15
            }
103 15
        }
104
105 15
        return $documents;
106
    }
107
108
    /**
109
     * Returns a single document based on it's ID
110
     *
111
     * @param  string $id The ID of the document to find
112
     *
113
     * @return Document|boolean  The document if it exists, false if not.
114
     */
115
    public function findById($id)
116
    {
117
        if(!file_exists($path = $this->getPathForDocument($id))) {
118
            return false;
119
        }
120
121
        $fp       = fopen($path, 'r');
122
        $contents = fread($fp, filesize($path));
123
        fclose($fp);
124
125
        $data = $this->formatter->decode($contents);
126
127
        if($data === null) {
128
            return false;
129
        }
130
131
        $ext = $this->formatter->getFileExtension();
132
133
        $doc = new $this->documentClass((array) $data);
134
        $doc->setId($this->getIdFromPath($path, $ext));
135
136
        return $doc;
137
    }
138
139
    /**
140
     * Store a Document in the repository.
141
     *
142
     * @param Document $document The document to store
143
     *
144
     * @return bool True if stored, otherwise false
145
     */
146 4
    public function store(DocumentInterface $document)
147
    {
148 4
        $id = $document->getId();
149
150
        // Generate an id if none has been defined
151 4
        if (is_null($id)) {
152
            $id = $document->setId($this->generateId());
153
        }
154
155 4
        if (!$this->validateId($id)) {
156
            throw new \Exception(sprintf('`%s` is not a valid document ID.', $id));
157
        }
158
159 4
        $path = $this->getPathForDocument($id);
160 4
        $data = get_object_vars($document);
161 4
        $data = $this->formatter->encode($data);
162
163 4
        if(!$this->write($path, $data)) {
164
            return false;
165
        }
166
167 4
        return $id;
168
    }
169
170
    /**
171
     * Store a Document in the repository, but only if it already
172
     * exists. The document must have an ID.
173
     *
174
     * @param Document $document The document to store
175
     *
176
     * @return bool True if stored, otherwise false
177
     */
178 1
    public function update(DocumentInterface $document)
179
    {
180 1
        if (!$document->getId()) {
181
            return false;
182
        }
183
184 1
        $oldPath = $this->getPathForDocument($document->getInitialId());
185
186 1
        if(!file_exists($oldPath)) {
187
            return false;
188
        }
189
190
        // If the ID has changed we need to delete the old document.
191 1
        if($document->getId() !== $document->getInitialId()) {
192 1
            if(file_exists($oldPath)) {
193 1
                unlink($oldPath);
194 1
            }
195 1
        }
196
197 1
        return $this->store($document);
198
    }
199
200
    /**
201
     * Delete a document from the repository using its ID.
202
     *
203
     * @param mixed $id The ID of the document (or the document itself) to delete
204
     *
205
     * @return boolean True if deleted, false if not.
206
     */
207 3
    public function delete($id)
208
    {
209 3
        if ($id instanceof DocumentInterface) {
210
            $id = $id->getId();
211
        }
212
213 3
        $path = $this->getPathForDocument($id);
214
215 3
        return unlink($path);
216
    }
217
218
    /**
219
     * Get the filesystem path for a document based on it's ID.
220
     *
221
     * @param string $id The ID of the document.
222
     *
223
     * @return string The full filesystem path of the document.
224
     */
225 4
    public function getPathForDocument($id)
226
    {
227 4
        if(!$this->validateId($id)) {
228
            throw new \Exception(sprintf('`%s` is not a valid document ID.', $id));
229
        }
230
231 4
        return $this->path . DIRECTORY_SEPARATOR . $this->getFilename($id);
232
    }
233
234
    /**
235
     * Gets just the filename for a document based on it's ID.
236
     *
237
     * @param string $id The ID of the document.
238
     *
239
     * @return string The filename of the document, including extension.
240
     */
241 4
    public function getFilename($id)
242
    {
243 4
        return $id . '.' . $this->formatter->getFileExtension();
244
    }
245
246
    /**
247
     * Get an array containing the path of all files in this repository
248
     *
249
     * @return array An array, item is a file
250
     */
251 14
    public function getAllFiles()
252
    {
253 14
        $ext       = $this->formatter->getFileExtension();
254 14
        $filesystemIterator = new \FilesystemIterator($this->path, \FilesystemIterator::SKIP_DOTS);
255 14
        $files = new \RegexIterator($filesystemIterator, "/\\.{$ext}$/");
256
257 14
        return $files;
258
    }
259
260
    /**
261
     * Writes data to the filesystem.
262
     *
263
     * @todo  Abstract this into a filesystem layer.
264
     *
265
     * @param  string $path     The absolute file path to write to
266
     * @param  string $contents The contents of the file to write
267
     *
268
     * @return boolean          Returns true if write was successful, false if not.
269
     */
270 4
    protected function write($path, $contents)
271
    {
272 4
        $fp = fopen($path, 'w');
273 4
        if(!flock($fp, LOCK_EX)) {
274
            return false;
275
        }
276 4
        $result = fwrite($fp, $contents);
277 4
        flock($fp, LOCK_UN);
278 4
        fclose($fp);
279
280 4
        return $result !== false;
281
    }
282
283
    /**
284
     * Validates the name of the repo to ensure it can be stored in the
285
     * filesystem.
286
     *
287
     * @param string $name The name to validate against
288
     *
289
     * @return bool Returns true if valid. Throws an exception if not.
290
     */
291 32
    protected function validateName($name)
292
    {
293 32
        if (!preg_match('/^[0-9A-Za-z\_\-]{1,63}$/', $name)) {
294 2
            throw new \Exception(sprintf('`%s` is not a valid repository name.', $name));
295
        }
296
297 30
        return true;
298
    }
299
300
    /**
301
     * Checks to see if a document ID is valid
302
     *
303
     * @param  string $id The ID to check
304
     *
305
     * @return bool     True if valid, otherwise false
306
     */
307 4
    protected function validateId($id)
308
    {
309 4
        return (boolean)preg_match('/^[^\\/\\?\\*:;{}\\\\\\n]+$/us', $id);
310
    }
311
312
    /**
313
     * Generates a random, unique ID for a document. The result is returned in
314
     * base62. This keeps it shorted but still human readable if shared in URLs.
315
     *
316
     * @return string The generated ID.
317
     */
318
    protected function generateId()
319
    {
320
        static $choices = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
321
        $id = '';
322
        while (strlen($id) < 9) {
323
            $id .= $choices[ mt_rand(0, strlen($choices) - 1) ];
324
        }
325
        return $id;
326
    }
327
328
    /**
329
     * Get a document's ID base on its filesystem path
330
     *
331
     * @param  string $path The full path to the file (including file extension)
332
     * @param  string $ext  The file extension (without the period)
333
     *
334
     * @return string       The ID of the document
335
     */
336 14
    protected function getIdFromPath($path, $ext)
337
    {
338 14
        return basename($path, '.' . $ext);
339
    }
340
341
}
342