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    public function __construct( DbFactory $dbFactory ) {
51        $this->dbr = $dbFactory->getDB( DB_REPLICA );
52        $this->conditions = [ 'workflow_wiki' => WikiMap::getCurrentWikiId() ];
53    }
54
55    /**
56     * @return bool|IResultWrapper
57     */
58    abstract protected function query();
59
60    /**
61     * @param array|int|null $pageId
62     */
63    public function setPage( $pageId = null ) {
64        $this->results = null;
65
66        unset( $this->conditions['workflow_page_id'] );
67        if ( $pageId !== null ) {
68            $this->conditions['workflow_page_id'] = $pageId;
69        }
70    }
71
72    /**
73     * @param int|null $namespace
74     */
75    public function setNamespace( $namespace = null ) {
76        $this->results = null;
77
78        unset( $this->conditions['workflow_namespace'] );
79        if ( $namespace !== null ) {
80            $this->conditions['workflow_namespace'] = $namespace;
81        }
82    }
83
84    /**
85     * Define where to start iterating (inclusive)
86     *
87     * @param UUID|null $revId
88     */
89    public function setFrom( ?UUID $revId = null ) {
90        $this->results = null;
91
92        unset( $this->conditions[0] );
93        if ( $revId !== null ) {
94            $this->conditions[0] = $this->dbr->expr( 'rev_id', '>=', $revId->getBinary() );
95        }
96    }
97
98    /**
99     * Define where to stop iterating (exclusive)
100     *
101     * @param UUID|null $revId
102     */
103    public function setTo( ?UUID $revId = null ) {
104        $this->results = null;
105
106        unset( $this->conditions[1] );
107        if ( $revId !== null ) {
108            $this->conditions[1] = $this->dbr->expr( 'rev_id', '<', $revId->getBinary() );
109        }
110    }
111
112    /**
113     * @return AbstractRevision|null The most recently fetched revision object
114     */
115    #[ReturnTypeWillChange]
116    public function current() {
117        return $this->current;
118    }
119
120    /**
121     * @return int 0-indexed count of the page number fetched
122     */
123    #[\ReturnTypeWillChange]
124    public function key() {
125        return $this->key;
126    }
127
128    /**
129     * Reset the iterator to the beginning of the table.
130     */
131    public function rewind(): void {
132        $this->results = null;
133        // self::next() will turn this into 0
134        $this->key = -1;
135        $this->current = null;
136        $this->next();
137    }
138
139    /**
140     * @return bool True when the iterator is in a valid state
141     */
142    public function valid(): bool {
143        return (bool)$this->current;
144    }
145
146    /**
147     * Fetch the next set of rows from the database.
148     */
149    public function next(): void {
150        if ( $this->results === null ) {
151            $this->results = $this->query();
152        }
153
154        $current = $this->results->fetchObject();
155        if ( $current !== false ) {
156            $this->current = $this->transform( $current );
157            $this->key++;
158        } else {
159            // end of iteration reached
160            $this->current = false;
161            $this->key = null;
162        }
163    }
164
165    /**
166     * Transforms the DB row into a revision object.
167     *
168     * $row will be one of the results of static::query(). In this method, $row
169     * is expected to have at least properties `rev_id` & `rev_type`, which will
170     * be used to fetch this specific row's data from storage.
171     *
172     * This will need to do some DB/cache requests. Ideally, those would be
173     * bundled instead of being done on a per-row record. These iterators
174     * are only meant to be run in maintenance scripts, however, so it
175     * doesn't really matter that much ;)
176     *
177     * @param stdClass $row
178     * @return AbstractRevision
179     */
180    protected function transform( stdClass $row ) {
181        $uuid = UUID::create( $row->rev_id );
182
183        /** @var ManagerGroup $storage */
184        $storage = Container::get( 'storage' );
185
186        // prevent memory from being filled up
187        $storage->clear();
188
189        return $storage->getStorage( $row->rev_type )->get( $uuid );
190    }
191}