|
1
|
|
|
<?php |
|
2
|
|
|
/** |
|
3
|
|
|
* An RFX object contains the parsed information for an RFX |
|
4
|
|
|
*/ |
|
5
|
|
|
|
|
6
|
|
|
namespace Xtools; |
|
7
|
|
|
|
|
8
|
|
|
/** |
|
9
|
|
|
* This class contains information about a single RfX page. |
|
10
|
|
|
*/ |
|
11
|
|
|
class RFX |
|
12
|
|
|
{ |
|
13
|
|
|
/** |
|
14
|
|
|
* @var array Data we parsed out of the page text |
|
15
|
|
|
*/ |
|
16
|
|
|
private $data; |
|
17
|
|
|
|
|
18
|
|
|
/** |
|
19
|
|
|
* @var array Duplicate voters |
|
20
|
|
|
*/ |
|
21
|
|
|
private $duplicates; |
|
22
|
|
|
|
|
23
|
|
|
/** |
|
24
|
|
|
* @var null|string Username of the user we're looking for. |
|
25
|
|
|
*/ |
|
26
|
|
|
private $userLookingFor; |
|
27
|
|
|
|
|
28
|
|
|
/** |
|
29
|
|
|
* @var string Section we found the user we're looking for |
|
30
|
|
|
*/ |
|
31
|
|
|
private $userSectionFound; |
|
32
|
|
|
|
|
33
|
|
|
/** |
|
34
|
|
|
* @var string Ending date of the RFX |
|
35
|
|
|
*/ |
|
36
|
|
|
private $endDate; |
|
37
|
|
|
|
|
38
|
|
|
/** |
|
39
|
|
|
* Attempts to find a signature in $input using the default regex. |
|
40
|
|
|
* Returns matches. |
|
41
|
|
|
* |
|
42
|
|
|
* @param string $input The line we're looking for |
|
43
|
|
|
* @param array $matches Pointer to an array where we stash results |
|
44
|
|
|
* |
|
45
|
|
|
* @TODO: Make this cleaner |
|
46
|
|
|
* |
|
47
|
|
|
* @return int |
|
48
|
|
|
*/ |
|
49
|
|
|
protected function findSig($input, &$matches) |
|
50
|
|
|
{ |
|
51
|
|
|
//Supports User: and User talk: wikilinks, {{fullurl}}, |
|
|
|
|
|
|
52
|
|
|
// unsubsted {{unsigned}}, unsubsted {{unsigned2}}, |
|
|
|
|
|
|
53
|
|
|
// anything that looks like a custom sig template |
|
54
|
|
|
// TODO: Cross-wiki this sucker |
|
55
|
|
|
$regexp |
|
56
|
|
|
= //1: Normal [[User:XX]] and [[User talk:XX]] |
|
|
|
|
|
|
57
|
|
|
"/\[\[[Uu]ser(?:[\s_][Tt]alk)?\:([^\]\|\/]*)(?:\|[^\]]*)?\]\]" |
|
58
|
|
|
//2: {{fullurl}} and {{unsigned}} templates |
|
|
|
|
|
|
59
|
|
|
. "|\{\{(?:[Ff]ullurl\:[Uu]ser(?:[\s_][Tt]alk)?\:|" |
|
60
|
|
|
. "[Uu]nsigned\|)([^\}\|]*)(?:|[\|\}]*)?\}\}" |
|
61
|
|
|
//3: {{User:XX/sig}} templates |
|
|
|
|
|
|
62
|
|
|
. "|(?:\{\{)[Uu]ser(?:[\s_][Tt]alk)?\:([^\}\/\|]*)" |
|
63
|
|
|
//4: {{unsigned2|Date|XX}} templates |
|
|
|
|
|
|
64
|
|
|
. "|\{\{[Uu]nsigned2\|[^\|]*\|([^\}]*)\}\}" |
|
65
|
|
|
//5: [[User:XX/sig]] links (compromise measure) |
|
|
|
|
|
|
66
|
|
|
. "|(?:\[\[)[Uu]ser\:([^\]\/\|]*)\/[Ss]ig[\|\]]/"; |
|
67
|
|
|
|
|
68
|
|
|
return preg_match_all( |
|
69
|
|
|
$regexp, |
|
70
|
|
|
$input, |
|
71
|
|
|
$matches, |
|
72
|
|
|
PREG_OFFSET_CAPTURE |
|
73
|
|
|
); |
|
74
|
|
|
} |
|
75
|
|
|
|
|
76
|
|
|
/** |
|
77
|
|
|
* This function parses the wikitext and stores it within this function. |
|
78
|
|
|
* It's been split out to make this class testable |
|
79
|
|
|
* |
|
80
|
|
|
* @param array $sectionArray Section names that we're looking for |
|
81
|
|
|
* @param string $rawWikiText The text of the page we're parsing |
|
82
|
|
|
* @param string $dateRegexp Valid Regular Expression for the end date |
|
83
|
|
|
* |
|
84
|
|
|
* @return null |
|
85
|
|
|
*/ |
|
86
|
|
|
private function setUp($sectionArray, $rawWikiText, $dateRegexp) |
|
87
|
|
|
{ |
|
88
|
|
|
$this->data = array(); |
|
89
|
|
|
|
|
90
|
|
|
$lines = explode("\n", $rawWikiText); |
|
91
|
|
|
|
|
92
|
|
|
$keys = join("|", $sectionArray); |
|
93
|
|
|
|
|
94
|
|
|
$lastSection = ""; |
|
95
|
|
|
|
|
96
|
|
|
foreach ($lines as $line) { |
|
97
|
|
|
if (preg_match("/={1,6}\s?($keys)\s?={1,6}/i", $line, $matches)) { |
|
98
|
|
|
$lastSection = strtolower($matches[1]); |
|
99
|
|
|
} elseif ($lastSection == "" |
|
100
|
|
|
&& preg_match( |
|
101
|
|
|
"/$dateRegexp/i", |
|
102
|
|
|
$line, |
|
103
|
|
|
$matches |
|
104
|
|
|
) |
|
105
|
|
|
) { |
|
106
|
|
|
$this->endDate = $matches[1]; |
|
107
|
|
|
} elseif ($lastSection != "" |
|
108
|
|
|
&& preg_match("/^\s*#?:.*/i", $line) === 0 |
|
109
|
|
|
) { |
|
110
|
|
|
$this->findSig($line, $matches); |
|
111
|
|
|
if (!isset($matches[1][0])) { |
|
112
|
|
|
continue; |
|
113
|
|
|
} |
|
114
|
|
|
$foundUser = trim($matches[1][0][0]); |
|
115
|
|
|
$this->data[$lastSection][] = $foundUser; |
|
116
|
|
|
if (strtolower($foundUser) == strtolower($this->userLookingFor)) { |
|
117
|
|
|
$this->userSectionFound = $lastSection; |
|
118
|
|
|
} |
|
119
|
|
|
} |
|
120
|
|
|
} |
|
121
|
|
|
|
|
122
|
|
|
$final = []; // initialize the final array |
|
123
|
|
|
$finalRaw = []; // Initialize the raw data array |
|
124
|
|
|
|
|
125
|
|
|
foreach ($this->data as $key => $value) { |
|
126
|
|
|
$finalRaw = array_merge($finalRaw, $this->data[$key]); |
|
127
|
|
|
} |
|
128
|
|
|
|
|
129
|
|
|
foreach ($finalRaw as $foundUsername) { |
|
130
|
|
|
$final[] = $foundUsername; // group all array's elements |
|
131
|
|
|
} |
|
132
|
|
|
|
|
133
|
|
|
$final = array_count_values($final); // find repetition and its count |
|
134
|
|
|
|
|
135
|
|
|
$final = array_diff($final, [1]); // remove single occurrences |
|
136
|
|
|
|
|
137
|
|
|
$this->duplicates = array_keys($final); |
|
138
|
|
|
} |
|
139
|
|
|
|
|
140
|
|
|
/** |
|
141
|
|
|
* RFX constructor. |
|
142
|
|
|
* |
|
143
|
|
|
* @param string $rawWikiText The text of the page we're parsing |
|
144
|
|
|
* @param array $sectionArray Section names that we're looking for |
|
145
|
|
|
* @param string $userNamespace Plain text of the user namespace |
|
146
|
|
|
* @param string $dateRegexp Valid Regular Expression for the end date |
|
147
|
|
|
* @param string|null $userLookingFor User we're trying to find. |
|
148
|
|
|
*/ |
|
149
|
|
|
public function __construct( |
|
150
|
|
|
$rawWikiText, |
|
151
|
|
|
$sectionArray = ["Support", "Oppose", "Neutral"], |
|
152
|
|
|
$userNamespace = "User", |
|
|
|
|
|
|
153
|
|
|
$dateRegexp = "final .*end(?:ing|ed)?(?: no earlier than)? (.*?)? \(UTC\)", |
|
154
|
|
|
$userLookingFor = null |
|
155
|
|
|
) { |
|
156
|
|
|
$this->userLookingFor = $userLookingFor; |
|
157
|
|
|
|
|
158
|
|
|
$this->setUp($sectionArray, $rawWikiText, $dateRegexp); |
|
159
|
|
|
} |
|
160
|
|
|
|
|
161
|
|
|
/** |
|
162
|
|
|
* Which section we found the user we're looking for. |
|
163
|
|
|
* |
|
164
|
|
|
* @return string |
|
165
|
|
|
*/ |
|
166
|
|
|
public function getUserSectionFound() |
|
167
|
|
|
{ |
|
168
|
|
|
return $this->userSectionFound; |
|
169
|
|
|
} |
|
170
|
|
|
|
|
171
|
|
|
/** |
|
172
|
|
|
* Returns data on the given section name. |
|
173
|
|
|
* |
|
174
|
|
|
* @param string $sectionName The section we're looking at |
|
175
|
|
|
* |
|
176
|
|
|
* @return array |
|
177
|
|
|
*/ |
|
178
|
|
|
public function getSection($sectionName) |
|
179
|
|
|
{ |
|
180
|
|
|
$sectionName = strtolower($sectionName); |
|
181
|
|
|
if (!isset($this->data[$sectionName])) { |
|
182
|
|
|
return []; |
|
183
|
|
|
} else { |
|
184
|
|
|
return $this->data[$sectionName]; |
|
185
|
|
|
} |
|
186
|
|
|
} |
|
187
|
|
|
|
|
188
|
|
|
/** |
|
189
|
|
|
* Get an array of duplicate votes. |
|
190
|
|
|
* |
|
191
|
|
|
* @return array |
|
192
|
|
|
*/ |
|
193
|
|
|
public function getDuplicates() |
|
194
|
|
|
{ |
|
195
|
|
|
return $this->duplicates; |
|
196
|
|
|
} |
|
197
|
|
|
|
|
198
|
|
|
/** |
|
199
|
|
|
* Get the End Date of the RFX |
|
200
|
|
|
* |
|
201
|
|
|
* @return string |
|
202
|
|
|
*/ |
|
203
|
|
|
public function getEndDate() |
|
204
|
|
|
{ |
|
205
|
|
|
return $this->endDate; |
|
206
|
|
|
} |
|
207
|
|
|
} |
|
208
|
|
|
|
Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.
The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.
This check looks for comments that seem to be mostly valid code and reports them.