Completed
Push — develop ( 667cd2...043012 )
by Vladimir
01:48
created

TrackingManager::declareTrackingNamespace()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
nc 2
nop 1
dl 0
loc 7
ccs 4
cts 4
cp 1
crap 2
rs 10
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * @copyright 2018 Vladimir Jimenez
5
 * @license   https://github.com/stakx-io/stakx/blob/master/LICENSE.md MIT
6
 */
7
8
namespace allejo\stakx\Manager;
9
10
use allejo\stakx\Document\CollectableItem;
11
use allejo\stakx\Document\JailedDocument;
12
use allejo\stakx\Document\ReadableDocument;
13
use allejo\stakx\Document\TemplateReadyDocument;
14
use allejo\stakx\Filesystem\File;
15
use allejo\stakx\Filesystem\FileExplorer;
16
use allejo\stakx\Filesystem\FileExplorerDefinition;
17
use allejo\stakx\Filesystem\FilesystemPath;
18
use allejo\stakx\RuntimeStatus;
19
use allejo\stakx\Service;
20
21
/**
22
 * Class TrackingManager.
23
 */
24
abstract class TrackingManager extends BaseManager
25
{
26
    /**
27
     * @var FileExplorer
28
     */
29
    protected $fileExplorer = null;
30
31
    /**
32
     * An array corresponding with $folderDefinitions to store metadata regarding a specificc folder.
33
     *
34
     * $folderDefinitionsOption['<folder path>'] = array()
35
     *
36
     * @var string[]
37
     */
38
    protected $folderDefinitionsOptions = [];
39
40
    /**
41
     * An array of folders which tracked items are stored in.
42
     *
43
     * $folderDefinitions[] = '<folder path>'
44
     *
45
     * @deprecated This has been superseded by `$watchedFolders`
46
     * @var string[]
47
     */
48
    protected $folderDefinitions = [];
49
50
    /**
51
     * An array of folder definitions which tracked items are stored in.
52
     *
53
     * @var FileExplorerDefinition[]
54
     */
55
    protected $watchedFolders = [];
56
57
    /**
58
     * The storage which contains the same information as $trackedItems but organized by relative file path instead of a
59
     * namespace or file name without extension.
60
     *
61
     * $trackedItemsOptions['<relative file path>'] = mixed
62
     *
63
     * @var array
64
     */
65
    protected $trackedItemsFlattened = [];
66
67
    /**
68
     * The storage used to cache any information needed for a specific FrontMatterObject or DataItem.
69
     *
70
     * For example, with a DataItem, which is just an array, the file path to the original file can be stored in this
71
     * array to be accessible in the future to refresh the contents without parsing all of the files again.
72
     *
73
     * $trackedItemsOptions['<relative file path>'] = array
74
     *
75
     * @var array
76
     */
77
    protected $trackedItemsOptions = [];
78
79
    /**
80
     * The storage used for ReadableDocument in the respective static classes.
81
     *
82
     * $trackedItems['<namespace>']['<file name w/o extension>'] = mixed
83
     * $trackedItems['<file name w/o extension>'] = mixed
84
     *
85
     * @var array
86
     */
87
    protected $trackedItems = [];
88
89
    /**
90
     * @param File|string $filePath
91
     *
92
     * @return mixed|null
93
     */
94
    public function createNewItem($filePath)
95
    {
96
        return $this->handleTrackableItem($filePath);
0 ignored issues
show
Bug introduced by
It seems like $filePath defined by parameter $filePath on line 94 can also be of type string; however, allejo\stakx\Manager\Tra...::handleTrackableItem() does only seem to accept object<allejo\stakx\Filesystem\File>, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
97
    }
98
99
    /**
100
     * @param FilesystemPath|string $filePath
101
     *
102
     * @return mixed|null
103
     */
104
    public function getTracked($filePath)
105
    {
106
        if ($this->isTracked($filePath))
0 ignored issues
show
Bug introduced by
It seems like $filePath defined by parameter $filePath on line 104 can also be of type object<allejo\stakx\Filesystem\FilesystemPath>; however, allejo\stakx\Manager\TrackingManager::isTracked() does only seem to accept string, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
107
        {
108
            return $this->trackedItemsFlattened[(string)$filePath];
109
        }
110
111
        return null;
112
    }
113
114
    /**
115
     * Check whether a file is already being tracked.
116
     *
117
     * @param string $filePath The relative path of the file
118
     *
119
     * @return bool
120
     */
121 1
    public function isTracked($filePath)
122
    {
123 1
        return array_key_exists((string)$filePath, $this->trackedItemsFlattened);
124
    }
125
126
    /**
127
     * Check to see if a given file path matches this tracker's definition and would be tracked.
128
     *
129
     * This function should be used to check whether or not to add a file to this tracker after the initial scan has
130
     * already happened.
131
     *
132
     * @param string $filePath
133
     *
134
     * @return bool True if the file is inside a tracked folder
135
     */
136
    public function shouldBeTracked($filePath)
137
    {
138
        foreach ($this->folderDefinitions as $folder)
0 ignored issues
show
Deprecated Code introduced by
The property allejo\stakx\Manager\Tra...ger::$folderDefinitions has been deprecated with message: This has been superseded by `$watchedFolders`

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
139
        {
140
            if (substr($filePath, 0, strlen($folder)) === $folder)
141
            {
142
                return true;
143
            }
144
        }
145
146
        return false;
147
    }
148
149
    /**
150
     * Update the contents of a specified file.
151
     *
152
     * @param File|string $filePath The relative path of the file
153
     *
154
     * @return mixed|null
155
     */
156
    public function refreshItem($filePath)
157
    {
158
        return $this->handleTrackableItem($filePath);
0 ignored issues
show
Bug introduced by
It seems like $filePath defined by parameter $filePath on line 156 can also be of type string; however, allejo\stakx\Manager\Tra...::handleTrackableItem() does only seem to accept object<allejo\stakx\Filesystem\File>, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
159
    }
160
161
    ///
162
    // Internal object handling
163
    ///
164
165
    /**
166
     * Initialize a namespace that will be tracked.
167
     *
168
     * @param string $namespace
169
     */
170 52
    protected function declareTrackingNamespace($namespace)
171
    {
172 52
        if (!isset($this->trackedItems[$namespace]))
173
        {
174 37
            $this->trackedItems[$namespace] = [];
175
        }
176 52
    }
177
178
    protected function addFileToTracker(File &$file)
179
    {
180
        $this->trackedItemsFlattened[$file->getRelativeFilePath()] = &$file;
181
    }
182
183
    protected function delFileFromTracker(File &$file)
184
    {
185
        unset($this->trackedItemsFlattened[$file->getRelativeFilePath()]);
186
    }
187
188
    /**
189
     * Add a ReadableDocument to the tracker.
190
     *
191
     * @param ReadableDocument $trackedItem
192
     * @param string|null      $namespace
193
     */
194 53
    protected function addObjectToTracker(ReadableDocument &$trackedItem, $namespace = null)
195
    {
196 53
        if ($namespace == null)
0 ignored issues
show
Bug introduced by
It seems like you are loosely comparing $namespace of type string|null against null; this is ambiguous if the string can be empty. Consider using a strict comparison === instead.
Loading history...
197
        {
198 1
            $this->trackedItems[$trackedItem->getIndexName()] = &$trackedItem;
199
        }
200
        else
201
        {
202 52
            $this->trackedItems[$namespace][$trackedItem->getIndexName()] = &$trackedItem;
203
        }
204
205 53
        $this->trackedItemsFlattened[$trackedItem->getIndexName()] = &$trackedItem;
206 53
    }
207
208
    /**
209
     * Remove an entry from the tracked items array.
210
     *
211
     * @param ReadableDocument $trackedItem
212
     * @param string|null      $namespace
213
     */
214
    protected function delObjectFromTracker(ReadableDocument &$trackedItem, $namespace = null)
215
    {
216
        if ($namespace == null)
0 ignored issues
show
Bug introduced by
It seems like you are loosely comparing $namespace of type string|null against null; this is ambiguous if the string can be empty. Consider using a strict comparison === instead.
Loading history...
217
        {
218
            unset($this->trackedItems[$trackedItem->getIndexName()]);
219
        }
220
        else
221
        {
222
            unset($this->trackedItems[$namespace][$trackedItem->getIndexName()]);
223
        }
224
225
        unset($this->trackedItemsFlattened[$trackedItem->getIndexName()]);
226
    }
227
228
    ///
229
    // Extra options stored for future use
230
    ///
231
232
    /**
233
     * Save a folder that is tracked by this manager and its respective options.
234
     *
235
     * @deprecated
236
     *
237
     * @param string $folderPath
238
     * @param array  $options
239
     */
240
    protected function saveFolderDefinition($folderPath, array $options = [])
241
    {
242
        $this->folderDefinitions[] = $folderPath;
0 ignored issues
show
Deprecated Code introduced by
The property allejo\stakx\Manager\Tra...ger::$folderDefinitions has been deprecated with message: This has been superseded by `$watchedFolders`

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
243
        $this->folderDefinitionsOptions[(string)$folderPath] = $options;
244
    }
245
246
    /**
247
     * Save any options related to an item needed in order to refresh the content.
248
     *
249
     * @param string $filePath
250
     * @param array  $options
251
     */
252 2
    protected function saveTrackerOptions($filePath, array $options = [])
253
    {
254 2
        $this->trackedItemsOptions[$filePath] = $options;
255 2
    }
256
257
    /**
258
     * Delete any options that were saved corresponding to an item.
259
     *
260
     * @param string $filePath
261
     */
262
    protected function forgetTrackerOptions($filePath)
263
    {
264
        unset($this->trackedItemsOptions[$filePath]);
265
    }
266
267
    ///
268
    // Handling of trackable items
269
    ///
270
271
    /**
272
     * Parse the specified folder for items to track.
273
     *
274
     * @param FileExplorerDefinition $def
275
     * @param array $options Special options that will be passed to the `static::parseTrackableItem()`
276
     *                       implementation
277
     */
278 52
    protected function scanTrackableItems(FileExplorerDefinition $def, array $options = [])
279
    {
280 52
        $this->watchedFolders[$def->folder->getAbsolutePath()] = $def;
281
282 52
        if (empty($def->excludes))
283
        {
284 52
            $def->excludes = self::$documentIgnoreList;
285
        }
286
287 52
        $fileExplorer = FileExplorer::createFromDefinition($def);
288 52
        $fileIterator = $fileExplorer->getFileIterator();
289
290 52
        foreach ($fileIterator as $file)
291
        {
292 52
            $this->handleTrackableItem($file, $options);
293
        }
294 52
    }
295
296
    /**
297
     * Handle a specific file type, parse it into the appropriate object type, and add it to the tracker.
298
     *
299
     * This function should make use of the appropriate functions:
300
     *
301
     *  - TrackingManager::addObjectToTracker()
302
     *  - TrackingManager::addFileToTracker()
303
     *  - TrackingManager::saveTrackerOptions()
304
     *
305
     * @param File  $filePath
306
     * @param array $options
307
     *
308
     * @return mixed|null
309
     */
310
    abstract protected function handleTrackableItem(File $filePath, array $options = []);
311
312
    ///
313
    // Utility functions
314
    ///
315
316
    /**
317
     * Return an array of JailedDocuments created from the tracked items.
318
     *
319
     * @param JailedDocument[] $elements An array of elements to get jailed versions of
320
     * @param \Closure $name A closure to generate the name of the element that will be used as the key in
0 ignored issues
show
Documentation introduced by
Should the type for parameter $name not be null|\Closure?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
321
     *                       this associative array.
322
     *
323
     * @return JailedDocument[]|JailedDocument[][]
324
     */
325 21
    protected static function getJailedTrackedItems(array &$elements, \Closure $name = null)
326
    {
327 21
        $jailItems = [];
328
329
        /**
330
         * @var string
331
         * @var CollectableItem|ReadableDocument $item
332
         */
333 21
        foreach ($elements as &$item)
334
        {
335 21
            if ($item instanceof TemplateReadyDocument)
336
            {
337 21
                if (!Service::hasRunTimeFlag(RuntimeStatus::USING_DRAFTS) && $item->isDraft())
338
                {
339 1
                    continue;
340
                }
341
            }
342
343 21
            $keyName = ($name === null) ? $item->getRelativeFilePath() : $name($item);
0 ignored issues
show
Documentation Bug introduced by
The method getRelativeFilePath does not exist on object<allejo\stakx\Document\JailedDocument>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
344
345 21
            if (empty($item->getNamespace()))
0 ignored issues
show
Documentation Bug introduced by
The method getNamespace does not exist on object<allejo\stakx\Document\JailedDocument>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
346
            {
347 1
                $jailItems[$keyName] = $item->createJail();
0 ignored issues
show
Documentation Bug introduced by
The method createJail does not exist on object<allejo\stakx\Document\JailedDocument>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
348
            }
349
            else
350
            {
351 20
                $jailItems[$item->getNamespace()][$keyName] = $item->createJail();
0 ignored issues
show
Documentation Bug introduced by
The method getNamespace does not exist on object<allejo\stakx\Document\JailedDocument>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
Documentation Bug introduced by
The method createJail does not exist on object<allejo\stakx\Document\JailedDocument>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
352
            }
353
        }
354
355 21
        return $jailItems;
356
    }
357
}
358