Skip to content

Commit 82716ae

Browse files
harm-lessharm-less
harm-less
authored and
harm-less
committedMay 27, 2015
QueryCollection and performance gains
- Added a QueryCollection that holds multiple queries you can retrieved by an ID - The root -and child dirs selection can now be cached to gain performance
1 parent 40dcd7a commit 82716ae

File tree

12 files changed

+402
-19
lines changed

12 files changed

+402
-19
lines changed
 

‎index.php

+4
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@
1313
pr($sampleSimple->queryNonExistingFileWithRequirementOne());
1414
pr($sampleSimple->queryNonExistingFileWithRequirementLast());
1515
pr($sampleSimple->queryNonExistingFileWithRequirementAll());
16+
pr($sampleSimple->executeQueryChild1());
17+
pr($sampleSimple->executeQueryChild1());
18+
pr($sampleSimple->executeQueryChild1('File2'));
19+
pr($sampleSimple->executeQueryChild1());
1620

1721

1822
function pr($var) {

‎samples/FQ/Samples/Simple.php

+24
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,17 @@
22

33
namespace FQ\Samples;
44

5+
use FQ\Collections\Query\QueryCollection;
56
use FQ\Dirs\ChildDir;
67
use FQ\Dirs\RootDir;
8+
use FQ\Query\FilesQuery;
79
use FQ\Query\FilesQueryBuilder;
810
use FQ\Query\FilesQueryRequirements;
911

1012
class Simple extends SampleBootstrapper {
1113

14+
private $_queryCollection;
15+
1216
function __construct() {
1317
parent::__construct('simple');
1418

@@ -17,6 +21,20 @@ function __construct() {
1721

1822
$this->addChildDir(new ChildDir('child1', 'child1'));
1923
$this->addChildDir(new ChildDir('child2', 'child2'));
24+
25+
$this->_queryCollection = new QueryCollection($this);
26+
27+
$queryChild1 = new FilesQuery($this);
28+
$queryChild1ChildSelection = $queryChild1->getChildDirSelection();
29+
$queryChild1ChildSelection->includeDirById('child1');
30+
31+
$this->_queryCollection->addQuery('child1', $queryChild1);
32+
33+
$queryChild2 = new FilesQuery($this);
34+
$queryChild2ChildSelection = $queryChild2->getChildDirSelection();
35+
$queryChild2ChildSelection->includeDirById('child2');
36+
37+
$this->_queryCollection->addQuery('child2', $queryChild2);
2038
}
2139

2240
public function queryFile1FromChild1() {
@@ -46,4 +64,10 @@ public function queryNonExistingFileWithRequirementAll() {
4664
$builder = new FilesQueryBuilder($this->query());
4765
return $builder->addRequirement(FilesQueryRequirements::REQUIRE_ALL)->run('File1')->listPaths();
4866
}
67+
68+
public function executeQueryChild1($fileName = 'File1') {
69+
$query = $this->_queryCollection->getQueryById('child1');
70+
$query->run($fileName);
71+
return $query->listPaths();
72+
}
4973
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
<?php
2+
3+
namespace FQ\Collections\Query;
4+
5+
use FQ\Exceptions\QueryCollectionException;
6+
use FQ\Files;
7+
use FQ\Query\FilesQuery;
8+
9+
class QueryCollection {
10+
11+
/**
12+
* @var FilesQuery[]
13+
*/
14+
private $_queries;
15+
16+
function __construct() {
17+
$this->_queries = array();
18+
}
19+
20+
/**
21+
* @param $id
22+
* @param FilesQuery $query
23+
* @return FilesQuery
24+
*/
25+
public function addQuery($id, FilesQuery $query) {
26+
if ($this->queryExists($id)) {
27+
throw new QueryCollectionException(sprintf('Query with id "%s" already defined', $id));
28+
}
29+
$this->_queries[$id] = $query;
30+
return $query;
31+
}
32+
33+
/**
34+
* @param string|FilesQuery $query
35+
* @return FilesQuery|null
36+
*/
37+
public function getQuery($query) {
38+
if (is_string($query)) {
39+
return $this->getQueryById($query);
40+
}
41+
else if (is_object($query) && $this->isInCollection($query)) {
42+
return $query;
43+
}
44+
return null;
45+
}
46+
47+
public function getQueryById($id) {
48+
if ($this->queryExists($id)) {
49+
return $this->_queries[$id];
50+
}
51+
return null;
52+
}
53+
54+
/**
55+
* @param string $id
56+
* @return bool
57+
*/
58+
public function removeQuery($id) {
59+
if ($this->queryExists($id)) {
60+
unset($this->_queries[$id]);
61+
return true;
62+
}
63+
return false;
64+
}
65+
66+
/**
67+
*
68+
*/
69+
public function removeAllQueries() {
70+
$this->_queries = array();
71+
}
72+
73+
/**
74+
* @param FilesQuery $query Query that will be checked
75+
* @return bool Returns true if dir is in the collection and false when it's not
76+
*/
77+
public function isInCollection(FilesQuery $query) {
78+
foreach ($this->_queries as $queryTemp) {
79+
if ($query === $queryTemp) return true;
80+
}
81+
return false;
82+
}
83+
84+
/**
85+
* @param $id
86+
* @return bool
87+
*/
88+
public function queryExists($id) {
89+
return isset($this->_queries[$id]);
90+
}
91+
92+
/**
93+
* @return FilesQuery[]
94+
*/
95+
public function queries() {
96+
return $this->_queries;
97+
}
98+
99+
/**
100+
* @return int Total amount of queries in this collection
101+
*/
102+
public function totalQueries() {
103+
return count($this->queries());
104+
}
105+
106+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<?php
2+
3+
namespace FQ\Exceptions;
4+
5+
use RuntimeException;
6+
7+
class QueryCollectionException extends RuntimeException implements FQExceptionInterface
8+
{
9+
}

‎src/FQ/query/FilesQuery.php

+46-14
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
use FQ\Files;
99
use FQ\Query\Selection\ChildSelection;
1010
use FQ\Query\Selection\RootSelection;
11+
use FQ\Utils\Dirs;
1112

1213
/**
1314
* Class FilesQuery
@@ -42,10 +43,19 @@ class FilesQuery {
4243
*/
4344
private $_currentQueryChildren;
4445

46+
/**
47+
* @var RootDir[]
48+
*/
49+
private $_cachedRootDirs;
4550
/**
4651
* @var RootDir[]
4752
*/
4853
private $_cachedQueryRootDirs;
54+
55+
/**
56+
* @var ChildDir[]
57+
*/
58+
private $_cachedChildDirs;
4959
/**
5060
* @var ChildDir[]
5161
*/
@@ -114,13 +124,9 @@ function __construct(Files $files) {
114124
}
115125

116126
public function reset() {
117-
$this->_cachedQueryRootDirs = null;
118-
$this->_currentQueryChildren = array();
119-
120-
$this->_cachedPaths = null;
121-
$this->_cachedPathsSimple = null;
122-
$this->_cachedRawPaths = null;
123-
$this->_cachedRawPathsSimple = null;
127+
$this->_clearRootDirCache();
128+
$this->_clearChildDirCache();
129+
$this->_clearPathCache();
124130

125131
$this->requirements()->removeAll();
126132
$this->filters(self::FILTER_EXISTING, false);
@@ -130,6 +136,20 @@ public function reset() {
130136
$this->_hasRun = false;
131137
$this->_queryError = null;
132138
}
139+
protected function _clearRootDirCache() {
140+
$this->_cachedQueryRootDirs = null;
141+
}
142+
protected function _clearChildDirCache() {
143+
$this->_cachedQueryChildDirs = null;
144+
}
145+
protected function _clearPathCache() {
146+
$this->_currentQueryChildren = array();
147+
148+
$this->_cachedPaths = null;
149+
$this->_cachedPathsSimple = null;
150+
$this->_cachedRawPaths = null;
151+
$this->_cachedRawPathsSimple = null;
152+
}
133153
public function resetSelection() {
134154
$this->getRootDirSelection()->reset();
135155
$this->getChildDirSelection()->reset();
@@ -256,21 +276,30 @@ protected function _hasRunCheck() {
256276

257277
/**
258278
* @param string $fileName The name of the file the query will be executing
279+
* @param bool $throwExceptions
280+
* @throws \Exception
259281
* @return bool
260282
*/
261-
public function run($fileName) {
262-
if ($this->files()->totalRootDirs() === 0) {
283+
public function run($fileName, $throwExceptions = false) {
284+
$files = $this->files();
285+
if ($files->totalRootDirs() === 0) {
263286
throw new FileQueryException(sprintf('Query is trying to run with file "%s" but no root directories are configured. Make sure sure you have added at least one root directory with Files::addRootDir() before you run a query', $fileName));
264287
}
265288

266-
$this->_queriedFileName = $fileName;
289+
$this->_clearPathCache();
267290

268-
if ($this->getRootDirSelection()->isInvalidated()) {
269-
$this->_cachedQueryRootDirs;
291+
$currentRootDirs = $files->rootDirs();
292+
if (!Dirs::equalDirs($this->_cachedRootDirs, $currentRootDirs) || $this->getRootDirSelection()->isInvalidated()) {
293+
$this->_clearRootDirCache();
270294
}
271-
if ($this->getChildDirSelection()->isInvalidated()) {
272-
$this->_cachedQueryChildren;
295+
$this->_cachedRootDirs = $currentRootDirs;
296+
$currentChildDirs = $files->childDirs();
297+
if (!Dirs::equalDirs($this->_cachedChildDirs, $currentChildDirs) || $this->getChildDirSelection()->isInvalidated()) {
298+
$this->_clearChildDirCache();
273299
}
300+
$this->_cachedChildDirs = $currentChildDirs;
301+
302+
$this->_queriedFileName = $fileName;
274303
$rootDirsSelection = $this->_getCachedRootDirSelection();
275304

276305
$this->_currentQueryChildren = array();
@@ -281,6 +310,9 @@ public function run($fileName) {
281310
$meetsRequirements = $this->requirements()->meetsRequirements(false);
282311
if ($meetsRequirements !== true) {
283312
$this->_queryError = $meetsRequirements;
313+
if ($throwExceptions === true) {
314+
throw new $this->_queryError;
315+
}
284316
}
285317
return $this->_queryError === null;
286318
}

‎src/FQ/utils/Dirs.php

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?php
2+
3+
namespace FQ\Utils;
4+
5+
use FQ\Dirs\Dir;
6+
7+
class Dirs {
8+
/**
9+
* @param Dir[] $array1
10+
* @param Dir[] $array2
11+
* @return bool Returns true if the array contains equal dirs in the same order. Otherwise returns false.
12+
*/
13+
public static function equalDirs($array1, $array2) {
14+
if (count($array1) !== count($array2)) return false;
15+
foreach ($array1 as $index => $dir) {
16+
if ($array2[$index] !== $dir) {
17+
return false;
18+
}
19+
}
20+
return true;
21+
}
22+
}

‎tests/FQ/Tests/Collections/AbstractDirCollectionTests.php

+3-1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
namespace FQ\Tests\Collections;
44

55
use FQ\Collections\DirCollection;
6+
use FQ\Dirs\Dir;
67
use FQ\Tests\AbstractFQTest;
78

89
class AbstractDirCollectionTests extends AbstractFQTest {
@@ -11,6 +12,7 @@ class AbstractDirCollectionTests extends AbstractFQTest {
1112

1213
protected function setUp()
1314
{
15+
parent::setUp();
1416
// Create a new FQ app,
1517
// since we need one pretty much everywhere
1618
$this->_dirCollection = $this->_createNewDirCollection();
@@ -30,7 +32,7 @@ protected function _createNewDirCollection() {
3032
return new DirCollection();
3133
}
3234

33-
protected function _addDirToCollection($dir = null, $index = null) {
35+
protected function _addDirToCollection(Dir $dir = null, $index = null) {
3436
$dir = $dir === null ? $this->_createNewDir() : $dir;
3537
$collection = $this->dirCollection();
3638
return $collection->addDir($dir, $index);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
<?php
2+
3+
namespace FQ\Tests\Collections\Query;
4+
5+
use FQ\Collections\Query\QueryCollection;
6+
use FQ\Query\FilesQuery;
7+
use FQ\Tests\AbstractFQTest;
8+
9+
class AbstractQueryCollectionTests extends AbstractFQTest
10+
{
11+
protected $_queryCollection;
12+
13+
const DEFAULT_QUERY_ID = 'query1';
14+
const SECOND_QUERY_ID = 'query2';
15+
16+
protected function setUp()
17+
{
18+
parent::setUp();
19+
// Create a new FQ app,
20+
// since we need one pretty much everywhere
21+
$this->_queryCollection = $this->_createNewQueryCollection();
22+
}
23+
24+
/**
25+
* @return QueryCollection
26+
*/
27+
protected function queryCollection()
28+
{
29+
return $this->_queryCollection;
30+
}
31+
32+
/**
33+
* @return QueryCollection
34+
*/
35+
protected function _createNewQueryCollection()
36+
{
37+
return new QueryCollection($this->_fqApp);
38+
}
39+
40+
protected function _addQueryToCollection($id = self::DEFAULT_QUERY_ID, FilesQuery $query = null)
41+
{
42+
$query = $query === null ? new FilesQuery($this->_fqApp) : $query;
43+
$collection = $this->queryCollection();
44+
return $collection->addQuery($id, $query);
45+
}
46+
47+
function testIgnore() {
48+
49+
}
50+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
<?php
2+
3+
namespace FQ\Tests\Collections\Query;
4+
5+
use FQ\Collections\Query\QueryCollection;
6+
use FQ\Query\FilesQuery;
7+
8+
class DirCollectionTest extends AbstractQueryCollectionTests {
9+
10+
public function testCreateNewQueryCollection() {
11+
$queryCollection = new QueryCollection($this->_fqApp);
12+
$this->assertNotNull($queryCollection);
13+
$this->assertTrue($queryCollection instanceof QueryCollection);
14+
}
15+
16+
public function testAddQuery() {
17+
$query = $this->_addQueryToCollection();
18+
$this->assertNotNull($query);
19+
$this->assertEquals(1, $this->queryCollection()->totalQueries());
20+
}
21+
public function testAddQueryWithSameId() {
22+
$this->setExpectedException('\FQ\Exceptions\QueryCollectionException');
23+
$this->_addQueryToCollection();
24+
$this->_addQueryToCollection();
25+
}
26+
27+
public function testAddMultipleQueries() {
28+
$firstQuery = $this->_addQueryToCollection();
29+
$secondQuery = $this->_addQueryToCollection(self::SECOND_QUERY_ID);
30+
$this->assertNotNull($firstQuery);
31+
$this->assertNotNull($secondQuery);
32+
$this->assertEquals(2, $this->queryCollection()->totalQueries());
33+
}
34+
35+
public function testRemoveQuery() {
36+
$query = $this->_addQueryToCollection();
37+
$this->_addQueryToCollection('custom-id');
38+
$collection = $this->queryCollection();
39+
$this->assertTrue($collection->removeQuery('custom-id'));
40+
$this->assertEquals(array(
41+
self::DEFAULT_QUERY_ID => $query
42+
), $collection->queries());
43+
}
44+
public function testRemoveQueryNotPresentInTheCollection() {
45+
$collection = $this->queryCollection();
46+
$this->assertFalse($collection->removeQuery('does-not-exist'));
47+
}
48+
public function testRemoveAllDirs() {
49+
$this->_addQueryToCollection();
50+
$this->_addQueryToCollection(self::SECOND_QUERY_ID);
51+
$collection = $this->queryCollection();
52+
$collection->removeAllQueries();
53+
$this->assertEquals(array(), $collection->queries());
54+
}
55+
public function testGetDirByIdThatDoesNotExist() {
56+
$this->assertNull($this->queryCollection()->getQueryById('id_that_does_not_exist'));
57+
}
58+
59+
public function testGetDir() {
60+
$dir = $this->_addQueryToCollection();
61+
$this->assertEquals($dir, $this->queryCollection()->getQuery($dir));
62+
$this->assertEquals($dir, $this->queryCollection()->getQuery(self::DEFAULT_QUERY_ID));
63+
$this->assertNull($this->queryCollection()->getQuery(null));
64+
}
65+
66+
public function testIsInCollection() {
67+
$query = $this->_addQueryToCollection();
68+
$this->assertTrue($this->queryCollection()->isInCollection($query));
69+
$this->assertFalse($this->queryCollection()->isInCollection(new FilesQuery($this->_fqApp)));
70+
}
71+
}

‎tests/FQ/Tests/Query/AbstractFilesQueryTests.php

+2-4
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@
22

33
namespace FQ\Tests\Query;
44

5-
use FQ\Collections\Dirs\DirCollection;
6-
use FQ\Dirs\ChildDir;
75
use FQ\Query\FilesQuery;
86
use FQ\Query\FilesQueryChild;
97
use FQ\Tests\AbstractFQTest;
@@ -45,8 +43,8 @@ protected function queryChild() {
4543
return $this->_queryChild;
4644
}
4745

48-
protected function runQuery($filename = 'File2') {
49-
return $this->query()->run($filename);
46+
protected function runQuery($filename = 'File2', $throwErrors = false) {
47+
return $this->query()->run($filename, $throwErrors);
5048
}
5149

5250
/**

‎tests/FQ/Tests/Query/FilesQueryTest.php

+20
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,15 @@ public function testRunQueryWhenRequirementsAreNotMet() {
177177
$this->assertNotNull($query->queryError());
178178
}
179179

180+
public function testRunQueryWhenRequirementsAreNotMetAndThrowErrorsIsSetToTrue() {
181+
$this->setExpectedException('FQ\Exceptions\FileQueryRequirementsException');
182+
$query = $this->query();
183+
$query->requirements()->addRequirement(FilesQueryRequirements::REQUIRE_ALL);
184+
$rootDir = $this->_newFictitiousRootDir(false);
185+
$this->files()->addRootDir($rootDir);
186+
$this->runQuery('File2', true);
187+
}
188+
180189
public function testRunQueryWhenNoRootDirsAreSet() {
181190
$this->setExpectedException('FQ\Exceptions\FileQueryException', 'Query is trying to run with file "File2" but no root directories are configured. Make sure sure you have added at least one root directory with Files::addRootDir() before you run a query');
182191
$this->files()->removeAllRootDirs();
@@ -197,6 +206,17 @@ public function testRunQueryAndLoadFileAfterwardsButFails() {
197206
$query->load();
198207
$this->assertTrue(class_exists('File2'));
199208
}
209+
public function testRunQueryTwice() {
210+
$query = $this->query();
211+
212+
$this->runQuery();
213+
$query->load();
214+
$this->assertTrue(class_exists('File2'));
215+
216+
$this->runQuery('File1');
217+
$query->load();
218+
$this->assertTrue(class_exists('File1'));
219+
}
200220

201221
public function testListPathsWhenQueryHasNotRan() {
202222
$this->setExpectedException('FQ\Exceptions\FileQueryException', 'You must first call the "run" method before you can retrieve query information');

‎tests/FQ/Tests/Utils/DirsTest.php

+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
<?php
2+
3+
namespace FQ\Tests\Utils;
4+
5+
use FQ\Tests\AbstractFQTest;
6+
use FQ\Utils\Dirs;
7+
8+
class DirsTest extends AbstractFQTest {
9+
10+
public function testEqualDirs() {
11+
$this->assertTrue(Dirs::equalDirs(array(), array()));
12+
13+
$rootDir1 = $this->_newActualRootDir();
14+
$rootDir2 = $this->_newActualRootDir('id');
15+
16+
$this->assertFalse(Dirs::equalDirs(array(
17+
$this->_newActualRootDir()
18+
), array(
19+
$this->_newActualRootDir()
20+
)));
21+
22+
$this->assertTrue(Dirs::equalDirs(array(
23+
$rootDir1
24+
), array(
25+
$rootDir1
26+
)));
27+
28+
$this->assertTrue(Dirs::equalDirs(array(
29+
$rootDir1,
30+
$rootDir2
31+
), array(
32+
$rootDir1,
33+
$rootDir2
34+
)));
35+
36+
$this->assertFalse(Dirs::equalDirs(array(
37+
$rootDir2,
38+
$rootDir1
39+
), array(
40+
$rootDir1,
41+
$rootDir2
42+
)));
43+
}
44+
45+
}

0 commit comments

Comments
 (0)
Please sign in to comment.