Completed
Push — master ( a2db4a...affb95 )
by
unknown
07:39 queued 10s
created

getGroupedRepoRevisionIdentifiers()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 9
rs 9.9666
c 0
b 0
f 0
cc 2
nc 2
nop 0
1
<?php
2
3
declare( strict_types = 1 );
4
5
namespace Wikibase\Client\ChangeModification;
6
7
use Job;
8
use Wikibase\Client\RecentChanges\RecentChangeFactory;
9
use Wikibase\Lib\Changes\RepoRevisionIdentifier;
10
use Wikibase\Lib\Changes\RepoRevisionIdentifierFactory;
11
use Wikimedia\Assert\Assert;
12
use Wikimedia\Rdbms\ILoadBalancer;
13
use Wikimedia\Rdbms\IResultWrapper;
14
15
/**
16
 * Base class for Jobs handling modifications to a set of client changes (identified
17
 * by RepoRevisionIdentifiers).
18
 *
19
 * @license GPL-2.0-or-later
20
 * @author Marius Hoch
21
 */
22
abstract class ChangeModificationNotificationJob extends Job {
23
24
	/**
25
	 * @var ILoadBalancer
26
	 */
27
	protected $loadBalancer;
28
29
	/**
30
	 * @var RepoRevisionIdentifier[]
31
	 */
32
	private $revisionIdentifiers;
33
34
	/**
35
	 * @param string $jobName Name of this job.
36
	 * @param ILoadBalancer $loadBalancer
37
	 * @param array $params Contains the revisionIdentifiersJson to act upon.
38
	 */
39
	public function __construct( string $jobName, ILoadBalancer $loadBalancer, array $params = [] ) {
40
		parent::__construct( $jobName, $params );
41
42
		Assert::parameterType( 'array', $params, '$params' );
43
		Assert::parameter(
44
			is_string( $params['revisionIdentifiersJson'] ) && $params['revisionIdentifiersJson'] !== '',
45
			'$params',
46
			'$params[\'revisionIdentifiersJson\'] must be a non-empty string.'
47
		);
48
49
		$this->revisionIdentifiers = $this->unpackRevisionIdentifiers( $params['revisionIdentifiersJson'] );
50
51
		$this->loadBalancer = $loadBalancer;
52
	}
53
54
	/**
55
	 * @param string $revisionIdentifiersJson
56
	 *
57
	 * @return RepoRevisionIdentifier[]
58
	 */
59
	private function unpackRevisionIdentifiers( string $revisionIdentifiersJson ): array {
60
		$repoRevisionFactory = new RepoRevisionIdentifierFactory();
61
		$revisionIdentifiersArray = json_decode( $revisionIdentifiersJson, true );
62
		$revisionIdentifiers = [];
63
		foreach ( $revisionIdentifiersArray as $revisionIdentifierArray ) {
64
			$revisionIdentifiers[] = $repoRevisionFactory->newFromArray( $revisionIdentifierArray );
65
		}
66
67
		return $revisionIdentifiers;
68
	}
69
70
	/**
71
	 * @return bool success
72
	 */
73
	public function run() {
74
		$relevantChanges = $this->getRelevantRecentChanges();
75
		if ( $relevantChanges === [] ) {
76
			return true;
77
		}
78
79
		$this->modifyChanges( $relevantChanges );
80
81
		return true;
82
	}
83
84
	/**
85
	 * @param int[] $relevantChanges Ids of changes relevant for this job.
86
	 * @return void
87
	 */
88
	abstract protected function modifyChanges( array $relevantChanges ): void;
89
90
	/**
91
	 * @return int[] Relevant rc_ids
92
	 */
93
	protected function getRelevantRecentChanges(): array {
94
		$revisionIdentifiersByEntityId = $this->getGroupedRepoRevisionIdentifiers();
95
96
		$toRedact = [];
97
		foreach ( $revisionIdentifiersByEntityId as $entityIdSerialization => $revisionIdentifiers ) {
98
			$candidateResults = $this->selectCandidateResults(
99
				$entityIdSerialization,
100
				$revisionIdentifiers
101
			);
102
103
			$toRedact = array_merge(
104
				$toRedact,
105
				$this->filterCandidateResults( $candidateResults, $revisionIdentifiers )
106
			);
107
		}
108
109
		return $toRedact;
110
	}
111
112
	/**
113
	 * @param string $entityIdSerialization
114
	 * @param RepoRevisionIdentifier[] $revisionIdentifiers
115
	 *
116
	 * @return IResultWrapper
117
	 */
118
	private function selectCandidateResults(
119
		string $entityIdSerialization,
120
		array $revisionIdentifiers
121
	): IResultWrapper {
122
		$dbr = $this->loadBalancer->getConnection( DB_REPLICA );
123
		$rcParamPattern = $dbr->buildLike(
124
			$dbr->anyString(),
125
			'"', $entityIdSerialization, '"',
126
			$dbr->anyString()
127
		);
128
129
		$timestamps = [];
130
		foreach ( $revisionIdentifiers as $revisionIdentifier ) {
131
			$timestamps[] = $revisionIdentifier->getRevisionTimestamp();
132
		}
133
134
		return $dbr->select(
135
			'recentchanges',
136
			[ 'rc_id', 'rc_params' ],
137
			[
138
				'rc_timestamp' => $timestamps,
139
				"rc_params $rcParamPattern",
140
				'rc_source' => RecentChangeFactory::SRC_WIKIBASE
141
			],
142
			__METHOD__
143
		);
144
	}
145
146
	/**
147
	 * @param IResultWrapper $candidateResults
148
	 * @param RepoRevisionIdentifier[] $revisionIdentifiers
149
	 *
150
	 * @return int[]
151
	 */
152
	private function filterCandidateResults( IResultWrapper $candidateResults, array $revisionIdentifiers ): array {
153
		if ( $candidateResults->numRows() === 0 ) {
154
			return [];
155
		}
156
		$ids = [];
157
		foreach ( $revisionIdentifiers as $revisionIdentifier ) {
158
			$ids[$revisionIdentifier->getRevisionId()] = true;
159
		}
160
161
		$results = [];
162
		foreach ( $candidateResults as $rc ) {
163
			$metadata = $this->readRecentChangeParams( $rc->rc_params );
164
165
			$rev_id = $metadata[ 'rev_id' ];
166
167
			if ( isset( $ids[$rev_id] ) ) {
168
				$results[] = (int)$rc->rc_id;
169
			}
170
		}
171
172
		return $results;
173
	}
174
175
	/**
176
	 * @return RepoRevisionIdentifier[][]
177
	 */
178
	private function getGroupedRepoRevisionIdentifiers(): array {
179
		$revisionIdentifiersByEntityId = [];
180
		foreach ( $this->revisionIdentifiers as $revisionIdentifier ) {
181
			$entityIdSerialization = $revisionIdentifier->getEntityIdSerialization();
182
			$revisionIdentifiersByEntityId[$entityIdSerialization][] = $revisionIdentifier;
183
		}
184
185
		return $revisionIdentifiersByEntityId;
186
	}
187
188
	/**
189
	 * Extracts the metadata array from the value of an rc_params field.
190
	 *
191
	 * @param array|string $rc_params
192
	 *
193
	 * @return array
194
	 */
195
	private function readRecentChangeParams( $rc_params ): array {
196
		if ( is_string( $rc_params ) ) {
197
			$rc_params = unserialize( $rc_params );
198
		}
199
200
		if ( is_array( $rc_params ) && array_key_exists( 'wikibase-repo-change', $rc_params ) ) {
201
			$metadata = $rc_params['wikibase-repo-change'];
202
		} else {
203
			$metadata = [];
204
		}
205
206
		$metadata = array_merge( [ 'rev_id' => 0 ], $metadata );
207
		return $metadata;
208
	}
209
210
}
211