/**
* @ingroup Search
*/
-class SearchResultSet {
-
+class SearchResultSet implements Countable, IteratorAggregate {
/**
* Types of interwiki results
*/
* as an array.
* @var SearchResult[]
*/
- private $results;
+ protected $results;
/**
* Set of result's extra data, indexed per result id
*/
protected $extraData = [];
- public function __construct( $containedSyntax = false ) {
+ /**
+ * @var boolean True when there are more pages of search results available.
+ */
+ private $hasMoreResults;
+
+ /**
+ * @var ArrayIterator|null Iterator supporting BC iteration methods
+ */
+ private $bcIterator;
+
+ /**
+ * @param bool $containedSyntax True when query is not requesting a simple
+ * term match
+ * @param bool $hasMoreResults True when there are more pages of search
+ * results available.
+ */
+ public function __construct( $containedSyntax = false, $hasMoreResults = false ) {
+ if ( static::class === __CLASS__ ) {
+ // This class will eventually be abstract. SearchEngine implementations
+ // already have to extend this class anyways to provide the actual
+ // search results.
+ wfDeprecated( __METHOD__, 1.32 );
+ }
$this->containedSyntax = $containedSyntax;
+ $this->hasMoreResults = $hasMoreResults;
}
/**
}
function numRows() {
- return 0;
+ return $this->count();
+ }
+
+ final public function count() {
+ return count( $this->extractResults() );
}
/**
/**
* Fetches next search result, or false.
- * STUB
- * FIXME: refactor as iterator, so we could use nicer interfaces.
- * @deprecated since 1.32; Use self::extractResults()
+ * @deprecated since 1.32; Use self::extractResults() or foreach
* @return SearchResult|false
*/
- function next() {
- return false;
+ public function next() {
+ wfDeprecated( __METHOD__, '1.32' );
+ $it = $this->bcIterator();
+ $searchResult = $it->current();
+ $it->next();
+ return $searchResult ?? false;
}
/**
* Rewind result set back to beginning
- * @deprecated since 1.32; Use self::extractResults()
+ * @deprecated since 1.32; Use self::extractResults() or foreach
*/
- function rewind() {
+ public function rewind() {
+ wfDeprecated( __METHOD__, '1.32' );
+ $this->bcIterator()->rewind();
+ }
+
+ private function bcIterator() {
+ if ( $this->bcIterator === null ) {
+ $this->bcIterator = 'RECURSION';
+ $this->bcIterator = $this->getIterator();
+ } elseif ( $this->bcIterator === 'RECURSION' ) {
+ // Either next/rewind or extractResults must be implemented. This
+ // class was potentially instantiated directly. It should be
+ // abstract with abstract methods to enforce this but that's a
+ // breaking change...
+ wfDeprecated( static::class . ' without implementing extractResults', '1.32' );
+ $this->bcIterator = new ArrayIterator( [] );
+ }
+ return $this->bcIterator;
}
/**
return $this->containedSyntax;
}
+ /**
+ * @return bool True when there are more pages of search results available.
+ */
+ public function hasMoreResults() {
+ return $this->hasMoreResults;
+ }
+
+ /**
+ * @param int $limit Shrink result set to $limit and flag
+ * if more results are available.
+ */
+ public function shrink( $limit ) {
+ if ( $this->count() > $limit ) {
+ $this->hasMoreResults = true;
+ // shrinking result set for implementations that
+ // have not implemented extractResults and use
+ // the default cache location. Other implementations
+ // must override this as well.
+ if ( is_array( $this->results ) ) {
+ $this->results = array_slice( $this->results, 0, $limit );
+ } else {
+ throw new \UnexpectedValueException(
+ "When overriding result store extending classes must "
+ . " also override " . __METHOD__ );
+ }
+ }
+ }
+
/**
* Extract all the results in the result set as array.
* @return SearchResult[]
/**
* Returns extra data for specific result and store it in SearchResult object.
* @param SearchResult $result
- * @return array|null List of data as name => value or null if none present.
*/
public function augmentResult( SearchResult $result ) {
$id = $result->getTitle()->getArticleID();
- if ( !$id || !isset( $this->extraData[$id] ) ) {
- return null;
+ if ( $id === -1 ) {
+ return;
}
- $result->setExtensionData( $this->extraData[$id] );
- return $this->extraData[$id];
+ $result->setExtensionData( function () use ( $id ) {
+ return $this->extraData[$id] ?? [];
+ } );
}
/**
public function getOffset() {
return null;
}
+
+ final public function getIterator() {
+ return new ArrayIterator( $this->extractResults() );
+ }
}