1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace SMW\SQLStore; |
4
|
|
|
|
5
|
|
|
use SMW\DIWikiPage; |
6
|
|
|
use SMWDIBlob as DIBlob; |
7
|
|
|
use SMWRequestOptions as RequestOptions; |
8
|
|
|
use SMWStringCondition as StringCondition; |
9
|
|
|
|
10
|
|
|
/** |
11
|
|
|
* @license GNU GPL v2+ |
12
|
|
|
* @since 2.3 |
13
|
|
|
* |
14
|
|
|
* @author Markus Krötzsch |
15
|
|
|
* @author mwjames |
16
|
|
|
*/ |
17
|
|
|
class RequestOptionsProcessor { |
18
|
|
|
|
19
|
|
|
/** |
20
|
|
|
* @var SQLStore |
21
|
|
|
*/ |
22
|
|
|
private $store; |
23
|
|
|
|
24
|
|
|
/** |
25
|
|
|
* @since 2.3 |
26
|
|
|
* |
27
|
|
|
* @param SQLStore $store |
28
|
|
|
*/ |
29
|
14 |
|
public function __construct( SQLStore $store ) { |
30
|
14 |
|
$this->store = $store; |
31
|
14 |
|
} |
32
|
|
|
|
33
|
|
|
/** |
34
|
|
|
* Transform input parameters into a suitable array of SQL options. |
35
|
|
|
* The parameter $valuecol defines the string name of the column to which |
36
|
|
|
* sorting requests etc. are to be applied. |
37
|
|
|
* |
38
|
|
|
* @since 1.8 |
39
|
|
|
* |
40
|
|
|
* @param RequestOptions|null $requestOptions |
41
|
|
|
* @param string $valueCol |
42
|
|
|
* |
43
|
|
|
* @return array |
44
|
|
|
*/ |
45
|
214 |
|
public function getSQLOptionsFrom( RequestOptions $requestOptions = null, $valueCol = '' ) { |
46
|
214 |
|
$sqlConds = array(); |
47
|
|
|
|
48
|
214 |
|
if ( $requestOptions === null ) { |
49
|
209 |
|
return $sqlConds; |
50
|
|
|
} |
51
|
|
|
|
52
|
65 |
|
if ( $requestOptions->limit > 0 ) { |
53
|
60 |
|
$sqlConds['LIMIT'] = $requestOptions->limit; |
54
|
|
|
} |
55
|
|
|
|
56
|
65 |
|
if ( $requestOptions->offset > 0 ) { |
57
|
1 |
|
$sqlConds['OFFSET'] = $requestOptions->offset; |
58
|
|
|
} |
59
|
|
|
|
60
|
65 |
|
if ( ( $valueCol !== '' ) && ( $requestOptions->sort ) ) { |
61
|
10 |
|
$sqlConds['ORDER BY'] = $requestOptions->ascending ? $valueCol : $valueCol . ' DESC'; |
62
|
|
|
} |
63
|
|
|
|
64
|
65 |
|
return $sqlConds; |
65
|
|
|
} |
66
|
|
|
|
67
|
|
|
/** |
68
|
|
|
* Transform input parameters into a suitable string of additional SQL |
69
|
|
|
* conditions. The parameter $valuecol defines the string name of the |
70
|
|
|
* column to which value restrictions etc. are to be applied. |
71
|
|
|
* |
72
|
|
|
* @since 1.8 |
73
|
|
|
* |
74
|
|
|
* @param RequestOptions|null $requestOptions |
75
|
|
|
* @param string $valueCol name of SQL column to which conditions apply |
76
|
|
|
* @param string $labelCol name of SQL column to which string conditions apply, if any |
77
|
|
|
* @param boolean $addAnd indicate whether the string should begin with " AND " if non-empty |
78
|
|
|
* |
79
|
|
|
* @return string |
80
|
|
|
*/ |
81
|
181 |
|
public function getSQLConditionsFrom( RequestOptions $requestOptions = null, $valueCol = '', $labelCol = '', $addAnd = true ) { |
82
|
181 |
|
$sqlConds = ''; |
83
|
|
|
|
84
|
181 |
|
if ( $requestOptions === null ) { |
85
|
172 |
|
return $sqlConds; |
86
|
|
|
} |
87
|
|
|
|
88
|
67 |
|
$connection = $this->store->getConnection( 'mw.db' ); |
89
|
|
|
|
90
|
|
|
// Apply value boundary |
91
|
67 |
|
if ( ( $valueCol !== '' ) && ( $requestOptions->boundary !== null ) ) { |
92
|
|
|
|
93
|
4 |
|
if ( $requestOptions->ascending ) { |
94
|
4 |
|
$op = $requestOptions->include_boundary ? ' >= ' : ' > '; |
95
|
|
|
} else { |
96
|
|
|
$op = $requestOptions->include_boundary ? ' <= ' : ' < '; |
97
|
|
|
} |
98
|
|
|
|
99
|
4 |
|
$sqlConds .= ( $addAnd ? ' AND ' : '' ) . $valueCol . $op . $connection->addQuotes( $requestOptions->boundary ); |
100
|
|
|
} |
101
|
|
|
|
102
|
|
|
// Apply string conditions |
103
|
67 |
|
if ( $labelCol !== '' ) { |
104
|
65 |
|
foreach ( $requestOptions->getStringConditions() as $strcond ) { |
105
|
4 |
|
$string = str_replace( '_', '\_', $strcond->string ); |
106
|
4 |
|
$condition = 'LIKE'; |
107
|
|
|
|
108
|
4 |
|
switch ( $strcond->condition ) { |
109
|
4 |
|
case StringCondition::COND_PRE: $string .= '%'; |
|
|
|
|
110
|
2 |
|
break; |
111
|
3 |
|
case StringCondition::COND_POST: $string = '%' . $string; |
|
|
|
|
112
|
1 |
|
break; |
113
|
2 |
|
case StringCondition::COND_MID: $string = '%' . $string . '%'; |
|
|
|
|
114
|
1 |
|
break; |
115
|
1 |
|
case StringCondition::COND_EQ: $condition = '='; |
|
|
|
|
116
|
1 |
|
break; |
117
|
|
|
} |
118
|
|
|
|
119
|
4 |
|
$conditionOperator = $strcond->isDisjunctiveCondition ? ' OR ' : ' AND '; |
120
|
|
|
|
121
|
4 |
|
$sqlConds .= ( ( $addAnd || ( $sqlConds !== '' ) ) ? $conditionOperator : '' ) . "$labelCol $condition " . $connection->addQuotes( $string ); |
122
|
|
|
} |
123
|
|
|
} |
124
|
|
|
|
125
|
67 |
|
return $sqlConds; |
126
|
|
|
} |
127
|
|
|
|
128
|
|
|
/** |
129
|
|
|
* Not in all cases can requestoptions be forwarded to the DB using |
130
|
|
|
* getSQLConditions() and getSQLOptions(): some data comes from caches |
131
|
|
|
* that do not respect the options yet. This method takes an array of |
132
|
|
|
* results (SMWDataItem objects) *of the same type* and applies the |
133
|
|
|
* given requestoptions as appropriate. |
134
|
|
|
* |
135
|
|
|
* @since 1.8 |
136
|
|
|
* @param array $data array of SMWDataItem objects |
137
|
|
|
* @param SMWRequestOptions|null $requestoptions |
|
|
|
|
138
|
|
|
* |
139
|
|
|
* @return SMWDataItem[] |
140
|
|
|
*/ |
141
|
211 |
|
public function applyRequestOptionsTo( array $data, RequestOptions $requestOptions = null ) { |
142
|
|
|
|
143
|
211 |
|
if ( $data === array() || $requestOptions === null ) { |
144
|
204 |
|
return $data; |
145
|
|
|
} |
146
|
|
|
|
147
|
15 |
|
$result = array(); |
148
|
15 |
|
$sortres = array(); |
149
|
|
|
|
150
|
15 |
|
$sampleDataItem = reset( $data ); |
151
|
15 |
|
$isNumeric = is_numeric( $sampleDataItem->getSortKey() ); |
152
|
|
|
|
153
|
15 |
|
$i = 0; |
154
|
|
|
|
155
|
15 |
|
foreach ( $data as $item ) { |
156
|
|
|
|
157
|
15 |
|
list( $label, $value ) = $this->getSortKeyForItem( $item ); |
158
|
|
|
|
159
|
15 |
|
$keepDataValue = $this->applyBoundaryConditions( $requestOptions, $value, $isNumeric ); |
160
|
15 |
|
$keepDataValue = $this->applyStringConditions( $requestOptions, $label, $keepDataValue ); |
161
|
|
|
|
162
|
15 |
|
if ( $keepDataValue ) { |
163
|
14 |
|
$result[$i] = $item; |
164
|
14 |
|
$sortres[$i] = $value; |
165
|
15 |
|
$i++; |
166
|
|
|
} |
167
|
|
|
} |
168
|
|
|
|
169
|
15 |
|
$this->applySortRestriction( $requestOptions, $result, $sortres, $isNumeric ); |
170
|
15 |
|
$this->applyLimitRestriction( $requestOptions, $result ); |
171
|
|
|
|
172
|
15 |
|
return $result; |
173
|
|
|
} |
174
|
|
|
|
175
|
15 |
|
private function applyStringConditions( $requestOptions, $label, $keepDataValue ) { |
176
|
|
|
|
177
|
15 |
|
foreach ( $requestOptions->getStringConditions() as $strcond ) { // apply string conditions |
178
|
2 |
|
switch ( $strcond->condition ) { |
179
|
2 |
|
case StringCondition::STRCOND_PRE: |
180
|
1 |
|
$keepDataValue = $keepDataValue && ( strpos( $label, $strcond->string ) === 0 ); |
181
|
1 |
|
break; |
182
|
1 |
|
case StringCondition::STRCOND_POST: |
183
|
1 |
|
$keepDataValue = $keepDataValue && ( strpos( strrev( $label ), strrev( $strcond->string ) ) === 0 ); |
184
|
1 |
|
break; |
185
|
|
|
case StringCondition::STRCOND_MID: |
186
|
|
|
$keepDataValue = $keepDataValue && ( strpos( $label, $strcond->string ) !== false ); |
187
|
2 |
|
break; |
188
|
|
|
} |
189
|
|
|
} |
190
|
|
|
|
191
|
15 |
|
return $keepDataValue; |
192
|
|
|
} |
193
|
|
|
|
194
|
15 |
|
private function applyBoundaryConditions( $requestOptions, $value, $isNumeric ) { |
195
|
15 |
|
$keepDataValue = true; // keep datavalue only if this remains true |
196
|
|
|
|
197
|
15 |
|
if ( $requestOptions->boundary === null ) { |
198
|
14 |
|
return $keepDataValue; |
199
|
|
|
} |
200
|
|
|
|
201
|
|
|
// apply value boundary |
202
|
1 |
|
$strc = $isNumeric ? 0 : strcmp( $value, $requestOptions->boundary ); |
203
|
|
|
|
204
|
1 |
|
if ( $requestOptions->ascending ) { |
205
|
1 |
|
if ( $requestOptions->include_boundary ) { |
206
|
1 |
|
$keepDataValue = $isNumeric ? ( $value >= $requestOptions->boundary ) : ( $strc >= 0 ); |
207
|
|
|
} else { |
208
|
1 |
|
$keepDataValue = $isNumeric ? ( $value > $requestOptions->boundary ) : ( $strc > 0 ); |
209
|
|
|
} |
210
|
|
|
} else { |
211
|
|
|
if ( $requestOptions->include_boundary ) { |
212
|
|
|
$keepDataValue = $isNumeric ? ( $value <= $requestOptions->boundary ) : ( $strc <= 0 ); |
213
|
|
|
} else { |
214
|
|
|
$keepDataValue = $isNumeric ? ( $value < $requestOptions->boundary ) : ( $strc < 0 ); |
215
|
|
|
} |
216
|
|
|
} |
217
|
|
|
|
218
|
1 |
|
return $keepDataValue; |
219
|
|
|
} |
220
|
|
|
|
221
|
15 |
|
private function getSortKeyForItem( $item ) { |
222
|
|
|
|
223
|
15 |
|
if ( $item instanceof DIWikiPage ) { |
224
|
2 |
|
$label = $this->store->getWikiPageSortKey( $item ); |
225
|
2 |
|
$value = $label; |
226
|
|
|
} else { |
227
|
13 |
|
$label = ( $item instanceof DIBlob ) ? $item->getString() : ''; |
228
|
13 |
|
$value = $item->getSortKey(); |
229
|
|
|
} |
230
|
|
|
|
231
|
15 |
|
return array( $label, $value ); |
232
|
|
|
} |
233
|
|
|
|
234
|
15 |
|
private function applySortRestriction( $requestOptions, &$result, $sortres, $isNumeric ) { |
235
|
|
|
|
236
|
15 |
|
if ( !$requestOptions->sort ) { |
237
|
4 |
|
return null; |
238
|
|
|
} |
239
|
|
|
|
240
|
11 |
|
$flag = $isNumeric ? SORT_NUMERIC : SORT_LOCALE_STRING; |
241
|
|
|
|
242
|
11 |
|
if ( $requestOptions->ascending ) { |
243
|
8 |
|
asort( $sortres, $flag ); |
244
|
|
|
} else { |
245
|
5 |
|
arsort( $sortres, $flag ); |
246
|
|
|
} |
247
|
|
|
|
248
|
11 |
|
$newres = array(); |
249
|
|
|
|
250
|
11 |
|
foreach ( $sortres as $key => $value ) { |
251
|
11 |
|
$newres[] = $result[$key]; |
252
|
|
|
} |
253
|
|
|
|
254
|
11 |
|
$result = $newres; |
255
|
11 |
|
} |
256
|
|
|
|
257
|
15 |
|
private function applyLimitRestriction( $requestOptions, &$result ) { |
258
|
|
|
|
259
|
15 |
|
if ( $requestOptions->limit > 0 ) { |
260
|
3 |
|
return $result = array_slice( $result, $requestOptions->offset, $requestOptions->limit ); |
261
|
|
|
} |
262
|
|
|
|
263
|
13 |
|
$result = array_slice( $result, $requestOptions->offset ); |
264
|
13 |
|
} |
265
|
|
|
|
266
|
|
|
} |
267
|
|
|
|
According to the PSR-2, the body of a case statement must start on the line immediately following the case statement.
}
To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.