private static function searchPath($prefix, $path, $conditions)
	{
		$result = [];
		
		$dir = new DirectoryIterator($path);
		foreach ( $dir as $entry ) {
			if ( $entry->isDot() ) {
				continue;
			}
			
			$de = $entry->getFilename();
			
			if ( $entry->isDir() && preg_match('/^[A-Za-z_]+$/', $de) ) {
				// it's a subdirectory, search it
				$result = array_merge($result, self::searchPath("$prefix\\$de", $path . DIRECTORY_SEPARATOR . $de, $conditions));
			}
			else if ( preg_match('/\.php$/', $de) ) {
				// FIXME I'm using a regex test here because that in theory is quicker than having
				// PHP parse the file. Also, this is deliberately designed to exclude abstract classes,
				// which will cause class_exists() to return (bool)true.
				
				$foundNamespace = $foundClass = false;
				$className = preg_replace('/\.php$/', '', $de);
				
				if ( $fp = @fopen($filePath = "$path/$de", 'r') ) {
					
					while ( !feof($fp) && ($line = fgets($fp)) ) {
						// FIXME PHP leaves pretty little room for error in the namespace declaration
						// line. This is still a little needlessly picky - if someone has a space between
						// the namespace name and the semicolon this test will fail, as well as dumber
						// stuff like the word "namespace" being mixed case/uppercase.
						if ( trim($line) === "namespace $prefix;" ) {
							$foundNamespace = true;
						}
						// This fairly complicated regexp covers "implements" with multiple inheritance,
						// "extends" and a fair amount of syntactical deviation.
						else if ( preg_match('/^\s*class ([A-Za-z0-9_]+)(?:\s+(?:extends|implements) [A-Za-z0-9_\\\\]+(?:\s*,\s*[A-Za-z0-9_]+)*)*\s*[\{,]?$/', trim($line), $match)
								&& ($match[1] === "\\$prefix\\$className" || $match[1] === $className) ) {
							$foundClass = true;
						}
						
						// if we have found the namespace declaration and the class declaration,
						// we no longer need any more information from this file, so break and
						// close the handle.
						if ( $foundNamespace && $foundClass ) {
							break;
						}
					}
					
					fclose($fp);
				}
				
				// if we found the namespace declaration and the class declaration, append
				// the discovered class to the result list.
				if ( $foundNamespace && $foundClass )
				{
					// fully qualified class name
					$fqcn = "$prefix\\$className";
					
					// conditions are an OR between each, AND within an individual condition
					$condCount = 0;
					
					// ReflectionClass instance - created only if necessary
					$rfl = null;
					foreach ( $conditions as $cond ) {
						$condResult = true;
						foreach ( $cond as $test => $value ) {
							switch($test) {
							case 'implements':
								if ( empty($rfl) ) {
									$rfl = new \ReflectionClass($fqcn);
								}
								if ( !$rfl->implementsInterface($value) ) {
									$condResult = false;
									break 2; // lazy evaluation
								}
								break;
							case 'extends':
								if ( empty($rfl) ) {
									$rfl = new \ReflectionClass($fqcn);
								}
								if ( $rfl->getParentClass()->getName() !== $value ) {
									$condResult = false;
									break 2; // lazy evaluation
								}
								break;
							case 'abstract':
								if ( empty($rfl) ) {
									$rfl = new \ReflectionClass($fqcn);
								}
								if ( $rfl->isAbstract() !== $value ) {
									$condResult = false;
									break 2; // lazy evaluation
								}
								break;
							}
						}
						if ( $condResult ) {
							$condCount++;
						}
					}
					
					if ( $condCount || count($conditions) === 0 ) {
						$result[] = $fqcn;
					}
				}
			}
		}
		
		return $result;
	}