These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | /** |
||
3 | * Page history |
||
4 | * |
||
5 | * Split off from Article.php and Skin.php, 2003-12-22 |
||
6 | * |
||
7 | * This program is free software; you can redistribute it and/or modify |
||
8 | * it under the terms of the GNU General Public License as published by |
||
9 | * the Free Software Foundation; either version 2 of the License, or |
||
10 | * (at your option) any later version. |
||
11 | * |
||
12 | * This program is distributed in the hope that it will be useful, |
||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||
15 | * GNU General Public License for more details. |
||
16 | * |
||
17 | * You should have received a copy of the GNU General Public License along |
||
18 | * with this program; if not, write to the Free Software Foundation, Inc., |
||
19 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
||
20 | * http://www.gnu.org/copyleft/gpl.html |
||
21 | * |
||
22 | * @file |
||
23 | * @ingroup Actions |
||
24 | */ |
||
25 | |||
26 | /** |
||
27 | * This class handles printing the history page for an article. In order to |
||
28 | * be efficient, it uses timestamps rather than offsets for paging, to avoid |
||
29 | * costly LIMIT,offset queries. |
||
30 | * |
||
31 | * Construct it by passing in an Article, and call $h->history() to print the |
||
32 | * history. |
||
33 | * |
||
34 | * @ingroup Actions |
||
35 | */ |
||
36 | class HistoryAction extends FormlessAction { |
||
37 | const DIR_PREV = 0; |
||
38 | const DIR_NEXT = 1; |
||
39 | |||
40 | /** @var array Array of message keys and strings */ |
||
41 | public $message; |
||
42 | |||
43 | public function getName() { |
||
44 | return 'history'; |
||
45 | } |
||
46 | |||
47 | public function requiresWrite() { |
||
48 | return false; |
||
49 | } |
||
50 | |||
51 | public function requiresUnblock() { |
||
52 | return false; |
||
53 | } |
||
54 | |||
55 | protected function getPageTitle() { |
||
56 | return $this->msg( 'history-title', $this->getTitle()->getPrefixedText() )->text(); |
||
57 | } |
||
58 | |||
59 | protected function getDescription() { |
||
60 | // Creation of a subtitle link pointing to [[Special:Log]] |
||
61 | return Linker::linkKnown( |
||
62 | SpecialPage::getTitleFor( 'Log' ), |
||
63 | $this->msg( 'viewpagelogs' )->escaped(), |
||
64 | [], |
||
65 | [ 'page' => $this->getTitle()->getPrefixedText() ] |
||
66 | ); |
||
67 | } |
||
68 | |||
69 | /** |
||
70 | * @return WikiPage|Article|ImagePage|CategoryPage|Page The Article object we are working on. |
||
71 | */ |
||
72 | public function getArticle() { |
||
73 | return $this->page; |
||
74 | } |
||
75 | |||
76 | /** |
||
77 | * As we use the same small set of messages in various methods and that |
||
78 | * they are called often, we call them once and save them in $this->message |
||
79 | */ |
||
80 | private function preCacheMessages() { |
||
81 | // Precache various messages |
||
82 | if ( !isset( $this->message ) ) { |
||
83 | $msgs = [ 'cur', 'last', 'pipe-separator' ]; |
||
84 | foreach ( $msgs as $msg ) { |
||
85 | $this->message[$msg] = $this->msg( $msg )->escaped(); |
||
86 | } |
||
87 | } |
||
88 | } |
||
89 | |||
90 | /** |
||
91 | * Print the history page for an article. |
||
92 | */ |
||
93 | function onView() { |
||
94 | $out = $this->getOutput(); |
||
95 | $request = $this->getRequest(); |
||
96 | |||
97 | /** |
||
98 | * Allow client caching. |
||
99 | */ |
||
100 | if ( $out->checkLastModified( $this->page->getTouched() ) ) { |
||
0 ignored issues
–
show
|
|||
101 | return; // Client cache fresh and headers sent, nothing more to do. |
||
102 | } |
||
103 | |||
104 | $this->preCacheMessages(); |
||
105 | $config = $this->context->getConfig(); |
||
106 | |||
107 | # Fill in the file cache if not set already |
||
108 | if ( HTMLFileCache::useFileCache( $this->getContext() ) ) { |
||
109 | $cache = new HTMLFileCache( $this->getTitle(), 'history' ); |
||
110 | if ( !$cache->isCacheGood( /* Assume up to date */ ) ) { |
||
111 | ob_start( [ &$cache, 'saveToFileCache' ] ); |
||
112 | } |
||
113 | } |
||
114 | |||
115 | // Setup page variables. |
||
116 | $out->setFeedAppendQuery( 'action=history' ); |
||
117 | $out->addModules( 'mediawiki.action.history' ); |
||
118 | $out->addModuleStyles( [ |
||
119 | 'mediawiki.action.history.styles', |
||
120 | 'mediawiki.special.changeslist', |
||
121 | ] ); |
||
122 | if ( $config->get( 'UseMediaWikiUIEverywhere' ) ) { |
||
123 | $out = $this->getOutput(); |
||
124 | $out->addModuleStyles( [ |
||
125 | 'mediawiki.ui.input', |
||
126 | 'mediawiki.ui.checkbox', |
||
127 | ] ); |
||
128 | } |
||
129 | |||
130 | // Handle atom/RSS feeds. |
||
131 | $feedType = $request->getVal( 'feed' ); |
||
132 | if ( $feedType ) { |
||
0 ignored issues
–
show
The expression
$feedType of type null|string is loosely compared to true ; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.
In PHP, under loose comparison (like For '' == false // true
'' == null // true
'ab' == false // false
'ab' == null // false
// It is often better to use strict comparison
'' === false // false
'' === null // false
Loading history...
|
|||
133 | $this->feed( $feedType ); |
||
134 | |||
135 | return; |
||
136 | } |
||
137 | |||
138 | $this->addHelpLink( '//meta.wikimedia.org/wiki/Special:MyLanguage/Help:Page_history', true ); |
||
139 | |||
140 | // Fail nicely if article doesn't exist. |
||
141 | if ( !$this->page->exists() ) { |
||
0 ignored issues
–
show
The method
exists does only exist in Article and CategoryPage... ImagePage and WikiPage , but not in Page .
It seems like the method you are trying to call exists only in some of the possible types. Let’s take a look at an example: class A
{
public function foo() { }
}
class B extends A
{
public function bar() { }
}
/**
* @param A|B $x
*/
function someFunction($x)
{
$x->foo(); // This call is fine as the method exists in A and B.
$x->bar(); // This method only exists in B and might cause an error.
}
Available Fixes
Loading history...
|
|||
142 | global $wgSend404Code; |
||
143 | if ( $wgSend404Code ) { |
||
144 | $out->setStatusCode( 404 ); |
||
145 | } |
||
146 | $out->addWikiMsg( 'nohistory' ); |
||
147 | # show deletion/move log if there is an entry |
||
148 | LogEventsList::showLogExtract( |
||
149 | $out, |
||
150 | [ 'delete', 'move' ], |
||
151 | $this->getTitle(), |
||
152 | '', |
||
153 | [ 'lim' => 10, |
||
154 | 'conds' => [ "log_action != 'revision'" ], |
||
155 | 'showIfEmpty' => false, |
||
156 | 'msgKey' => [ 'moveddeleted-notice' ] |
||
157 | ] |
||
158 | ); |
||
159 | |||
160 | return; |
||
161 | } |
||
162 | |||
163 | /** |
||
164 | * Add date selector to quickly get to a certain time |
||
165 | */ |
||
166 | $year = $request->getInt( 'year' ); |
||
167 | $month = $request->getInt( 'month' ); |
||
168 | $tagFilter = $request->getVal( 'tagfilter' ); |
||
169 | $tagSelector = ChangeTags::buildTagFilterSelector( $tagFilter ); |
||
170 | |||
171 | /** |
||
172 | * Option to show only revisions that have been (partially) hidden via RevisionDelete |
||
173 | */ |
||
174 | if ( $request->getBool( 'deleted' ) ) { |
||
175 | $conds = [ 'rev_deleted != 0' ]; |
||
176 | } else { |
||
177 | $conds = []; |
||
178 | } |
||
179 | if ( $this->getUser()->isAllowed( 'deletedhistory' ) ) { |
||
180 | $checkDeleted = Xml::checkLabel( $this->msg( 'history-show-deleted' )->text(), |
||
181 | 'deleted', 'mw-show-deleted-only', $request->getBool( 'deleted' ) ) . "\n"; |
||
182 | } else { |
||
183 | $checkDeleted = ''; |
||
184 | } |
||
185 | |||
186 | // Add the general form |
||
187 | $action = htmlspecialchars( wfScript() ); |
||
188 | $out->addHTML( |
||
189 | "<form action=\"$action\" method=\"get\" id=\"mw-history-searchform\">" . |
||
190 | Xml::fieldset( |
||
191 | $this->msg( 'history-fieldset-title' )->text(), |
||
192 | false, |
||
193 | [ 'id' => 'mw-history-search' ] |
||
194 | ) . |
||
195 | Html::hidden( 'title', $this->getTitle()->getPrefixedDBkey() ) . "\n" . |
||
196 | Html::hidden( 'action', 'history' ) . "\n" . |
||
197 | Xml::dateMenu( |
||
198 | ( $year == null ? MWTimestamp::getLocalInstance()->format( 'Y' ) : $year ), |
||
199 | $month |
||
200 | ) . ' ' . |
||
201 | ( $tagSelector ? ( implode( ' ', $tagSelector ) . ' ' ) : '' ) . |
||
202 | $checkDeleted . |
||
203 | Html::submitButton( |
||
204 | $this->msg( 'historyaction-submit' )->text(), |
||
205 | [], |
||
206 | [ 'mw-ui-progressive' ] |
||
207 | ) . "\n" . |
||
208 | '</fieldset></form>' |
||
209 | ); |
||
210 | |||
211 | Hooks::run( 'PageHistoryBeforeList', [ &$this->page, $this->getContext() ] ); |
||
212 | |||
213 | // Create and output the list. |
||
214 | $pager = new HistoryPager( $this, $year, $month, $tagFilter, $conds ); |
||
215 | $out->addHTML( |
||
216 | $pager->getNavigationBar() . |
||
217 | $pager->getBody() . |
||
218 | $pager->getNavigationBar() |
||
219 | ); |
||
220 | $out->preventClickjacking( $pager->getPreventClickjacking() ); |
||
221 | } |
||
222 | |||
223 | /** |
||
224 | * Fetch an array of revisions, specified by a given limit, offset and |
||
225 | * direction. This is now only used by the feeds. It was previously |
||
226 | * used by the main UI but that's now handled by the pager. |
||
227 | * |
||
228 | * @param int $limit The limit number of revisions to get |
||
229 | * @param int $offset |
||
230 | * @param int $direction Either self::DIR_PREV or self::DIR_NEXT |
||
231 | * @return ResultWrapper |
||
232 | */ |
||
233 | function fetchRevisions( $limit, $offset, $direction ) { |
||
234 | // Fail if article doesn't exist. |
||
235 | if ( !$this->getTitle()->exists() ) { |
||
236 | return new FakeResultWrapper( [] ); |
||
237 | } |
||
238 | |||
239 | $dbr = wfGetDB( DB_REPLICA ); |
||
240 | |||
241 | if ( $direction === self::DIR_PREV ) { |
||
242 | list( $dirs, $oper ) = [ "ASC", ">=" ]; |
||
243 | } else { /* $direction === self::DIR_NEXT */ |
||
244 | list( $dirs, $oper ) = [ "DESC", "<=" ]; |
||
245 | } |
||
246 | |||
247 | if ( $offset ) { |
||
248 | $offsets = [ "rev_timestamp $oper " . $dbr->addQuotes( $dbr->timestamp( $offset ) ) ]; |
||
249 | } else { |
||
250 | $offsets = []; |
||
251 | } |
||
252 | |||
253 | $page_id = $this->page->getId(); |
||
0 ignored issues
–
show
The method
getId does only exist in Article and CategoryPage... ImagePage and WikiPage , but not in Page .
It seems like the method you are trying to call exists only in some of the possible types. Let’s take a look at an example: class A
{
public function foo() { }
}
class B extends A
{
public function bar() { }
}
/**
* @param A|B $x
*/
function someFunction($x)
{
$x->foo(); // This call is fine as the method exists in A and B.
$x->bar(); // This method only exists in B and might cause an error.
}
Available Fixes
Loading history...
|
|||
254 | |||
255 | return $dbr->select( 'revision', |
||
256 | Revision::selectFields(), |
||
257 | array_merge( [ 'rev_page' => $page_id ], $offsets ), |
||
258 | __METHOD__, |
||
259 | [ 'ORDER BY' => "rev_timestamp $dirs", |
||
260 | 'USE INDEX' => 'page_timestamp', 'LIMIT' => $limit ] |
||
261 | ); |
||
262 | } |
||
263 | |||
264 | /** |
||
265 | * Output a subscription feed listing recent edits to this page. |
||
266 | * |
||
267 | * @param string $type Feed type |
||
268 | */ |
||
269 | function feed( $type ) { |
||
270 | if ( !FeedUtils::checkFeedOutput( $type ) ) { |
||
271 | return; |
||
272 | } |
||
273 | $request = $this->getRequest(); |
||
274 | |||
275 | $feedClasses = $this->context->getConfig()->get( 'FeedClasses' ); |
||
276 | /** @var RSSFeed|AtomFeed $feed */ |
||
277 | $feed = new $feedClasses[$type]( |
||
278 | $this->getTitle()->getPrefixedText() . ' - ' . |
||
279 | $this->msg( 'history-feed-title' )->inContentLanguage()->text(), |
||
280 | $this->msg( 'history-feed-description' )->inContentLanguage()->text(), |
||
281 | $this->getTitle()->getFullURL( 'action=history' ) |
||
282 | ); |
||
283 | |||
284 | // Get a limit on number of feed entries. Provide a sane default |
||
285 | // of 10 if none is defined (but limit to $wgFeedLimit max) |
||
286 | $limit = $request->getInt( 'limit', 10 ); |
||
287 | $limit = min( |
||
288 | max( $limit, 1 ), |
||
289 | $this->context->getConfig()->get( 'FeedLimit' ) |
||
290 | ); |
||
291 | |||
292 | $items = $this->fetchRevisions( $limit, 0, self::DIR_NEXT ); |
||
293 | |||
294 | // Generate feed elements enclosed between header and footer. |
||
295 | $feed->outHeader(); |
||
296 | if ( $items->numRows() ) { |
||
297 | foreach ( $items as $row ) { |
||
298 | $feed->outItem( $this->feedItem( $row ) ); |
||
0 ignored issues
–
show
It seems like
$row defined by $row on line 297 can be null ; however, HistoryAction::feedItem() does not accept null , maybe add an additional type check?
Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code: /** @return stdClass|null */
function mayReturnNull() { }
function doesNotAcceptNull(stdClass $x) { }
// With potential error.
function withoutCheck() {
$x = mayReturnNull();
doesNotAcceptNull($x); // Potential error here.
}
// Safe - Alternative 1
function withCheck1() {
$x = mayReturnNull();
if ( ! $x instanceof stdClass) {
throw new \LogicException('$x must be defined.');
}
doesNotAcceptNull($x);
}
// Safe - Alternative 2
function withCheck2() {
$x = mayReturnNull();
if ($x instanceof stdClass) {
doesNotAcceptNull($x);
}
}
Loading history...
|
|||
299 | } |
||
300 | } else { |
||
301 | $feed->outItem( $this->feedEmpty() ); |
||
302 | } |
||
303 | $feed->outFooter(); |
||
304 | } |
||
305 | |||
306 | function feedEmpty() { |
||
307 | return new FeedItem( |
||
308 | $this->msg( 'nohistory' )->inContentLanguage()->text(), |
||
309 | $this->msg( 'history-feed-empty' )->inContentLanguage()->parseAsBlock(), |
||
310 | $this->getTitle()->getFullURL(), |
||
311 | wfTimestamp( TS_MW ), |
||
0 ignored issues
–
show
|
|||
312 | '', |
||
313 | $this->getTitle()->getTalkPage()->getFullURL() |
||
314 | ); |
||
315 | } |
||
316 | |||
317 | /** |
||
318 | * Generate a FeedItem object from a given revision table row |
||
319 | * Borrows Recent Changes' feed generation functions for formatting; |
||
320 | * includes a diff to the previous revision (if any). |
||
321 | * |
||
322 | * @param stdClass|array $row Database row |
||
323 | * @return FeedItem |
||
324 | */ |
||
325 | function feedItem( $row ) { |
||
326 | $rev = new Revision( $row ); |
||
327 | $rev->setTitle( $this->getTitle() ); |
||
328 | $text = FeedUtils::formatDiffRow( |
||
329 | $this->getTitle(), |
||
330 | $this->getTitle()->getPreviousRevisionID( $rev->getId() ), |
||
0 ignored issues
–
show
|
|||
331 | $rev->getId(), |
||
332 | $rev->getTimestamp(), |
||
333 | $rev->getComment() |
||
334 | ); |
||
335 | if ( $rev->getComment() == '' ) { |
||
336 | global $wgContLang; |
||
337 | $title = $this->msg( 'history-feed-item-nocomment', |
||
338 | $rev->getUserText(), |
||
339 | $wgContLang->timeanddate( $rev->getTimestamp() ), |
||
340 | $wgContLang->date( $rev->getTimestamp() ), |
||
341 | $wgContLang->time( $rev->getTimestamp() ) )->inContentLanguage()->text(); |
||
342 | } else { |
||
343 | $title = $rev->getUserText() . |
||
344 | $this->msg( 'colon-separator' )->inContentLanguage()->text() . |
||
345 | FeedItem::stripComment( $rev->getComment() ); |
||
346 | } |
||
347 | |||
348 | return new FeedItem( |
||
349 | $title, |
||
350 | $text, |
||
351 | $this->getTitle()->getFullURL( 'diff=' . $rev->getId() . '&oldid=prev' ), |
||
352 | $rev->getTimestamp(), |
||
0 ignored issues
–
show
|
|||
353 | $rev->getUserText(), |
||
0 ignored issues
–
show
It seems like
$rev->getUserText() targeting Revision::getUserText() can also be of type boolean ; however, FeedItem::__construct() does only seem to accept string , maybe add an additional type check?
This check looks at variables that are passed out again to other methods. If the outgoing method call has stricter type requirements than the method itself, an issue is raised. An additional type check may prevent trouble.
Loading history...
|
|||
354 | $this->getTitle()->getTalkPage()->getFullURL() |
||
355 | ); |
||
356 | } |
||
357 | } |
||
358 | |||
359 | /** |
||
360 | * @ingroup Pager |
||
361 | * @ingroup Actions |
||
362 | */ |
||
363 | class HistoryPager extends ReverseChronologicalPager { |
||
364 | /** |
||
365 | * @var bool|stdClass |
||
366 | */ |
||
367 | public $lastRow = false; |
||
368 | |||
369 | public $counter, $historyPage, $buttons, $conds; |
||
0 ignored issues
–
show
|
|||
370 | |||
371 | protected $oldIdChecked; |
||
372 | |||
373 | protected $preventClickjacking = false; |
||
374 | /** |
||
375 | * @var array |
||
376 | */ |
||
377 | protected $parentLens; |
||
378 | |||
379 | /** @var bool Whether to show the tag editing UI */ |
||
380 | protected $showTagEditUI; |
||
381 | |||
382 | /** |
||
383 | * @param HistoryAction $historyPage |
||
384 | * @param string $year |
||
385 | * @param string $month |
||
386 | * @param string $tagFilter |
||
387 | * @param array $conds |
||
388 | */ |
||
389 | function __construct( $historyPage, $year = '', $month = '', $tagFilter = '', $conds = [] ) { |
||
390 | parent::__construct( $historyPage->getContext() ); |
||
391 | $this->historyPage = $historyPage; |
||
392 | $this->tagFilter = $tagFilter; |
||
0 ignored issues
–
show
The property
tagFilter does not exist. Did you maybe forget to declare it?
In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code: class MyClass { }
$x = new MyClass();
$x->foo = true;
Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion: class MyClass {
public $foo;
}
$x = new MyClass();
$x->foo = true;
Loading history...
|
|||
393 | $this->getDateCond( $year, $month ); |
||
394 | $this->conds = $conds; |
||
395 | $this->showTagEditUI = ChangeTags::showTagEditingUI( $this->getUser() ); |
||
396 | } |
||
397 | |||
398 | // For hook compatibility... |
||
399 | function getArticle() { |
||
400 | return $this->historyPage->getArticle(); |
||
401 | } |
||
402 | |||
403 | function getSqlComment() { |
||
404 | if ( $this->conds ) { |
||
405 | return 'history page filtered'; // potentially slow, see CR r58153 |
||
406 | } else { |
||
407 | return 'history page unfiltered'; |
||
408 | } |
||
409 | } |
||
410 | |||
411 | function getQueryInfo() { |
||
412 | $queryInfo = [ |
||
413 | 'tables' => [ 'revision', 'user' ], |
||
414 | 'fields' => array_merge( Revision::selectFields(), Revision::selectUserFields() ), |
||
415 | 'conds' => array_merge( |
||
416 | [ 'rev_page' => $this->getWikiPage()->getId() ], |
||
417 | $this->conds ), |
||
418 | 'options' => [ 'USE INDEX' => [ 'revision' => 'page_timestamp' ] ], |
||
419 | 'join_conds' => [ 'user' => Revision::userJoinCond() ], |
||
420 | ]; |
||
421 | ChangeTags::modifyDisplayQuery( |
||
422 | $queryInfo['tables'], |
||
423 | $queryInfo['fields'], |
||
424 | $queryInfo['conds'], |
||
425 | $queryInfo['join_conds'], |
||
426 | $queryInfo['options'], |
||
427 | $this->tagFilter |
||
428 | ); |
||
429 | Hooks::run( 'PageHistoryPager::getQueryInfo', [ &$this, &$queryInfo ] ); |
||
430 | |||
431 | return $queryInfo; |
||
432 | } |
||
433 | |||
434 | function getIndexField() { |
||
435 | return 'rev_timestamp'; |
||
436 | } |
||
437 | |||
438 | /** |
||
439 | * @param stdClass $row |
||
440 | * @return string |
||
441 | */ |
||
442 | function formatRow( $row ) { |
||
443 | if ( $this->lastRow ) { |
||
444 | $latest = ( $this->counter == 1 && $this->mIsFirst ); |
||
445 | $firstInList = $this->counter == 1; |
||
446 | $this->counter++; |
||
447 | |||
448 | $notifTimestamp = $this->getConfig()->get( 'ShowUpdatedMarker' ) |
||
449 | ? $this->getTitle()->getNotificationTimestamp( $this->getUser() ) |
||
450 | : false; |
||
451 | |||
452 | $s = $this->historyLine( |
||
453 | $this->lastRow, $row, $notifTimestamp, $latest, $firstInList ); |
||
0 ignored issues
–
show
It seems like
$this->lastRow can also be of type boolean ; however, HistoryPager::historyLine() does only seem to accept object<stdClass> , maybe add an additional type check?
If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check: /**
* @return array|string
*/
function returnsDifferentValues($x) {
if ($x) {
return 'foo';
}
return array();
}
$x = returnsDifferentValues($y);
if (is_array($x)) {
// $x is an array.
}
If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.
Loading history...
|
|||
454 | } else { |
||
455 | $s = ''; |
||
456 | } |
||
457 | $this->lastRow = $row; |
||
458 | |||
459 | return $s; |
||
460 | } |
||
461 | |||
462 | function doBatchLookups() { |
||
463 | if ( !Hooks::run( 'PageHistoryPager::doBatchLookups', [ $this, $this->mResult ] ) ) { |
||
464 | return; |
||
465 | } |
||
466 | |||
467 | # Do a link batch query |
||
468 | $this->mResult->seek( 0 ); |
||
469 | $batch = new LinkBatch(); |
||
470 | $revIds = []; |
||
471 | foreach ( $this->mResult as $row ) { |
||
472 | if ( $row->rev_parent_id ) { |
||
473 | $revIds[] = $row->rev_parent_id; |
||
474 | } |
||
475 | if ( !is_null( $row->user_name ) ) { |
||
476 | $batch->add( NS_USER, $row->user_name ); |
||
477 | $batch->add( NS_USER_TALK, $row->user_name ); |
||
478 | } else { # for anons or usernames of imported revisions |
||
479 | $batch->add( NS_USER, $row->rev_user_text ); |
||
480 | $batch->add( NS_USER_TALK, $row->rev_user_text ); |
||
481 | } |
||
482 | } |
||
483 | $this->parentLens = Revision::getParentLengths( $this->mDb, $revIds ); |
||
0 ignored issues
–
show
It seems like
$this->mDb can be null ; however, getParentLengths() does not accept null , maybe add an additional type check?
Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code: /** @return stdClass|null */
function mayReturnNull() { }
function doesNotAcceptNull(stdClass $x) { }
// With potential error.
function withoutCheck() {
$x = mayReturnNull();
doesNotAcceptNull($x); // Potential error here.
}
// Safe - Alternative 1
function withCheck1() {
$x = mayReturnNull();
if ( ! $x instanceof stdClass) {
throw new \LogicException('$x must be defined.');
}
doesNotAcceptNull($x);
}
// Safe - Alternative 2
function withCheck2() {
$x = mayReturnNull();
if ($x instanceof stdClass) {
doesNotAcceptNull($x);
}
}
Loading history...
|
|||
484 | $batch->execute(); |
||
485 | $this->mResult->seek( 0 ); |
||
486 | } |
||
487 | |||
488 | /** |
||
489 | * Creates begin of history list with a submit button |
||
490 | * |
||
491 | * @return string HTML output |
||
492 | */ |
||
493 | function getStartBody() { |
||
494 | $this->lastRow = false; |
||
495 | $this->counter = 1; |
||
496 | $this->oldIdChecked = 0; |
||
497 | |||
498 | $this->getOutput()->wrapWikiMsg( "<div class='mw-history-legend'>\n$1\n</div>", 'histlegend' ); |
||
499 | $s = Html::openElement( 'form', [ 'action' => wfScript(), |
||
500 | 'id' => 'mw-history-compare' ] ) . "\n"; |
||
501 | $s .= Html::hidden( 'title', $this->getTitle()->getPrefixedDBkey() ) . "\n"; |
||
502 | $s .= Html::hidden( 'action', 'historysubmit' ) . "\n"; |
||
503 | $s .= Html::hidden( 'type', 'revision' ) . "\n"; |
||
504 | |||
505 | // Button container stored in $this->buttons for re-use in getEndBody() |
||
506 | $this->buttons = '<div>'; |
||
507 | $className = 'historysubmit mw-history-compareselectedversions-button'; |
||
508 | $attrs = [ 'class' => $className ] |
||
509 | + Linker::tooltipAndAccesskeyAttribs( 'compareselectedversions' ); |
||
510 | $this->buttons .= $this->submitButton( $this->msg( 'compareselectedversions' )->text(), |
||
511 | $attrs |
||
512 | ) . "\n"; |
||
513 | |||
514 | $user = $this->getUser(); |
||
515 | $actionButtons = ''; |
||
516 | if ( $user->isAllowed( 'deleterevision' ) ) { |
||
517 | $actionButtons .= $this->getRevisionButton( 'revisiondelete', 'showhideselectedversions' ); |
||
518 | } |
||
519 | if ( $this->showTagEditUI ) { |
||
520 | $actionButtons .= $this->getRevisionButton( 'editchangetags', 'history-edit-tags' ); |
||
521 | } |
||
522 | if ( $actionButtons ) { |
||
523 | $this->buttons .= Xml::tags( 'div', [ 'class' => |
||
524 | 'mw-history-revisionactions' ], $actionButtons ); |
||
525 | } |
||
526 | |||
527 | if ( $user->isAllowed( 'deleterevision' ) || $this->showTagEditUI ) { |
||
528 | $this->buttons .= ( new ListToggle( $this->getOutput() ) )->getHTML(); |
||
529 | } |
||
530 | |||
531 | $this->buttons .= '</div>'; |
||
532 | |||
533 | $s .= $this->buttons; |
||
534 | $s .= '<ul id="pagehistory">' . "\n"; |
||
535 | |||
536 | return $s; |
||
537 | } |
||
538 | |||
539 | private function getRevisionButton( $name, $msg ) { |
||
540 | $this->preventClickjacking(); |
||
541 | # Note bug #20966, <button> is non-standard in IE<8 |
||
542 | $element = Html::element( |
||
543 | 'button', |
||
544 | [ |
||
545 | 'type' => 'submit', |
||
546 | 'name' => $name, |
||
547 | 'value' => '1', |
||
548 | 'class' => "historysubmit mw-history-$name-button", |
||
549 | ], |
||
550 | $this->msg( $msg )->text() |
||
551 | ) . "\n"; |
||
552 | return $element; |
||
553 | } |
||
554 | |||
555 | function getEndBody() { |
||
556 | if ( $this->lastRow ) { |
||
557 | $latest = $this->counter == 1 && $this->mIsFirst; |
||
558 | $firstInList = $this->counter == 1; |
||
559 | if ( $this->mIsBackwards ) { |
||
560 | # Next row is unknown, but for UI reasons, probably exists if an offset has been specified |
||
561 | if ( $this->mOffset == '' ) { |
||
562 | $next = null; |
||
563 | } else { |
||
564 | $next = 'unknown'; |
||
565 | } |
||
566 | } else { |
||
567 | # The next row is the past-the-end row |
||
568 | $next = $this->mPastTheEndRow; |
||
569 | } |
||
570 | $this->counter++; |
||
571 | |||
572 | $notifTimestamp = $this->getConfig()->get( 'ShowUpdatedMarker' ) |
||
573 | ? $this->getTitle()->getNotificationTimestamp( $this->getUser() ) |
||
574 | : false; |
||
575 | |||
576 | $s = $this->historyLine( |
||
577 | $this->lastRow, $next, $notifTimestamp, $latest, $firstInList ); |
||
0 ignored issues
–
show
It seems like
$this->lastRow can also be of type boolean ; however, HistoryPager::historyLine() does only seem to accept object<stdClass> , maybe add an additional type check?
If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check: /**
* @return array|string
*/
function returnsDifferentValues($x) {
if ($x) {
return 'foo';
}
return array();
}
$x = returnsDifferentValues($y);
if (is_array($x)) {
// $x is an array.
}
If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.
Loading history...
|
|||
578 | } else { |
||
579 | $s = ''; |
||
580 | } |
||
581 | $s .= "</ul>\n"; |
||
582 | # Add second buttons only if there is more than one rev |
||
583 | if ( $this->getNumRows() > 2 ) { |
||
584 | $s .= $this->buttons; |
||
585 | } |
||
586 | $s .= '</form>'; |
||
587 | |||
588 | return $s; |
||
589 | } |
||
590 | |||
591 | /** |
||
592 | * Creates a submit button |
||
593 | * |
||
594 | * @param string $message Text of the submit button, will be escaped |
||
595 | * @param array $attributes Attributes |
||
596 | * @return string HTML output for the submit button |
||
597 | */ |
||
598 | function submitButton( $message, $attributes = [] ) { |
||
599 | # Disable submit button if history has 1 revision only |
||
600 | if ( $this->getNumRows() > 1 ) { |
||
601 | return Html::submitButton( $message, $attributes ); |
||
602 | } else { |
||
603 | return ''; |
||
604 | } |
||
605 | } |
||
606 | |||
607 | /** |
||
608 | * Returns a row from the history printout. |
||
609 | * |
||
610 | * @todo document some more, and maybe clean up the code (some params redundant?) |
||
611 | * |
||
612 | * @param stdClass $row The database row corresponding to the previous line. |
||
613 | * @param mixed $next The database row corresponding to the next line |
||
614 | * (chronologically previous) |
||
615 | * @param bool|string $notificationtimestamp |
||
616 | * @param bool $latest Whether this row corresponds to the page's latest revision. |
||
617 | * @param bool $firstInList Whether this row corresponds to the first |
||
618 | * displayed on this history page. |
||
619 | * @return string HTML output for the row |
||
620 | */ |
||
621 | function historyLine( $row, $next, $notificationtimestamp = false, |
||
622 | $latest = false, $firstInList = false ) { |
||
623 | $rev = new Revision( $row ); |
||
624 | $rev->setTitle( $this->getTitle() ); |
||
0 ignored issues
–
show
It seems like
$this->getTitle() can be null ; however, setTitle() does not accept null , maybe add an additional type check?
Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code: /** @return stdClass|null */
function mayReturnNull() { }
function doesNotAcceptNull(stdClass $x) { }
// With potential error.
function withoutCheck() {
$x = mayReturnNull();
doesNotAcceptNull($x); // Potential error here.
}
// Safe - Alternative 1
function withCheck1() {
$x = mayReturnNull();
if ( ! $x instanceof stdClass) {
throw new \LogicException('$x must be defined.');
}
doesNotAcceptNull($x);
}
// Safe - Alternative 2
function withCheck2() {
$x = mayReturnNull();
if ($x instanceof stdClass) {
doesNotAcceptNull($x);
}
}
Loading history...
|
|||
625 | |||
626 | if ( is_object( $next ) ) { |
||
627 | $prevRev = new Revision( $next ); |
||
628 | $prevRev->setTitle( $this->getTitle() ); |
||
0 ignored issues
–
show
It seems like
$this->getTitle() can be null ; however, setTitle() does not accept null , maybe add an additional type check?
Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code: /** @return stdClass|null */
function mayReturnNull() { }
function doesNotAcceptNull(stdClass $x) { }
// With potential error.
function withoutCheck() {
$x = mayReturnNull();
doesNotAcceptNull($x); // Potential error here.
}
// Safe - Alternative 1
function withCheck1() {
$x = mayReturnNull();
if ( ! $x instanceof stdClass) {
throw new \LogicException('$x must be defined.');
}
doesNotAcceptNull($x);
}
// Safe - Alternative 2
function withCheck2() {
$x = mayReturnNull();
if ($x instanceof stdClass) {
doesNotAcceptNull($x);
}
}
Loading history...
|
|||
629 | } else { |
||
630 | $prevRev = null; |
||
631 | } |
||
632 | |||
633 | $curlink = $this->curLink( $rev, $latest ); |
||
634 | $lastlink = $this->lastLink( $rev, $next ); |
||
635 | $curLastlinks = $curlink . $this->historyPage->message['pipe-separator'] . $lastlink; |
||
636 | $histLinks = Html::rawElement( |
||
637 | 'span', |
||
638 | [ 'class' => 'mw-history-histlinks' ], |
||
639 | $this->msg( 'parentheses' )->rawParams( $curLastlinks )->escaped() |
||
640 | ); |
||
641 | |||
642 | $diffButtons = $this->diffButtons( $rev, $firstInList ); |
||
643 | $s = $histLinks . $diffButtons; |
||
644 | |||
645 | $link = $this->revLink( $rev ); |
||
646 | $classes = []; |
||
647 | |||
648 | $del = ''; |
||
649 | $user = $this->getUser(); |
||
650 | $canRevDelete = $user->isAllowed( 'deleterevision' ); |
||
651 | // Show checkboxes for each revision, to allow for revision deletion and |
||
652 | // change tags |
||
653 | if ( $canRevDelete || $this->showTagEditUI ) { |
||
654 | $this->preventClickjacking(); |
||
655 | // If revision was hidden from sysops and we don't need the checkbox |
||
656 | // for anything else, disable it |
||
657 | View Code Duplication | if ( !$this->showTagEditUI && !$rev->userCan( Revision::DELETED_RESTRICTED, $user ) ) { |
|
658 | $del = Xml::check( 'deleterevisions', false, [ 'disabled' => 'disabled' ] ); |
||
659 | // Otherwise, enable the checkbox... |
||
660 | } else { |
||
661 | $del = Xml::check( 'showhiderevisions', false, |
||
662 | [ 'name' => 'ids[' . $rev->getId() . ']' ] ); |
||
663 | } |
||
664 | // User can only view deleted revisions... |
||
665 | } elseif ( $rev->getVisibility() && $user->isAllowed( 'deletedhistory' ) ) { |
||
666 | // If revision was hidden from sysops, disable the link |
||
667 | if ( !$rev->userCan( Revision::DELETED_RESTRICTED, $user ) ) { |
||
668 | $del = Linker::revDeleteLinkDisabled( false ); |
||
669 | // Otherwise, show the link... |
||
670 | } else { |
||
671 | $query = [ 'type' => 'revision', |
||
672 | 'target' => $this->getTitle()->getPrefixedDBkey(), 'ids' => $rev->getId() ]; |
||
673 | $del .= Linker::revDeleteLink( $query, |
||
674 | $rev->isDeleted( Revision::DELETED_RESTRICTED ), false ); |
||
675 | } |
||
676 | } |
||
677 | if ( $del ) { |
||
678 | $s .= " $del "; |
||
679 | } |
||
680 | |||
681 | $lang = $this->getLanguage(); |
||
682 | $dirmark = $lang->getDirMark(); |
||
683 | |||
684 | $s .= " $link"; |
||
685 | $s .= $dirmark; |
||
686 | $s .= " <span class='history-user'>" . |
||
687 | Linker::revUserTools( $rev, true ) . "</span>"; |
||
688 | $s .= $dirmark; |
||
689 | |||
690 | if ( $rev->isMinor() ) { |
||
691 | $s .= ' ' . ChangesList::flag( 'minor', $this->getContext() ); |
||
692 | } |
||
693 | |||
694 | # Sometimes rev_len isn't populated |
||
695 | if ( $rev->getSize() !== null ) { |
||
696 | # Size is always public data |
||
697 | $prevSize = isset( $this->parentLens[$row->rev_parent_id] ) |
||
698 | ? $this->parentLens[$row->rev_parent_id] |
||
699 | : 0; |
||
700 | $sDiff = ChangesList::showCharacterDifference( $prevSize, $rev->getSize() ); |
||
701 | $fSize = Linker::formatRevisionSize( $rev->getSize() ); |
||
702 | $s .= ' <span class="mw-changeslist-separator">. .</span> ' . "$fSize $sDiff"; |
||
703 | } |
||
704 | |||
705 | # Text following the character difference is added just before running hooks |
||
706 | $s2 = Linker::revComment( $rev, false, true ); |
||
707 | |||
708 | if ( $notificationtimestamp && ( $row->rev_timestamp >= $notificationtimestamp ) ) { |
||
709 | $s2 .= ' <span class="updatedmarker">' . $this->msg( 'updatedmarker' )->escaped() . '</span>'; |
||
710 | $classes[] = 'mw-history-line-updated'; |
||
711 | } |
||
712 | |||
713 | $tools = []; |
||
714 | |||
715 | # Rollback and undo links |
||
716 | if ( $prevRev && $this->getTitle()->quickUserCan( 'edit', $user ) ) { |
||
717 | if ( $latest && $this->getTitle()->quickUserCan( 'rollback', $user ) ) { |
||
718 | // Get a rollback link without the brackets |
||
719 | $rollbackLink = Linker::generateRollback( |
||
720 | $rev, |
||
721 | $this->getContext(), |
||
722 | [ 'verify', 'noBrackets' ] |
||
723 | ); |
||
724 | if ( $rollbackLink ) { |
||
725 | $this->preventClickjacking(); |
||
726 | $tools[] = $rollbackLink; |
||
727 | } |
||
728 | } |
||
729 | |||
730 | if ( !$rev->isDeleted( Revision::DELETED_TEXT ) |
||
731 | && !$prevRev->isDeleted( Revision::DELETED_TEXT ) |
||
732 | ) { |
||
733 | # Create undo tooltip for the first (=latest) line only |
||
734 | $undoTooltip = $latest |
||
735 | ? [ 'title' => $this->msg( 'tooltip-undo' )->text() ] |
||
736 | : []; |
||
737 | $undolink = Linker::linkKnown( |
||
738 | $this->getTitle(), |
||
739 | $this->msg( 'editundo' )->escaped(), |
||
740 | $undoTooltip, |
||
741 | [ |
||
742 | 'action' => 'edit', |
||
743 | 'undoafter' => $prevRev->getId(), |
||
744 | 'undo' => $rev->getId() |
||
745 | ] |
||
746 | ); |
||
747 | $tools[] = "<span class=\"mw-history-undo\">{$undolink}</span>"; |
||
748 | } |
||
749 | } |
||
750 | // Allow extension to add their own links here |
||
751 | Hooks::run( 'HistoryRevisionTools', [ $rev, &$tools, $prevRev, $user ] ); |
||
752 | |||
753 | if ( $tools ) { |
||
754 | $s2 .= ' ' . $this->msg( 'parentheses' )->rawParams( $lang->pipeList( $tools ) )->escaped(); |
||
755 | } |
||
756 | |||
757 | # Tags |
||
758 | list( $tagSummary, $newClasses ) = ChangeTags::formatSummaryRow( |
||
759 | $row->ts_tags, |
||
760 | 'history', |
||
761 | $this->getContext() |
||
762 | ); |
||
763 | $classes = array_merge( $classes, $newClasses ); |
||
764 | if ( $tagSummary !== '' ) { |
||
765 | $s2 .= " $tagSummary"; |
||
766 | } |
||
767 | |||
768 | # Include separator between character difference and following text |
||
769 | if ( $s2 !== '' ) { |
||
770 | $s .= ' <span class="mw-changeslist-separator">. .</span> ' . $s2; |
||
771 | } |
||
772 | |||
773 | Hooks::run( 'PageHistoryLineEnding', [ $this, &$row, &$s, &$classes ] ); |
||
774 | |||
775 | $attribs = []; |
||
776 | if ( $classes ) { |
||
777 | $attribs['class'] = implode( ' ', $classes ); |
||
778 | } |
||
779 | |||
780 | return Xml::tags( 'li', $attribs, $s ) . "\n"; |
||
781 | } |
||
782 | |||
783 | /** |
||
784 | * Create a link to view this revision of the page |
||
785 | * |
||
786 | * @param Revision $rev |
||
787 | * @return string |
||
788 | */ |
||
789 | function revLink( $rev ) { |
||
790 | $date = $this->getLanguage()->userTimeAndDate( $rev->getTimestamp(), $this->getUser() ); |
||
791 | $date = htmlspecialchars( $date ); |
||
792 | if ( $rev->userCan( Revision::DELETED_TEXT, $this->getUser() ) ) { |
||
793 | $link = Linker::linkKnown( |
||
794 | $this->getTitle(), |
||
795 | $date, |
||
796 | [ 'class' => 'mw-changeslist-date' ], |
||
797 | [ 'oldid' => $rev->getId() ] |
||
798 | ); |
||
799 | } else { |
||
800 | $link = $date; |
||
801 | } |
||
802 | if ( $rev->isDeleted( Revision::DELETED_TEXT ) ) { |
||
803 | $link = "<span class=\"history-deleted\">$link</span>"; |
||
804 | } |
||
805 | |||
806 | return $link; |
||
807 | } |
||
808 | |||
809 | /** |
||
810 | * Create a diff-to-current link for this revision for this page |
||
811 | * |
||
812 | * @param Revision $rev |
||
813 | * @param bool $latest This is the latest revision of the page? |
||
814 | * @return string |
||
815 | */ |
||
816 | function curLink( $rev, $latest ) { |
||
817 | $cur = $this->historyPage->message['cur']; |
||
818 | if ( $latest || !$rev->userCan( Revision::DELETED_TEXT, $this->getUser() ) ) { |
||
819 | return $cur; |
||
820 | } else { |
||
821 | return Linker::linkKnown( |
||
822 | $this->getTitle(), |
||
823 | $cur, |
||
824 | [], |
||
825 | [ |
||
826 | 'diff' => $this->getWikiPage()->getLatest(), |
||
827 | 'oldid' => $rev->getId() |
||
828 | ] |
||
829 | ); |
||
830 | } |
||
831 | } |
||
832 | |||
833 | /** |
||
834 | * Create a diff-to-previous link for this revision for this page. |
||
835 | * |
||
836 | * @param Revision $prevRev The revision being displayed |
||
837 | * @param stdClass|string|null $next The next revision in list (that is |
||
838 | * the previous one in chronological order). |
||
839 | * May either be a row, "unknown" or null. |
||
840 | * @return string |
||
841 | */ |
||
842 | function lastLink( $prevRev, $next ) { |
||
843 | $last = $this->historyPage->message['last']; |
||
844 | |||
845 | if ( $next === null ) { |
||
846 | # Probably no next row |
||
847 | return $last; |
||
848 | } |
||
849 | |||
850 | if ( $next === 'unknown' ) { |
||
851 | # Next row probably exists but is unknown, use an oldid=prev link |
||
852 | return Linker::linkKnown( |
||
853 | $this->getTitle(), |
||
854 | $last, |
||
855 | [], |
||
856 | [ |
||
857 | 'diff' => $prevRev->getId(), |
||
858 | 'oldid' => 'prev' |
||
859 | ] |
||
860 | ); |
||
861 | } |
||
862 | |||
863 | $nextRev = new Revision( $next ); |
||
0 ignored issues
–
show
It seems like
$next defined by parameter $next on line 842 can also be of type string ; however, Revision::__construct() does only seem to accept object|array , maybe add an additional type check?
This check looks at variables that have been passed in as parameters and are passed out again to other methods. If the outgoing method call has stricter type requirements than the method itself, an issue is raised. An additional type check may prevent trouble.
Loading history...
|
|||
864 | |||
865 | if ( !$prevRev->userCan( Revision::DELETED_TEXT, $this->getUser() ) |
||
866 | || !$nextRev->userCan( Revision::DELETED_TEXT, $this->getUser() ) |
||
867 | ) { |
||
868 | return $last; |
||
869 | } |
||
870 | |||
871 | return Linker::linkKnown( |
||
872 | $this->getTitle(), |
||
873 | $last, |
||
874 | [], |
||
875 | [ |
||
876 | 'diff' => $prevRev->getId(), |
||
877 | 'oldid' => $next->rev_id |
||
878 | ] |
||
879 | ); |
||
880 | } |
||
881 | |||
882 | /** |
||
883 | * Create radio buttons for page history |
||
884 | * |
||
885 | * @param Revision $rev |
||
886 | * @param bool $firstInList Is this version the first one? |
||
887 | * |
||
888 | * @return string HTML output for the radio buttons |
||
889 | */ |
||
890 | function diffButtons( $rev, $firstInList ) { |
||
891 | if ( $this->getNumRows() > 1 ) { |
||
892 | $id = $rev->getId(); |
||
893 | $radio = [ 'type' => 'radio', 'value' => $id ]; |
||
894 | /** @todo Move title texts to javascript */ |
||
895 | if ( $firstInList ) { |
||
896 | $first = Xml::element( 'input', |
||
897 | array_merge( $radio, [ |
||
898 | 'style' => 'visibility:hidden', |
||
899 | 'name' => 'oldid', |
||
900 | 'id' => 'mw-oldid-null' ] ) |
||
901 | ); |
||
902 | $checkmark = [ 'checked' => 'checked' ]; |
||
903 | } else { |
||
904 | # Check visibility of old revisions |
||
905 | if ( !$rev->userCan( Revision::DELETED_TEXT, $this->getUser() ) ) { |
||
906 | $radio['disabled'] = 'disabled'; |
||
907 | $checkmark = []; // We will check the next possible one |
||
908 | } elseif ( !$this->oldIdChecked ) { |
||
909 | $checkmark = [ 'checked' => 'checked' ]; |
||
910 | $this->oldIdChecked = $id; |
||
911 | } else { |
||
912 | $checkmark = []; |
||
913 | } |
||
914 | $first = Xml::element( 'input', |
||
915 | array_merge( $radio, $checkmark, [ |
||
916 | 'name' => 'oldid', |
||
917 | 'id' => "mw-oldid-$id" ] ) ); |
||
918 | $checkmark = []; |
||
919 | } |
||
920 | $second = Xml::element( 'input', |
||
921 | array_merge( $radio, $checkmark, [ |
||
922 | 'name' => 'diff', |
||
923 | 'id' => "mw-diff-$id" ] ) ); |
||
924 | |||
925 | return $first . $second; |
||
926 | } else { |
||
927 | return ''; |
||
928 | } |
||
929 | } |
||
930 | |||
931 | /** |
||
932 | * This is called if a write operation is possible from the generated HTML |
||
933 | * @param bool $enable |
||
934 | */ |
||
935 | function preventClickjacking( $enable = true ) { |
||
936 | $this->preventClickjacking = $enable; |
||
937 | } |
||
938 | |||
939 | /** |
||
940 | * Get the "prevent clickjacking" flag |
||
941 | * @return bool |
||
942 | */ |
||
943 | function getPreventClickjacking() { |
||
944 | return $this->preventClickjacking; |
||
945 | } |
||
946 | |||
947 | } |
||
948 |
It seems like the method you are trying to call exists only in some of the possible types.
Let’s take a look at an example:
Available Fixes
Add an additional type-check:
Only allow a single type to be passed if the variable comes from a parameter: