Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 37
0.00% covered (danger)
0.00%
0 / 11
CRAP
0.00% covered (danger)
0.00%
0 / 1
AbstractIterator
0.00% covered (danger)
0.00%
0 / 37
0.00% covered (danger)
0.00%
0 / 11
306
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 query
n/a
0 / 0
n/a
0 / 0
0
 setPage
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
 setNamespace
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
 setFrom
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
 setTo
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
 current
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 key
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 rewind
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
2
 valid
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 next
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
12
 transform
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2
3namespace Flow\Search\Iterators;
4
5use Flow\Container;
6use Flow\Data\ManagerGroup;
7use Flow\DbFactory;
8use Flow\Model\AbstractRevision;
9use Flow\Model\UUID;
10use Iterator;
11use MediaWiki\WikiMap\WikiMap;
12use ReturnTypeWillChange;
13use stdClass;
14use Wikimedia\Rdbms\IReadableDatabase;
15use Wikimedia\Rdbms\IResultWrapper;
16
17abstract class AbstractIterator implements Iterator {
18    /**
19     * @var IReadableDatabase
20     */
21    protected $dbr;
22
23    /**
24     * @var array
25     */
26    protected $conditions = [];
27
28    /**
29     * @var IResultWrapper|null
30     */
31    protected $results;
32
33    /**
34     * Depending on where we are in the iteration, this can be null (object
35     * constructed but not yet being iterated over), AbstractRevision (being
36     * iterated) or false (end of iteration, no more revisions)
37     *
38     * @var AbstractRevision|null|false
39     */
40    protected $current;
41
42    /**
43     * Depending on where we are in the iteration, this can be integer (object
44     * being iterated over) or null (iteration not yet started, or completed)
45     *
46     * @var int|null
47     */
48    protected $key;
49
50    /**
51     * @param DbFactory $dbFactory
52     */
53    public function __construct( DbFactory $dbFactory ) {
54        $this->dbr = $dbFactory->getDB( DB_REPLICA );
55        $this->conditions = [ 'workflow_wiki' => WikiMap::getCurrentWikiId() ];
56    }
57
58    /**
59     * @return bool|IResultWrapper
60     */
61    abstract protected function query();
62
63    /**
64     * @param array|int|null $pageId
65     */
66    public function setPage( $pageId = null ) {
67        $this->results = null;
68
69        unset( $this->conditions['workflow_page_id'] );
70        if ( $pageId !== null ) {
71            $this->conditions['workflow_page_id'] = $pageId;
72        }
73    }
74
75    /**
76     * @param int|null $namespace
77     */
78    public function setNamespace( $namespace = null ) {
79        $this->results = null;
80
81        unset( $this->conditions['workflow_namespace'] );
82        if ( $namespace !== null ) {
83            $this->conditions['workflow_namespace'] = $namespace;
84        }
85    }
86
87    /**
88     * Define where to start iterating (inclusive)
89     *
90     * @param UUID|null $revId
91     */
92    public function setFrom( ?UUID $revId = null ) {
93        $this->results = null;
94
95        unset( $this->conditions[0] );
96        if ( $revId !== null ) {
97            $this->conditions[0] = $this->dbr->expr( 'rev_id', '>=', $revId->getBinary() );
98        }
99    }
100
101    /**
102     * Define where to stop iterating (exclusive)
103     *
104     * @param UUID|null $revId
105     */
106    public function setTo( ?UUID $revId = null ) {
107        $this->results = null;
108
109        unset( $this->conditions[1] );
110        if ( $revId !== null ) {
111            $this->conditions[1] = $this->dbr->expr( 'rev_id', '<', $revId->getBinary() );
112        }
113    }
114
115    /**
116     * @return AbstractRevision|null The most recently fetched revision object
117     */
118    #[ReturnTypeWillChange]
119    public function current() {
120        return $this->current;
121    }
122
123    /**
124     * @return int 0-indexed count of the page number fetched
125     */
126    #[\ReturnTypeWillChange]
127    public function key() {
128        return $this->key;
129    }
130
131    /**
132     * Reset the iterator to the beginning of the table.
133     */
134    public function rewind(): void {
135        $this->results = null;
136        // self::next() will turn this into 0
137        $this->key = -1;
138        $this->current = null;
139        $this->next();
140    }
141
142    /**
143     * @return bool True when the iterator is in a valid state
144     */
145    public function valid(): bool {
146        return (bool)$this->current;
147    }
148
149    /**
150     * Fetch the next set of rows from the database.
151     */
152    public function next(): void {
153        if ( $this->results === null ) {
154            $this->results = $this->query();
155        }
156
157        $current = $this->results->fetchObject();
158        if ( $current !== false ) {
159            $this->current = $this->transform( $current );
160            $this->key++;
161        } else {
162            // end of iteration reached
163            $this->current = false;
164            $this->key = null;
165        }
166    }
167
168    /**
169     * Transforms the DB row into a revision object.
170     *
171     * $row will be one of the results of static::query(). In this method, $row
172     * is expected to have at least properties `rev_id` & `rev_type`, which will
173     * be used to fetch this specific row's data from storage.
174     *
175     * This will need to do some DB/cache requests. Ideally, those would be
176     * bundled instead of being done on a per-row record. These iterators
177     * are only meant to be run in maintenance scripts, however, so it
178     * doesn't really matter that much ;)
179     *
180     * @param stdClass $row
181     * @return AbstractRevision
182     */
183    protected function transform( stdClass $row ) {
184        $uuid = UUID::create( $row->rev_id );
185
186        /** @var ManagerGroup $storage */
187        $storage = Container::get( 'storage' );
188
189        // prevent memory from being filled up
190        $storage->clear();
191
192        return $storage->getStorage( $row->rev_type )->get( $uuid );
193    }
194}