Skip to content

Commit

Permalink
QueryCollection and performance gains
Browse files Browse the repository at this point in the history
- 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
  • Loading branch information
harm-less authored and harm-less committed May 27, 2015
1 parent 40dcd7a commit 82716ae
Show file tree
Hide file tree
Showing 12 changed files with 402 additions and 19 deletions.
4 changes: 4 additions & 0 deletions index.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@
pr($sampleSimple->queryNonExistingFileWithRequirementOne());
pr($sampleSimple->queryNonExistingFileWithRequirementLast());
pr($sampleSimple->queryNonExistingFileWithRequirementAll());
pr($sampleSimple->executeQueryChild1());
pr($sampleSimple->executeQueryChild1());
pr($sampleSimple->executeQueryChild1('File2'));
pr($sampleSimple->executeQueryChild1());


function pr($var) {
Expand Down
24 changes: 24 additions & 0 deletions samples/FQ/Samples/Simple.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,17 @@

namespace FQ\Samples;

use FQ\Collections\Query\QueryCollection;
use FQ\Dirs\ChildDir;
use FQ\Dirs\RootDir;
use FQ\Query\FilesQuery;
use FQ\Query\FilesQueryBuilder;
use FQ\Query\FilesQueryRequirements;

class Simple extends SampleBootstrapper {

private $_queryCollection;

function __construct() {
parent::__construct('simple');

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

$this->addChildDir(new ChildDir('child1', 'child1'));
$this->addChildDir(new ChildDir('child2', 'child2'));

$this->_queryCollection = new QueryCollection($this);

$queryChild1 = new FilesQuery($this);
$queryChild1ChildSelection = $queryChild1->getChildDirSelection();
$queryChild1ChildSelection->includeDirById('child1');

$this->_queryCollection->addQuery('child1', $queryChild1);

$queryChild2 = new FilesQuery($this);
$queryChild2ChildSelection = $queryChild2->getChildDirSelection();
$queryChild2ChildSelection->includeDirById('child2');

$this->_queryCollection->addQuery('child2', $queryChild2);
}

public function queryFile1FromChild1() {
Expand Down Expand Up @@ -46,4 +64,10 @@ public function queryNonExistingFileWithRequirementAll() {
$builder = new FilesQueryBuilder($this->query());
return $builder->addRequirement(FilesQueryRequirements::REQUIRE_ALL)->run('File1')->listPaths();
}

public function executeQueryChild1($fileName = 'File1') {
$query = $this->_queryCollection->getQueryById('child1');
$query->run($fileName);
return $query->listPaths();
}
}
106 changes: 106 additions & 0 deletions src/FQ/collections/query/QueryCollection.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
<?php

namespace FQ\Collections\Query;

use FQ\Exceptions\QueryCollectionException;
use FQ\Files;
use FQ\Query\FilesQuery;

class QueryCollection {

/**
* @var FilesQuery[]
*/
private $_queries;

function __construct() {
$this->_queries = array();
}

/**
* @param $id
* @param FilesQuery $query
* @return FilesQuery
*/
public function addQuery($id, FilesQuery $query) {
if ($this->queryExists($id)) {
throw new QueryCollectionException(sprintf('Query with id "%s" already defined', $id));
}
$this->_queries[$id] = $query;
return $query;
}

/**
* @param string|FilesQuery $query
* @return FilesQuery|null
*/
public function getQuery($query) {
if (is_string($query)) {
return $this->getQueryById($query);
}
else if (is_object($query) && $this->isInCollection($query)) {
return $query;
}
return null;
}

public function getQueryById($id) {
if ($this->queryExists($id)) {
return $this->_queries[$id];
}
return null;
}

/**
* @param string $id
* @return bool
*/
public function removeQuery($id) {
if ($this->queryExists($id)) {
unset($this->_queries[$id]);
return true;
}
return false;
}

/**
*
*/
public function removeAllQueries() {
$this->_queries = array();
}

/**
* @param FilesQuery $query Query that will be checked
* @return bool Returns true if dir is in the collection and false when it's not
*/
public function isInCollection(FilesQuery $query) {
foreach ($this->_queries as $queryTemp) {
if ($query === $queryTemp) return true;
}
return false;
}

/**
* @param $id
* @return bool
*/
public function queryExists($id) {
return isset($this->_queries[$id]);
}

/**
* @return FilesQuery[]
*/
public function queries() {
return $this->_queries;
}

/**
* @return int Total amount of queries in this collection
*/
public function totalQueries() {
return count($this->queries());
}

}
9 changes: 9 additions & 0 deletions src/FQ/exceptions/QueryCollectionException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?php

namespace FQ\Exceptions;

use RuntimeException;

class QueryCollectionException extends RuntimeException implements FQExceptionInterface
{
}
60 changes: 46 additions & 14 deletions src/FQ/query/FilesQuery.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
use FQ\Files;
use FQ\Query\Selection\ChildSelection;
use FQ\Query\Selection\RootSelection;
use FQ\Utils\Dirs;

/**
* Class FilesQuery
Expand Down Expand Up @@ -42,10 +43,19 @@ class FilesQuery {
*/
private $_currentQueryChildren;

/**
* @var RootDir[]
*/
private $_cachedRootDirs;
/**
* @var RootDir[]
*/
private $_cachedQueryRootDirs;

/**
* @var ChildDir[]
*/
private $_cachedChildDirs;
/**
* @var ChildDir[]
*/
Expand Down Expand Up @@ -114,13 +124,9 @@ function __construct(Files $files) {
}

public function reset() {
$this->_cachedQueryRootDirs = null;
$this->_currentQueryChildren = array();

$this->_cachedPaths = null;
$this->_cachedPathsSimple = null;
$this->_cachedRawPaths = null;
$this->_cachedRawPathsSimple = null;
$this->_clearRootDirCache();
$this->_clearChildDirCache();
$this->_clearPathCache();

$this->requirements()->removeAll();
$this->filters(self::FILTER_EXISTING, false);
Expand All @@ -130,6 +136,20 @@ public function reset() {
$this->_hasRun = false;
$this->_queryError = null;
}
protected function _clearRootDirCache() {
$this->_cachedQueryRootDirs = null;
}
protected function _clearChildDirCache() {
$this->_cachedQueryChildDirs = null;
}
protected function _clearPathCache() {
$this->_currentQueryChildren = array();

$this->_cachedPaths = null;
$this->_cachedPathsSimple = null;
$this->_cachedRawPaths = null;
$this->_cachedRawPathsSimple = null;
}
public function resetSelection() {
$this->getRootDirSelection()->reset();
$this->getChildDirSelection()->reset();
Expand Down Expand Up @@ -256,21 +276,30 @@ protected function _hasRunCheck() {

/**
* @param string $fileName The name of the file the query will be executing
* @param bool $throwExceptions
* @throws \Exception
* @return bool
*/
public function run($fileName) {
if ($this->files()->totalRootDirs() === 0) {
public function run($fileName, $throwExceptions = false) {
$files = $this->files();
if ($files->totalRootDirs() === 0) {
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));
}

$this->_queriedFileName = $fileName;
$this->_clearPathCache();

if ($this->getRootDirSelection()->isInvalidated()) {
$this->_cachedQueryRootDirs;
$currentRootDirs = $files->rootDirs();
if (!Dirs::equalDirs($this->_cachedRootDirs, $currentRootDirs) || $this->getRootDirSelection()->isInvalidated()) {
$this->_clearRootDirCache();
}
if ($this->getChildDirSelection()->isInvalidated()) {
$this->_cachedQueryChildren;
$this->_cachedRootDirs = $currentRootDirs;
$currentChildDirs = $files->childDirs();
if (!Dirs::equalDirs($this->_cachedChildDirs, $currentChildDirs) || $this->getChildDirSelection()->isInvalidated()) {
$this->_clearChildDirCache();
}
$this->_cachedChildDirs = $currentChildDirs;

$this->_queriedFileName = $fileName;
$rootDirsSelection = $this->_getCachedRootDirSelection();

$this->_currentQueryChildren = array();
Expand All @@ -281,6 +310,9 @@ public function run($fileName) {
$meetsRequirements = $this->requirements()->meetsRequirements(false);
if ($meetsRequirements !== true) {
$this->_queryError = $meetsRequirements;
if ($throwExceptions === true) {
throw new $this->_queryError;
}
}
return $this->_queryError === null;
}
Expand Down
22 changes: 22 additions & 0 deletions src/FQ/utils/Dirs.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

namespace FQ\Utils;

use FQ\Dirs\Dir;

class Dirs {
/**
* @param Dir[] $array1
* @param Dir[] $array2
* @return bool Returns true if the array contains equal dirs in the same order. Otherwise returns false.
*/
public static function equalDirs($array1, $array2) {
if (count($array1) !== count($array2)) return false;
foreach ($array1 as $index => $dir) {
if ($array2[$index] !== $dir) {
return false;
}
}
return true;
}
}
4 changes: 3 additions & 1 deletion tests/FQ/Tests/Collections/AbstractDirCollectionTests.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace FQ\Tests\Collections;

use FQ\Collections\DirCollection;
use FQ\Dirs\Dir;
use FQ\Tests\AbstractFQTest;

class AbstractDirCollectionTests extends AbstractFQTest {
Expand All @@ -11,6 +12,7 @@ class AbstractDirCollectionTests extends AbstractFQTest {

protected function setUp()
{
parent::setUp();
// Create a new FQ app,
// since we need one pretty much everywhere
$this->_dirCollection = $this->_createNewDirCollection();
Expand All @@ -30,7 +32,7 @@ protected function _createNewDirCollection() {
return new DirCollection();
}

protected function _addDirToCollection($dir = null, $index = null) {
protected function _addDirToCollection(Dir $dir = null, $index = null) {
$dir = $dir === null ? $this->_createNewDir() : $dir;
$collection = $this->dirCollection();
return $collection->addDir($dir, $index);
Expand Down
50 changes: 50 additions & 0 deletions tests/FQ/Tests/Collections/Query/AbstractQueryCollectionTests.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<?php

namespace FQ\Tests\Collections\Query;

use FQ\Collections\Query\QueryCollection;
use FQ\Query\FilesQuery;
use FQ\Tests\AbstractFQTest;

class AbstractQueryCollectionTests extends AbstractFQTest
{
protected $_queryCollection;

const DEFAULT_QUERY_ID = 'query1';
const SECOND_QUERY_ID = 'query2';

protected function setUp()
{
parent::setUp();
// Create a new FQ app,
// since we need one pretty much everywhere
$this->_queryCollection = $this->_createNewQueryCollection();
}

/**
* @return QueryCollection
*/
protected function queryCollection()
{
return $this->_queryCollection;
}

/**
* @return QueryCollection
*/
protected function _createNewQueryCollection()
{
return new QueryCollection($this->_fqApp);
}

protected function _addQueryToCollection($id = self::DEFAULT_QUERY_ID, FilesQuery $query = null)
{
$query = $query === null ? new FilesQuery($this->_fqApp) : $query;
$collection = $this->queryCollection();
return $collection->addQuery($id, $query);
}

function testIgnore() {

}
}
Loading

0 comments on commit 82716ae

Please sign in to comment.