Skip to content

Commit 7b046e6

Browse files
harm-lessharm-less
authored andcommitted
Requirements bugfixes
1 parent 41f6543 commit 7b046e6

File tree

9 files changed

+206
-82
lines changed

9 files changed

+206
-82
lines changed

index.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@
1010
pr($sampleSimple->queryFile1FromChild1());
1111
pr($sampleSimple->queryFile1FromRoot1AndFromChild1());
1212
pr($sampleSimple->queryFile1InReverse());
13-
pr($sampleSimple->queryNonExistingFileWithRequirement());
13+
pr($sampleSimple->queryNonExistingFileWithRequirementOne());
14+
pr($sampleSimple->queryNonExistingFileWithRequirementLast());
15+
pr($sampleSimple->queryNonExistingFileWithRequirementAll());
1416

1517

1618
function pr($var) {

samples/FQ/Samples/Simple.php

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,17 @@ public function queryFile1InReverse() {
3333
return $builder->reverse(true)->run('File1')->listPaths();
3434
}
3535

36-
public function queryNonExistingFileWithRequirement() {
36+
public function queryNonExistingFileWithRequirementOne() {
3737
$builder = new FilesQueryBuilder($this);
38-
return $builder->addRequirement(FilesQueryRequirements::LEVELS_ONE)->run('does-not-exist')->listPaths();
38+
return $builder->addRequirement(FilesQueryRequirements::REQUIRE_ONE)->run('File1')->listPaths();
3939
}
4040

41+
public function queryNonExistingFileWithRequirementLast() {
42+
$builder = new FilesQueryBuilder($this);
43+
return $builder->addRequirement(FilesQueryRequirements::REQUIRE_LAST)->run('File1')->listPaths();
44+
}
45+
public function queryNonExistingFileWithRequirementAll() {
46+
$builder = new FilesQueryBuilder($this);
47+
return $builder->addRequirement(FilesQueryRequirements::REQUIRE_ALL)->run('File1')->listPaths();
48+
}
4149
}

src/FQ/Files.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -234,7 +234,7 @@ public function getFullPath($rootDir, $childDir) {
234234
public function queryPath($fileName, ChildSelection $childSelection = null, RootSelection $rootSelection = null, $reverseLoad = false) {
235235
$query = $this->query($rootSelection, $childSelection, true, true);
236236
$query->reverse($reverseLoad);
237-
$query->requirements(FilesQueryRequirements::LEVELS_ONE);
237+
$query->requirements(FilesQueryRequirements::REQUIRE_ONE);
238238
if ($query->run($fileName)) {
239239
$paths = $query->listPaths();
240240
if (count($paths)) foreach ($paths as $path) return $path;
@@ -265,7 +265,7 @@ public function loadFile($fileName, ChildSelection $childSelection = null, RootS
265265
* @param bool $reverseLoad
266266
* @return bool
267267
*/
268-
public function loadFiles($fileName, RootSelection $rootDirs = null, ChildSelection $children = null, $requiredLevels = FilesQueryRequirements::LEVELS_ONE, $reverseLoad = false)
268+
public function loadFiles($fileName, RootSelection $rootDirs = null, ChildSelection $children = null, $requiredLevels = FilesQueryRequirements::REQUIRE_ONE, $reverseLoad = false)
269269
{
270270
$query = $this->query($rootDirs, $children, true, true);
271271
$query->reverse($reverseLoad);

src/FQ/query/FilesQuery.php

Lines changed: 104 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,24 @@ class FilesQuery {
4141
*/
4242
private $_cachedQueryRootDirs;
4343

44+
/**
45+
* @var array
46+
*/
47+
private $_cachedPaths;
48+
/**
49+
* @var string[]
50+
*/
51+
private $_cachedPathsSimple;
52+
53+
/**
54+
* @var array
55+
*/
56+
private $_cachedRawPaths;
57+
/**
58+
* @var string[]
59+
*/
60+
private $_cachedRawPathsSimple;
61+
4462
/**
4563
* @var bool Indicator if the query has run or not
4664
*/
@@ -90,6 +108,9 @@ public function reset() {
90108
$this->_cachedQueryRootDirs = null;
91109
$this->_currentQueryChildren = array();
92110

111+
$this->_cachedPaths = null;
112+
$this->_cachedRawPaths = null;
113+
93114
$this->requirements()->removeAll();
94115
$this->filters(self::FILTER_EXISTING, false);
95116

@@ -173,7 +194,13 @@ public function queriedFileName() {
173194
return $this->_queriedFileName;
174195
}
175196

176-
public function queryChildDirs() {
197+
/**
198+
* @return FilesQueryChild[]
199+
*/
200+
public function queryChildDirs($reversed = false) {
201+
if ($reversed === true) {
202+
return array_reverse($this->_currentQueryChildren);
203+
}
177204
return $this->_currentQueryChildren;
178205
}
179206

@@ -211,14 +238,13 @@ public function run($fileName) {
211238

212239
$this->_currentQueryChildren = array();
213240
foreach ($this->getCurrentChildDirSelection() as $childDir) {
214-
$queryChild = $this->_processQueryChild($childDir, $rootDirsSelection);
215-
$meetsRequirements = $this->requirements()->meetsRequirements($queryChild, false);
216-
if ($meetsRequirements !== true) {
217-
$this->_queryError = $meetsRequirements;
218-
}
219-
$this->_currentQueryChildren[$childDir->id()] = $queryChild;
241+
$this->_currentQueryChildren[$childDir->id()] = $this->_processQueryChild($childDir, $rootDirsSelection);;
220242
}
221243
$this->_hasRun = true;
244+
$meetsRequirements = $this->requirements()->meetsRequirements($this, false);
245+
if ($meetsRequirements !== true) {
246+
$this->_queryError = $meetsRequirements;
247+
}
222248
return $this->_queryError === null;
223249
}
224250

@@ -273,17 +299,80 @@ protected function _getQueryChild(ChildDir $childDir) {
273299
public function listPaths() {
274300
$this->_hasRunCheck();
275301

276-
$paths = array();
277-
foreach($this->queryChildDirs() as $childQuery) {
278-
// in case one of the child queries failed and returned false, do not try to add this to the list
279-
if ($childQuery !== false) {
280-
$paths = array_merge_recursive($paths, $childQuery->filteredAbsolutePaths());
302+
if ($this->_cachedPaths === null) {
303+
304+
$paths = array();
305+
foreach ($this->queryChildDirs() as $childQuery) {
306+
// in case one of the child queries failed and returned false, do not try to add this to the list
307+
if ($childQuery !== false) {
308+
$paths = array_merge_recursive($paths, $childQuery->filteredAbsolutePaths());
309+
}
310+
}
311+
if ($this->isReversed()) {
312+
$paths = $this->reversePaths($paths);
281313
}
314+
$this->_cachedPaths = $paths;
282315
}
283-
if ($this->isReversed()) {
284-
$paths = $this->reversePaths($paths);
316+
return $this->_cachedPaths;
317+
}
318+
/**
319+
* Returns a list of all the paths it found
320+
*
321+
* @return null|string[]
322+
*/
323+
public function listPathsSimple() {
324+
$this->_hasRunCheck();
325+
326+
if ($this->_cachedPathsSimple === null) {
327+
$temp = array();
328+
foreach ($this->listPaths() as $rootDirPaths) {
329+
$temp = array_merge($temp, (array) $rootDirPaths);
330+
}
331+
$this->_cachedPathsSimple = $temp;
285332
}
286-
return $paths;
333+
return $this->_cachedPathsSimple;
334+
}
335+
336+
/**
337+
* Returns a list of all the paths it found
338+
*
339+
* @return null|string[]
340+
*/
341+
public function listRawPaths() {
342+
$this->_hasRunCheck();
343+
344+
if ($this->_cachedRawPaths === null) {
345+
346+
$paths = array();
347+
foreach ($this->queryChildDirs() as $childQuery) {
348+
// in case one of the child queries failed and returned false, do not try to add this to the list
349+
if ($childQuery !== false) {
350+
$paths = array_merge_recursive($paths, $childQuery->rawAbsolutePaths());
351+
}
352+
}
353+
if ($this->isReversed()) {
354+
$paths = $this->reversePaths($paths);
355+
}
356+
$this->_cachedRawPaths = $paths;
357+
}
358+
return $this->_cachedRawPaths;
359+
}
360+
/**
361+
* Returns a list of all the paths it found
362+
*
363+
* @return null|string[]
364+
*/
365+
public function listRawPathsSimple() {
366+
$this->_hasRunCheck();
367+
368+
if ($this->_cachedRawPathsSimple === null) {
369+
$temp = array();
370+
foreach ($this->listRawPaths() as $rootDirPaths) {
371+
$temp = array_merge($temp, (array) $rootDirPaths);
372+
}
373+
$this->_cachedRawPathsSimple = $temp;
374+
}
375+
return $this->_cachedRawPathsSimple;
287376
}
288377

289378
/**

src/FQ/query/FilesQueryBuilder.php

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ class FilesQueryBuilder {
2424
function __construct(Files $files) {
2525
$this->_childDirs = array();
2626
$this->_requirements = array();
27-
$this->_reset = true;
2827

2928
$this->_files = $files;
3029
}
@@ -40,6 +39,9 @@ public function fileName($fileName) {
4039
protected function getFileName() {
4140
return $this->_fileName;
4241
}
42+
protected function _reset() {
43+
return $this->_reset === null ? true : $this->_reset;
44+
}
4345
protected function _getRequirements() {
4446
return $this->_requirements;
4547
}
@@ -162,7 +164,7 @@ public function showErrors($showErrors) {
162164
}
163165

164166
public function run($fileName = null) {
165-
$query = $this->_files()->query($this->rootSelection(), $this->childSelection(), $this->_reset);
167+
$query = $this->_files()->query($this->rootSelection(), $this->childSelection(), $this->_reset(), $this->_reset());
166168
$requirements = $query->requirements();
167169
$requirements->addRequirements($this->_getRequirements());
168170
$query->reverse($this->_isReversed());
@@ -173,10 +175,10 @@ public function run($fileName = null) {
173175
}
174176

175177
$fileNameToUse = $fileName !== null ? $fileName : $this->getFileName();
176-
177178
if (!is_string($fileNameToUse)) {
178179
throw new FileQueryBuilderException('No filename has been set. Use filename() to use a filename for the query or supply it this this run() method');
179180
}
181+
180182
$result = $query->run($fileNameToUse);
181183
if ($result !== true && $this->_showErrors()) {
182184
throw $query->queryError();

src/FQ/query/FilesQueryRequirements.php

Lines changed: 52 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -19,16 +19,16 @@ class FilesQueryRequirements {
1919
/**
2020
* Constants determining requirement checking for the query
2121
*/
22-
const LEVELS_ONE = 'levels_one';
23-
const LEVELS_LAST = 'levels_last';
24-
const LEVELS_ALL = 'levels_all';
22+
const REQUIRE_ONE = 'require_one';
23+
const REQUIRE_LAST = 'require_last';
24+
const REQUIRE_ALL = 'require_all';
2525

2626
function __construct() {
2727
$this->_requirements = array();
2828

29-
$this->registerRequirement(self::LEVELS_ONE, array($this, 'requirementAtLeastOne'));
30-
$this->registerRequirement(self::LEVELS_LAST, array($this, 'requirementLast'));
31-
$this->registerRequirement(self::LEVELS_ALL, array($this, 'requirementAll'));
29+
$this->registerRequirement(self::REQUIRE_ONE, array($this, 'requirementAtLeastOne'));
30+
$this->registerRequirement(self::REQUIRE_LAST, array($this, 'requirementLast'));
31+
$this->registerRequirement(self::REQUIRE_ALL, array($this, 'requirementAll'));
3232
}
3333

3434
/**
@@ -170,73 +170,97 @@ public function isRegisteredRequirement($id) {
170170

171171
/**
172172
* @param string $id
173-
* @param FilesQueryChild $child
173+
* @param FilesQuery $query
174174
* @throws FileQueryRequirementsException
175175
* @return mixed
176176
*/
177-
public function tryRequirement($id, FilesQueryChild $child) {
177+
public function tryRequirement($id, FilesQuery $query) {
178178
if (!$this->requirementIsRegistered($id)) {
179179
throw new FileQueryRequirementsException(sprintf('Trying to call a requirement, but it isn\'t registered. Provided requirement "%s"', $id), 10);
180180
}
181-
return call_user_func($this->_registeredRequirements[$id], $child);
181+
return call_user_func($this->_registeredRequirements[$id], $query);
182182
}
183183

184184
/**
185-
* @param FilesQueryChild $child
185+
* @param FilesQuery $query
186186
* @return bool|FileQueryRequirementsException
187187
*/
188-
protected function requirementAtLeastOne(FilesQueryChild $child) {
189-
if (!$child->totalExistingPaths() >= 1) {
190-
return new FileQueryRequirementsException(sprintf('At least 1 file must be available for file "%s" in child with an id of "%s". Please create the file in any of these locations: "%s"', $child->relativePath(), $child->childDir()->id(), implode('", "', $child->rawAbsolutePaths())));
188+
protected function requirementAtLeastOne(FilesQuery $query) {
189+
if (!$query->hasPaths() >= 1) {
190+
return new FileQueryRequirementsException(sprintf('At least 1 file must be available for file "%s". Please create the file in any of these locations: "%s"', $query->queriedFileName(), implode('", "', $query->listRawPathsSimple())));
191191
}
192192
return true;
193193
}
194194

195195
/**
196-
* @param FilesQueryChild $child
196+
* @param FilesQuery $query
197197
* @return bool|FileQueryRequirementsException
198198
*/
199-
protected function requirementLast(FilesQueryChild $child) {
200-
$pathsExist = array_reverse($child->pathsExist());
201-
foreach ($pathsExist as $rootDir) {
202-
if ($rootDir === false) {
203-
return new FileQueryRequirementsException(sprintf('Last file "%s" not found in child "%s" but it is required', $child->relativePath(), $child->childDir()->id()));
199+
protected function requirementLast(FilesQuery $query) {
200+
$oneExists = false;
201+
202+
$lastRootDirId = null;
203+
$rootSelection = $query->getCurrentRootDirSelection();
204+
if (count($rootSelection) >= 1) {
205+
$lastRootDirId = $rootSelection[0]->id();
206+
207+
foreach ($query->queryChildDirs(true) as $child) {
208+
$pathExist = $child->pathsExist();
209+
if ($pathExist[$lastRootDirId] !== false) {
210+
$oneExists = true;
211+
break;
212+
}
213+
}
214+
}
215+
216+
if (!$oneExists) {
217+
if ($lastRootDirId === null) {
218+
return new FileQueryRequirementsException('Query requires at least one file to exist in at least one child directory, but there isn\'t even a root directory. Make sure you have at least one root directory in your query');
219+
}
220+
221+
$paths = array();
222+
foreach ($query->queryChildDirs(true) as $child) {
223+
$rawPaths = $child->rawAbsolutePaths();
224+
$paths = array_merge($paths, (array) $rawPaths[$lastRootDirId]);
204225
}
205-
// just check the first root dir (which is the last one because it was reversed)
206-
break;
226+
return new FileQueryRequirementsException(sprintf('Query requires at least one file to exist in at least one child directory in the last root directory with ID "%s". File must be present in one of the following locations: "%s"', $lastRootDirId, implode('", "', $paths)));
207227
}
208228
return true;
209229
}
210230

211231
/**
212-
* @param FilesQueryChild $child
232+
* @param FilesQuery $query
213233
* @return bool|FileQueryRequirementsException
214234
*/
215-
protected function requirementAll(FilesQueryChild $child) {
216-
if ($child->totalExistingPaths() != count($child->getRootDirs())) {
217-
return new FileQueryRequirementsException(sprintf('All "%s" children must contain a file called "%s".', $child->childDir()->id(), $child->relativePath()));
235+
protected function requirementAll(FilesQuery $query) {
236+
if (count($query->listPathsSimple()) != count($query->listRawPathsSimple())) {
237+
$totalNeeded = count($query->listRawPathsSimple());
238+
$totalAvailable = count($query->listPathsSimple());
239+
240+
$neededPaths = array_diff($query->listRawPathsSimple(), $query->listPathsSimple());
241+
return new FileQueryRequirementsException(sprintf('All root directories and children must contain a file called "%s". Total files needed is %s but only %s files exist. Make sure that file is also available in the following locations: "%s"', $query->queriedFileName(), $totalNeeded, $totalAvailable, implode('", "', $neededPaths)));
218242
}
219243
return true;
220244
}
221245

222246
/**
223247
* Checks if the query meets all its requirements
224248
*
225-
* @param FilesQueryChild $queryChild
249+
* @param FilesQuery $query
226250
* @param bool $throwExceptionOnFail
227251
* @throws \Exception When $throwExceptionOnFail is set to true and one of the requirements fails, it will throw
228252
* the exception from that fail. Otherwise this exception will be returned
229253
* @return mixed Returns true if all requirements are met. Otherwise returns an un-thrown exception
230254
* if 'throwExceptionOnFail' is set to false or the response from the requirement
231255
*/
232-
public function meetsRequirements(FilesQueryChild $queryChild, $throwExceptionOnFail = true) {
256+
public function meetsRequirements(FilesQuery $query, $throwExceptionOnFail = true) {
233257
// if there are no requirements it certainly is valid and it can be returned immediately
234258
if (!$this->hasRequirements()) {
235259
return true;
236260
}
237261

238262
foreach ($this->requirements() as $requirement) {
239-
$attempt = $this->tryRequirement($requirement, $queryChild);
263+
$attempt = $this->tryRequirement($requirement, $query);
240264
if ($attempt instanceof \Exception && $throwExceptionOnFail === true) {
241265
throw $attempt;
242266
}

0 commit comments

Comments
 (0)