Passed
Push — master ( 3f6c85...9c6499 )
by Jens
02:40
created

Repository   C

Complexity

Total Complexity 61

Size/Duplication

Total Lines 582
Duplicated Lines 4.64 %

Coupling/Cohesion

Components 1
Dependencies 2

Importance

Changes 15
Bugs 2 Features 0
Metric Value
c 15
b 2
f 0
dl 27
loc 582
rs 6.018
wmc 61
lcom 1
cbo 2

29 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 9 3
A create() 0 4 1
A init() 0 10 1
A __get() 12 16 4
A __set() 0 10 2
A save() 0 7 1
A saveSubset() 0 15 3
A loadSubset() 0 8 1
A initContentDb() 0 6 1
A initConfigStorage() 0 23 1
A getContentDbHandle() 0 7 2
A getDocuments() 0 7 2
B getDocumentsWithState() 0 35 3
A getDocumentsByPath() 0 22 3
A getDocumentContainerByPath() 0 17 4
A getDocumentByPath() 0 17 4
A getTotalDocumentCount() 0 17 3
A publishDocumentByPath() 0 4 1
A unpublishDocumentByPath() 0 4 1
A cleanPublishedDeletedDocuments() 0 13 1
A fetchAllDocuments() 0 5 1
A fetchDocument() 0 5 1
A getRootFolder() 0 7 1
B saveDocument() 0 27 2
B deleteDocumentByPath() 0 23 4
A setAssetsToDocumentFolders() 0 10 2
A getPublishedDocumentsNoFolders() 5 17 3
A publishOrUnpublishDocumentByPath() 5 23 3
A getDbStatement() 5 11 2

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like Repository often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Repository, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * User: Jens
4
 * Date: 30-1-2017
5
 * Time: 20:15
6
 */
7
8
namespace library\storage;
9
use library\storage\storage\DocumentStorage;
10
11
/**
12
 * Class Repository
13
 * @package library\storage
14
 * @property array sitemap
15
 * @property array applicationComponents
16
 * @property array documentTypes
17
 * @property array bricks
18
 * @property array imageSet
19
 * @property array images
20
 * @property array files
21
 * @property array users
22
 * @property array valuelists
23
 * @property array redirects
24
 */
25
class Repository
26
{
27
    protected $storagePath;
28
29
    protected $fileBasedSubsets = array('sitemap', 'applicationComponents', 'documentTypes', 'bricks', 'imageSet', 'images', 'files', 'users', 'valuelists', 'redirects');
30
31
    protected $sitemap;
32
    protected $sitemapChanges = false;
33
34
    protected $applicationComponents;
35
    protected $applicationComponentsChanges = false;
36
37
    protected $documentTypes;
38
    protected $documentTypesChanges = false;
39
40
    protected $bricks;
41
    protected $bricksChanges = false;
42
43
    protected $imageSet;
44
    protected $imageSetChanges = false;
45
46
    protected $images;
47
    protected $imagesChanges = false;
48
49
    protected $files;
50
    protected $filesChanges = false;
51
52
    protected $users;
53
    protected $usersChanges = false;
54
55
    protected $valuelists;
56
    protected $valuelistsChanges = false;
57
58
    protected $redirects;
59
    protected $redirectsChanges = false;
60
61
    protected $contentDbHandle;
62
63
64
    /**
65
     * Repository constructor.
66
     * @param $storagePath
67
     * @throws \Exception
68
     */
69
    public function __construct($storagePath)
70
    {
71
        $storagePath = realpath($storagePath);
72
        if (is_dir($storagePath) && $storagePath !== false) {
73
            $this->storagePath = $storagePath;
74
        } else {
75
            throw new \Exception('Repository not yet initialized.');
76
        }
77
    }
78
79
    /**
80
     * Creates the folder in which to create all storage related files
81
     *
82
     * @param $storagePath
83
     * @return bool
84
     */
85
    public static function create($storagePath)
86
    {
87
        return mkdir($storagePath);
88
    }
89
90
    /**
91
     * Initiates default storage
92
     * @throws \Exception
93
     */
94
    public function init()
95
    {
96
        $storageDefaultPath = realpath('../library/cc/install/_storage.json');
97
        $contentSqlPath = realpath('../library/cc/install/content.sql');
98
99
        $this->initConfigStorage($storageDefaultPath);
100
        $this->initContentDb($contentSqlPath);
101
102
        $this->save();
103
    }
104
105
    /**
106
     * Load filebased subset and return it's contents
107
     *
108
     * @param $name
109
     * @return mixed|string
110
     * @throws \Exception
111
     */
112
    public function __get($name)
113
    {
114
        if (isset($this->$name)) {
115 View Code Duplication
            if (in_array($name, $this->fileBasedSubsets)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
116
                return $this->$name;
117
            } else {
118
                throw new \Exception('Trying to get undefined property from Repository: ' . $name);
119
            }
120 View Code Duplication
        } else {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
121
            if (in_array($name, $this->fileBasedSubsets)) {
122
                return $this->loadSubset($name);
123
            } else {
124
                throw new \Exception('Trying to get undefined property from Repository: ' . $name);
125
            }
126
        }
127
    }
128
129
    /**
130
     * Set filebased subset contents
131
     * @param $name
132
     * @param $value
133
     * @throws \Exception
134
     */
135
    public function __set($name, $value)
136
    {
137
        if (in_array($name, $this->fileBasedSubsets)) {
138
            $this->$name = $value;
139
            $changes = $name . 'Changes';
140
            $this->$changes = true;
141
        } else {
142
            throw new \Exception('Trying to persist unknown subset in repository: ' . $name . ' <br /><pre>' . print_r($value, true) . '</pre>');
143
        }
144
    }
145
146
    /**
147
     * Persist all subsets
148
     */
149
    public function save()
150
    {
151
        $host = $this;
152
        array_map(function ($value) use ($host) {
153
            $host->saveSubset($value);
154
		}, $this->fileBasedSubsets);
155
    }
156
157
    /**
158
     * Persist subset to disk
159
     * @param $subset
160
     */
161
    public function saveSubset($subset)
162
    {
163
		$changes = $subset . 'Changes';
164
		if ($this->$changes === true) {
165
            if (!defined('JSON_PRETTY_PRINT')) {
166
                $json = json_encode($this->$subset);
167
            } else {
168
                $json = json_encode($this->$subset, JSON_PRETTY_PRINT);
169
            }
170
			$subsetStoragePath = $this->storagePath . DIRECTORY_SEPARATOR . $subset . '.json';
171
			file_put_contents($subsetStoragePath, $json);
172
173
			$this->$changes = false;
174
		}
175
    }
176
177
    /**
178
     * Load subset from disk
179
     * @param $subset
180
     * @return mixed|string
181
     */
182
    protected function loadSubset($subset)
183
    {
184
        $subsetStoragePath = $this->storagePath . DIRECTORY_SEPARATOR . $subset . '.json';
185
        $json = file_get_contents($subsetStoragePath);
186
        $json = json_decode($json);
187
        $this->$subset = $json;
188
        return $json;
189
    }
190
191
    /**
192
     * @param $contentSqlPath
193
     */
194
    protected function initContentDb($contentSqlPath)
195
    {
196
        $db = $this->getContentDbHandle();
197
        $sql = file_get_contents($contentSqlPath);
198
        $db->exec($sql);
199
    }
200
201
    /**
202
     * @param $storageDefaultPath
203
     */
204
    protected function initConfigStorage($storageDefaultPath)
205
    {
206
        $json = file_get_contents($storageDefaultPath);
207
        $json = json_decode($json);
208
        $this->sitemap = $json->sitemap;
209
        $this->sitemapChanges = true;
210
        $this->applicationComponents = $json->applicationComponents;
211
        $this->applicationComponentsChanges = true;
212
        $this->documentTypes = $json->documentTypes;
213
        $this->documentTypesChanges = true;
214
        $this->bricks = $json->bricks;
215
        $this->bricksChanges = true;
216
        $this->imageSet = $json->imageSet;
217
        $this->imageSetChanges = true;
218
        $this->images = $json->images;
219
        $this->imagesChanges = true;
220
        $this->files = $json->files;
221
        $this->filesChanges = true;
222
        $this->users = $json->users;
223
        $this->usersChanges = true;
224
        $this->valuelists = $json->valuelists;
225
        $this->valuelistsChanges = true;
226
    }
227
228
    /**
229
     * @return \PDO
230
     */
231
    public function getContentDbHandle()
232
    {
233
        if ($this->contentDbHandle === null) {
234
            $this->contentDbHandle = new \PDO('sqlite:' . $this->storagePath . DIRECTORY_SEPARATOR . 'content.db');
235
        }
236
        return $this->contentDbHandle;
237
    }
238
239
	/**
240
	 * Get all documents
241
	 *
242
	 * @param string $state
243
	 *
244
	 * @return array
245
	 * @throws \Exception
246
	 */
247
    public function getDocuments($state = 'published')
248
    {
249
		if (!in_array($state, Document::$DOCUMENT_STATES)) {
250
			throw new \Exception('Unsupported document state: ' . $state);
251
		}
252
        return $this->getDocumentsByPath('/', $state);
253
    }
254
255
	public function getDocumentsWithState($folderPath = '/')
256
	{
257
		$db = $this->getContentDbHandle();
258
		$folderPathWithWildcard = $folderPath . '%';
259
260
		$ifRootIndex = 1;
261
		if ($folderPath == '/') {
262
			$ifRootIndex = 0;
263
		}
264
265
		$sql = '
266
            SELECT documents_unpublished.*,
267
            	   IFNULL(documents_published.state,"unpublished") as state,
268
            	   IFNULL(documents_published.publicationDate,NULL) as publicationDate,
269
            	   (documents_published.lastModificationDate != documents_unpublished.lastModificationDate) as unpublishedChanges 
270
              FROM documents_unpublished
271
		 LEFT JOIN documents_published
272
         		ON documents_published.path = documents_unpublished.path
273
             WHERE documents_unpublished.`path` LIKE ' . $db->quote($folderPathWithWildcard) . '
274
               AND substr(documents_unpublished.`path`, ' . (strlen($folderPath) + $ifRootIndex + 1) . ') NOT LIKE "%/%"
275
               AND length(documents_unpublished.`path`) > ' . (strlen($folderPath) + $ifRootIndex) . '
276
               AND documents_unpublished.path != ' . $db->quote($folderPath) . '
277
          ORDER BY documents_unpublished.`type` DESC, documents_unpublished.`path` ASC
278
        ';
279
		$stmt = $this->getDbStatement($sql);
280
281
282
283
		$documents = $stmt->fetchAll(\PDO::FETCH_CLASS, '\library\storage\Document');
284
		foreach ($documents as $key => $document) {
285
			$documents = $this->setAssetsToDocumentFolders($document, $db, $documents, $key);
286
		}
287
		//dump($documents);
288
		return $documents;
289
	}
290
291
	/**
292
	 * Get all documents and folders in a certain path
293
	 *
294
	 * @param        $folderPath
295
	 * @param string $state
296
	 *
297
	 * @return array
298
	 * @throws \Exception
299
	 */
300
    public function getDocumentsByPath($folderPath, $state = 'published')
301
    {
302
    	if (!in_array($state, Document::$DOCUMENT_STATES)) {
303
    		throw new \Exception('Unsupported document state: ' . $state);
304
		}
305
        $db = $this->getContentDbHandle();
306
        $folderPathWithWildcard = $folderPath . '%';
307
308
        $sql = 'SELECT *
309
              FROM documents_' . $state . '
310
             WHERE `path` LIKE ' . $db->quote($folderPathWithWildcard) . '
311
               AND substr(`path`, ' . (strlen($folderPath) + 1) . ') NOT LIKE "%/%"
312
               AND path != ' . $db->quote($folderPath) . '
313
          ORDER BY `type` DESC, `path` ASC';
314
        $stmt = $this->getDbStatement($sql);
315
316
        $documents = $stmt->fetchAll(\PDO::FETCH_CLASS, '\library\storage\Document');
317
        foreach ($documents as $key => $document) {
318
			$documents = $this->setAssetsToDocumentFolders($document, $db, $documents, $key);
319
        }
320
        return $documents;
321
    }
322
323
324
    /**
325
     * @param $path
326
     * @return bool|Document
327
     */
328
    public function getDocumentContainerByPath($path)
329
    {
330
        $document = $this->getDocumentByPath($path, 'unpublished');
331
        if ($document === false) {
332
            return false;
333
        }
334
        $slugLength = strlen($document->slug);
335
        $containerPath = substr($path, 0, -$slugLength);
336
        if ($containerPath === '/') {
337
            return $this->getRootFolder();
338
        }
339
        if (substr($containerPath, -1) === '/'){
340
			$containerPath = substr($containerPath, 0, -1);
341
		}
342
        $containerFolder = $this->getDocumentByPath($containerPath, 'unpublished');
343
        return $containerFolder;
344
    }
345
346
	/**
347
	 * @param        $path
348
	 * @param string $state
349
	 *
350
	 * @return bool|\library\storage\Document
351
	 * @throws \Exception
352
	 */
353
    public function getDocumentByPath($path, $state = 'published')
354
    {
355
		if (!in_array($state, Document::$DOCUMENT_STATES)) {
356
			throw new \Exception('Unsupported document state: ' . $state);
357
		}
358
        $db = $this->getContentDbHandle();
359
        $document = $this->fetchDocument('
360
            SELECT *
361
              FROM documents_' .  $state . '
362
             WHERE path = ' . $db->quote($path) . '
363
        ');
364
        if ($document instanceof Document && $document->type === 'folder') {
365
            $document->dbHandle = $db;
366
            $document->documentStorage = new DocumentStorage($this);
367
        }
368
        return $document;
369
    }
370
371
	/**
372
	 * Returns the count of all documents stored in the db
373
	 *
374
	 * @param string $state
375
	 *
376
	 * @return int
377
	 * @throws \Exception
378
	 */
379
	public function getTotalDocumentCount($state = 'published')
380
	{
381
		if (!in_array($state, Document::$DOCUMENT_STATES)) {
382
			throw new \Exception('Unsupported document state: ' . $state);
383
		}
384
		$db = $this->getContentDbHandle();
385
		$stmt = $db->query('
386
			SELECT count(*)
387
			  FROM documents_' . $state . '
388
			 WHERE `type` != "folder"
389
		');
390
		$result = $stmt->fetch(\PDO::FETCH_ASSOC);
391
		if (!is_array($result )) {
392
			return 0;
393
		}
394
		return intval(current($result));
395
	}
396
397
	public function getPublishedDocumentsNoFolders()
398
	{
399
		$db = $this->getContentDbHandle();
400
		$sql = '
401
			SELECT *
402
			  FROM documents_published
403
			 WHERE `type` != "folder"
404
		';
405
		$stmt = $db->query($sql);
406
		$result = $stmt->fetchAll(\PDO::FETCH_CLASS, '\library\storage\Document');
407 View Code Duplication
		if ($stmt === false || !$stmt->execute()) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
408
			$errorInfo = $db->errorInfo();
409
			$errorMsg = $errorInfo[2];
410
			throw new \Exception('SQLite Exception: ' . $errorMsg . ' in SQL: <br /><pre>' . $sql . '</pre>');
411
		}
412
		return $result;
413
	}
414
415
	private function publishOrUnpublishDocumentByPath($path, $publish = true) {
416
		if ($publish) {
417
			$sql = '
418
				INSERT OR REPLACE INTO documents_published 
419
					  (`id`,`path`,`title`,`slug`,`type`,`documentType`,`documentTypeSlug`,`state`,`lastModificationDate`,`creationDate`,`publicationDate`,`lastModifiedBy`,`fields`,`bricks`,`dynamicBricks`)
420
				SELECT `id`,`path`,`title`,`slug`,`type`,`documentType`,`documentTypeSlug`,"published" as state,`lastModificationDate`,`creationDate`,' . time() . ' as publicationDate, `lastModifiedBy`,`fields`,`bricks`,`dynamicBricks`
421
				  FROM documents_unpublished
422
				 WHERE `path` = :path
423
			';
424
		} else {
425
			$sql = 'DELETE FROM documents_published
426
					  WHERE `path` = :path';
427
		}
428
		$db = $this->getContentDbHandle();
429
		$stmt = $db->prepare($sql);
430 View Code Duplication
		if ($stmt === false) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
431
			$errorInfo = $db->errorInfo();
432
			$errorMsg = $errorInfo[2];
433
			throw new \Exception('SQLite Exception: ' . $errorMsg . ' in SQL: <br /><pre>' . $sql . '</pre>');
434
		}
435
		$stmt->bindValue(':path', $path);
436
		$stmt->execute();
437
	}
438
439
	public function publishDocumentByPath($path)
440
	{
441
		$this->publishOrUnpublishDocumentByPath($path);
442
	}
443
444
	public function unpublishDocumentByPath($path)
445
	{
446
		$this->publishOrUnpublishDocumentByPath($path, false);
447
	}
448
449
	public function cleanPublishedDeletedDocuments()
450
	{
451
		$sql = '   DELETE FROM documents_published
452
						 WHERE documents_published.path IN (
453
						SELECT documents_published.path
454
						  FROM documents_published
455
					 LEFT JOIN documents_unpublished
456
							ON documents_unpublished.path = documents_published.path
457
						 WHERE documents_unpublished.path IS NULL
458
		)';
459
		$stmt = $this->getDbStatement($sql);
460
		$stmt->execute();
461
	}
462
463
	/**
464
     * Return the results of the query as array of Documents
465
     * @param $sql
466
     * @return array
467
     * @throws \Exception
468
     */
469
    protected function fetchAllDocuments($sql)
470
    {
471
        $stmt = $this->getDbStatement($sql);
472
        return $stmt->fetchAll(\PDO::FETCH_CLASS, '\library\storage\Document');
473
    }
474
475
    /**
476
     * Return the result of the query as Document
477
     * @param $sql
478
     * @return mixed
479
     * @throws \Exception
480
     */
481
    protected function fetchDocument($sql)
482
    {
483
        $stmt = $this->getDbStatement($sql);
484
        return $stmt->fetchObject('\library\storage\Document');
485
    }
486
487
    /**
488
     * Prepare the sql statement
489
     * @param $sql
490
     * @return \PDOStatement
491
     * @throws \Exception
492
     */
493
    protected function getDbStatement($sql)
494
    {
495
        $db = $this->getContentDbHandle();
496
        $stmt = $db->query($sql);
497 View Code Duplication
        if ($stmt === false) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
498
            $errorInfo = $db->errorInfo();
499
            $errorMsg = $errorInfo[2];
500
            throw new \Exception('SQLite Exception: ' . $errorMsg . ' in SQL: <br /><pre>' . $sql . '</pre>');
501
        }
502
        return $stmt;
503
    }
504
505
    /**
506
     * Create a (non-existent) root folder Document and return it
507
     * @return Document
508
     */
509
    protected function getRootFolder()
510
    {
511
        $rootFolder = new Document();
512
        $rootFolder->path = '/';
513
        $rootFolder->type = 'folder';
514
        return $rootFolder;
515
    }
516
517
	/**
518
	 * Save the document to the database
519
	 *
520
	 * @param Document $documentObject
521
	 * @param string   $state
522
	 *
523
	 * @return bool
524
	 * @throws \Exception
525
	 * @internal param $path
526
	 */
527
    public function saveDocument($documentObject, $state = 'published')
528
    {
529
		if (!in_array($state, Document::$DOCUMENT_STATES)) {
530
			throw new \Exception('Unsupported document state: ' . $state);
531
		}
532
        $db = $this->getContentDbHandle();
533
        $stmt = $this->getDbStatement('
534
            INSERT OR REPLACE INTO documents_' . $state . ' (`path`,`title`,`slug`,`type`,`documentType`,`documentTypeSlug`,`state`,`lastModificationDate`,`creationDate`,`lastModifiedBy`,`fields`,`bricks`,`dynamicBricks`)
535
            VALUES(
536
              ' . $db->quote($documentObject->path) . ',
537
              ' . $db->quote($documentObject->title) . ',
538
              ' . $db->quote($documentObject->slug) . ',
539
              ' . $db->quote($documentObject->type) . ',
540
              ' . $db->quote($documentObject->documentType) . ',
541
              ' . $db->quote($documentObject->documentTypeSlug) . ',
542
              ' . $db->quote($documentObject->state) . ',
543
              ' . $db->quote($documentObject->lastModificationDate) . ',
544
              ' . $db->quote($documentObject->creationDate) . ',
545
              ' . $db->quote($documentObject->lastModifiedBy) . ',
546
              ' . $db->quote(json_encode($documentObject->fields)) . ',
547
              ' . $db->quote(json_encode($documentObject->bricks)) . ',
548
              ' . $db->quote(json_encode($documentObject->dynamicBricks)) . '
549
            )
550
        ');
551
        $result = $stmt->execute();
552
        return $result;
553
    }
554
555
	/**
556
	 * Delete the document from the database
557
	 * If it's a folder, also delete it's contents
558
	 *
559
	 * @param        $path
560
	 *
561
	 * @internal param string $state
562
	 *
563
	 */
564
    public function deleteDocumentByPath($path)
565
    {
566
        $db = $this->getContentDbHandle();
567
        $documentToDelete = $this->getDocumentByPath($path, 'unpublished');
568
        if ($documentToDelete instanceof Document) {
569
            if ($documentToDelete->type == 'document') {
570
                $stmt = $this->getDbStatement('
571
                    DELETE FROM documents_unpublished
572
                          WHERE path = ' . $db->quote($path) . '
573
                ');
574
                $stmt->execute();
575
            } elseif ($documentToDelete->type == 'folder') {
576
                $folderPathWithWildcard = $path . '%';
577
                $stmt = $this->getDbStatement('
578
                    DELETE FROM documents_unpublished
579
                          WHERE (path LIKE ' . $db->quote($folderPathWithWildcard) . '
580
                            AND substr(`path`, ' . (strlen($path) + 1) . ', 1) = "/")
581
                            OR path = ' . $db->quote($path) . '
582
                ');
583
                $stmt->execute();
584
            }
585
        }
586
    }
587
588
	/**
589
	 * @param $document
590
	 * @param $db
591
	 * @param $documents
592
	 * @param $key
593
	 *
594
	 * @return mixed
595
	 */
596
	private function setAssetsToDocumentFolders($document, $db, $documents, $key)
597
	{
598
		if ($document->type === 'folder') {
599
			$document->dbHandle = $db;
600
			$document->documentStorage = new DocumentStorage($this);
601
			$documents[$key] = $document;
602
		}
603
604
		return $documents;
605
	}
606
}