MediaWiki master
BlockManager.php
Go to the documentation of this file.
1<?php
21namespace MediaWiki\Block;
22
23use LogicException;
37use MWCryptHash;
38use Psr\Log\LoggerInterface;
39use Wikimedia\IPSet;
40use Wikimedia\IPUtils;
41
50 private $userFactory;
51
53 private $userIdentityUtils;
54
56 private $options;
57
61 public const CONSTRUCTOR_OPTIONS = [
71 ];
72
74 private $logger;
75
77 private $hookRunner;
78
80 private $blockStore;
81
83 private $proxyLookup;
84
86 private $userBlockCache;
87
89 private $createAccountBlockCache;
90
99 public function __construct(
100 ServiceOptions $options,
101 UserFactory $userFactory,
102 UserIdentityUtils $userIdentityUtils,
103 LoggerInterface $logger,
104 HookContainer $hookContainer,
105 DatabaseBlockStore $blockStore,
106 ProxyLookup $proxyLookup
107 ) {
108 $options->assertRequiredOptions( self::CONSTRUCTOR_OPTIONS );
109 $this->options = $options;
110 $this->userFactory = $userFactory;
111 $this->userIdentityUtils = $userIdentityUtils;
112 $this->logger = $logger;
113 $this->hookRunner = new HookRunner( $hookContainer );
114 $this->blockStore = $blockStore;
115 $this->proxyLookup = $proxyLookup;
116
117 $this->userBlockCache = new BlockCache;
118 $this->createAccountBlockCache = new BlockCache;
119 }
120
156 public function getUserBlock(
157 UserIdentity $user,
158 $request,
159 $fromReplica,
160 $disableIpBlockExemptChecking = false
161 ) {
162 // If this is the global user, they may be affected by IP blocks (case #1),
163 // or they may be exempt (case #2). If affected, look for additional blocks
164 // against the IP address and referenced in a cookie.
165 $checkIpBlocks = $request &&
166 // Because calling getBlock within Autopromote leads back to here,
167 // thus causing a infinite recursion. We fix this by not checking for
168 // ipblock-exempt when calling getBlock within Autopromote.
169 // See T270145.
170 !$disableIpBlockExemptChecking &&
171 !$this->isIpBlockExempt( $user );
172
173 return $this->getBlock(
174 $user,
175 $checkIpBlocks ? $request : null,
176 $fromReplica
177 );
178 }
179
197 public function getBlock(
198 UserIdentity $user,
199 ?WebRequest $request,
200 $fromReplica = true
201 ): ?AbstractBlock {
202 $fromPrimary = !$fromReplica;
203 $ip = null;
204
205 // TODO: normalise the fromPrimary parameter when replication is not configured.
206 // Maybe DatabaseBlockStore can tell us about the LoadBalancer configuration.
207 $cacheKey = new BlockCacheKey(
208 $request,
209 $user,
210 $fromPrimary
211 );
212 $block = $this->userBlockCache->get( $cacheKey );
213 if ( $block !== null ) {
214 $this->logger->debug( "Block cache hit with key {$cacheKey}" );
215 return $block ?: null;
216 }
217 $this->logger->debug( "Block cache miss with key {$cacheKey}" );
218
219 if ( $request ) {
220
221 // Case #1: checking the global user, including IP blocks
222 $ip = $request->getIP();
223 // For soft blocks, i.e. blocks that don't block logged-in users,
224 // temporary users are treated as anon users, and are blocked.
225 $applySoftBlocks = !$this->userIdentityUtils->isNamed( $user );
226
227 $xff = $request->getHeader( 'X-Forwarded-For' );
228
229 $blocks = array_merge(
230 $this->blockStore->newListFromTarget( $user, $ip, $fromPrimary ),
231 $this->getSystemIpBlocks( $ip, $applySoftBlocks ),
232 $this->getXffBlocks( $ip, $xff, $applySoftBlocks, $fromPrimary ),
233 $this->getCookieBlock( $user, $request )
234 );
235 } else {
236
237 // Case #2: checking the global user, but they are exempt from IP blocks
238 // and cookie blocks, so we only check for a user account block.
239 // Case #3: checking whether another user's account is blocked.
240 $blocks = $this->blockStore->newListFromTarget( $user, null, $fromPrimary );
241
242 }
243
244 $block = $this->createGetBlockResult( $ip, $blocks );
245
246 $legacyUser = $this->userFactory->newFromUserIdentity( $user );
247 $this->hookRunner->onGetUserBlock( clone $legacyUser, $ip, $block );
248
249 $this->userBlockCache->set( $cacheKey, $block ?: false );
250 return $block;
251 }
252
258 public function clearUserCache( UserIdentity $user ) {
259 $this->userBlockCache->clearUser( $user );
260 $this->createAccountBlockCache->clearUser( $user );
261 }
262
274 public function getCreateAccountBlock(
275 UserIdentity $user,
276 ?WebRequest $request,
277 $fromReplica
278 ) {
279 $key = new BlockCacheKey( $request, $user, $fromReplica );
280 $cachedBlock = $this->createAccountBlockCache->get( $key );
281 if ( $cachedBlock !== null ) {
282 $this->logger->debug( "Create account block cache hit with key {$key}" );
283 return $cachedBlock ?: null;
284 }
285 $this->logger->debug( "Create account block cache miss with key {$key}" );
286
287 $applicableBlocks = [];
288 $userBlock = $this->getBlock( $user, $request, $fromReplica );
289 if ( $userBlock ) {
290 $applicableBlocks = $userBlock->toArray();
291 }
292
293 // T15611: if the IP address the user is trying to create an account from is
294 // blocked with createaccount disabled, prevent new account creation there even
295 // when the user is logged in
296 if ( $request ) {
297 $ipBlock = $this->blockStore->newFromTarget(
298 null, $request->getIP()
299 );
300 if ( $ipBlock ) {
301 $applicableBlocks = array_merge( $applicableBlocks, $ipBlock->toArray() );
302 }
303 }
304
305 foreach ( $applicableBlocks as $i => $block ) {
306 if ( !$block->appliesToRight( 'createaccount' ) ) {
307 unset( $applicableBlocks[$i] );
308 }
309 }
310 $result = $this->createGetBlockResult(
311 $request ? $request->getIP() : null,
312 $applicableBlocks
313 );
314 $this->createAccountBlockCache->set( $key, $result ?: false );
315 return $result;
316 }
317
334 public function filter( ?Block $block, $callback ) {
335 if ( !$block ) {
336 return null;
337 } elseif ( $block instanceof CompositeBlock ) {
338 $blocks = $block->getOriginalBlocks();
339 $originalCount = count( $blocks );
340 foreach ( $blocks as $i => $originalBlock ) {
341 if ( !$callback( $originalBlock ) ) {
342 unset( $blocks[$i] );
343 }
344 }
345 if ( !$blocks ) {
346 return null;
347 } elseif ( count( $blocks ) === 1 ) {
348 return $blocks[ array_key_first( $blocks ) ];
349 } elseif ( count( $blocks ) === $originalCount ) {
350 return $block;
351 } else {
352 return $block->withOriginalBlocks( array_values( $blocks ) );
353 }
354 } elseif ( !$callback( $block ) ) {
355 return null;
356 } else {
357 return $block;
358 }
359 }
360
366 private function isIpBlockExempt( UserIdentity $user ) {
367 return MediaWikiServices::getInstance()->getPermissionManager()
368 ->userHasRight( $user, 'ipblock-exempt' );
369 }
370
376 private function createGetBlockResult( ?string $ip, array $blocks ): ?AbstractBlock {
377 // Filter out any duplicated blocks, e.g. from the cookie
378 $blocks = $this->getUniqueBlocks( $blocks );
379
380 if ( count( $blocks ) === 0 ) {
381 return null;
382 } elseif ( count( $blocks ) === 1 ) {
383 return $blocks[ 0 ];
384 } else {
385 $compositeBlock = CompositeBlock::createFromBlocks( ...$blocks );
386 $compositeBlock->setTarget( $ip );
387 return $compositeBlock;
388 }
389 }
390
400 public function getIpBlock( string $ip, bool $fromReplica ): ?AbstractBlock {
401 if ( !IPUtils::isValid( $ip ) ) {
402 return null;
403 }
404
405 $blocks = array_merge(
406 $this->blockStore->newListFromTarget( $ip, $ip, !$fromReplica ),
407 $this->getSystemIpBlocks( $ip, true )
408 );
409
410 return $this->createGetBlockResult( $ip, $blocks );
411 }
412
420 private function getCookieBlock( UserIdentity $user, WebRequest $request ): array {
421 $cookieBlock = $this->getBlockFromCookieValue( $user, $request );
422
423 return $cookieBlock instanceof DatabaseBlock ? [ $cookieBlock ] : [];
424 }
425
433 private function getSystemIpBlocks( string $ip, bool $applySoftBlocks ): array {
434 $blocks = [];
435
436 // Proxy blocking
437 if ( !in_array( $ip, $this->options->get( MainConfigNames::ProxyWhitelist ) ) ) {
438 // Local list
439 if ( $this->isLocallyBlockedProxy( $ip ) ) {
440 $blocks[] = new SystemBlock( [
441 'reason' => new Message( 'proxyblockreason' ),
442 'address' => $ip,
443 'systemBlock' => 'proxy',
444 ] );
445 } elseif ( $applySoftBlocks && $this->isDnsBlacklisted( $ip ) ) {
446 $blocks[] = new SystemBlock( [
447 'reason' => new Message( 'sorbsreason' ),
448 'address' => $ip,
449 'anonOnly' => true,
450 'systemBlock' => 'dnsbl',
451 ] );
452 }
453 }
454
455 // Soft blocking
456 if ( $applySoftBlocks && IPUtils::isInRanges( $ip, $this->options->get( MainConfigNames::SoftBlockRanges ) ) ) {
457 $blocks[] = new SystemBlock( [
458 'address' => $ip,
459 'reason' => new Message( 'softblockrangesreason', [ $ip ] ),
460 'anonOnly' => true,
461 'systemBlock' => 'wgSoftBlockRanges',
462 ] );
463 }
464
465 return $blocks;
466 }
467
479 private function getXffBlocks(
480 string $ip,
481 string $xff,
482 bool $applySoftBlocks,
483 bool $fromPrimary
484 ): array {
485 // (T25343) Apply IP blocks to the contents of XFF headers, if enabled
486 if ( $this->options->get( MainConfigNames::ApplyIpBlocksToXff )
487 && !in_array( $ip, $this->options->get( MainConfigNames::ProxyWhitelist ) )
488 ) {
489 $xff = array_map( 'trim', explode( ',', $xff ) );
490 $xff = array_diff( $xff, [ $ip ] );
491 $xffblocks = $this->getBlocksForIPList( $xff, $applySoftBlocks, $fromPrimary );
492
493 // (T285159) Exclude autoblocks from XFF headers to prevent spoofed
494 // headers uncovering the IPs of autoblocked users
495 $xffblocks = array_filter( $xffblocks, static function ( $block ) {
496 return $block->getType() !== Block::TYPE_AUTO;
497 } );
498
499 return $xffblocks;
500 }
501
502 return [];
503 }
504
517 public function getBlocksForIPList( array $ipChain, bool $applySoftBlocks, bool $fromPrimary ) {
518 if ( $ipChain === [] ) {
519 return [];
520 }
521
522 $ips = [];
523 foreach ( array_unique( $ipChain ) as $ipaddr ) {
524 // Discard invalid IP addresses. Since XFF can be spoofed and we do not
525 // necessarily trust the header given to us, make sure that we are only
526 // checking for blocks on well-formatted IP addresses (IPv4 and IPv6).
527 // Do not treat private IP spaces as special as it may be desirable for wikis
528 // to block those IP ranges in order to stop misbehaving proxies that spoof XFF.
529 if ( !IPUtils::isValid( $ipaddr ) ) {
530 continue;
531 }
532 // Don't check trusted IPs (includes local CDNs which will be in every request)
533 if ( $this->proxyLookup->isTrustedProxy( $ipaddr ) ) {
534 continue;
535 }
536 $ips[] = $ipaddr;
537 }
538 return $this->blockStore->newListFromIPs( $ips, $applySoftBlocks, $fromPrimary );
539 }
540
551 private function getUniqueBlocks( array $blocks ) {
552 $systemBlocks = [];
553 $databaseBlocks = [];
554
555 foreach ( $blocks as $block ) {
556 if ( $block instanceof SystemBlock ) {
557 $systemBlocks[] = $block;
558 } elseif ( $block->getType() === DatabaseBlock::TYPE_AUTO ) {
560 '@phan-var DatabaseBlock $block';
561 if ( !isset( $databaseBlocks[$block->getParentBlockId()] ) ) {
562 $databaseBlocks[$block->getParentBlockId()] = $block;
563 }
564 } else {
565 // @phan-suppress-next-line PhanTypeMismatchDimAssignment getId is not null here
566 $databaseBlocks[$block->getId()] = $block;
567 }
568 }
569
570 return array_values( array_merge( $systemBlocks, $databaseBlocks ) );
571 }
572
584 private function getBlockFromCookieValue(
585 UserIdentity $user,
586 WebRequest $request
587 ) {
588 $cookieValue = $request->getCookie( 'BlockID' );
589 if ( $cookieValue === null ) {
590 return false;
591 }
592
593 $blockCookieId = $this->getIdFromCookieValue( $cookieValue );
594 if ( $blockCookieId !== null ) {
595 $block = $this->blockStore->newFromID( $blockCookieId );
596 if (
597 $block instanceof DatabaseBlock &&
598 $this->shouldApplyCookieBlock( $block, !$user->isRegistered() )
599 ) {
600 return $block;
601 }
602 }
603
604 return false;
605 }
606
614 private function shouldApplyCookieBlock( DatabaseBlock $block, $isAnon ) {
615 if ( !$block->isExpired() ) {
616 switch ( $block->getType() ) {
617 case DatabaseBlock::TYPE_IP:
618 case DatabaseBlock::TYPE_RANGE:
619 // If block is type IP or IP range, load only
620 // if user is not logged in (T152462)
621 return $isAnon &&
622 $this->options->get( MainConfigNames::CookieSetOnIpBlock );
623 case DatabaseBlock::TYPE_USER:
624 return $block->isAutoblocking() &&
625 $this->options->get( MainConfigNames::CookieSetOnAutoblock );
626 default:
627 return false;
628 }
629 }
630 return false;
631 }
632
639 private function isLocallyBlockedProxy( $ip ) {
640 $proxyList = $this->options->get( MainConfigNames::ProxyList );
641 if ( !$proxyList ) {
642 return false;
643 }
644
645 if ( !is_array( $proxyList ) ) {
646 // Load values from the specified file
647 $proxyList = array_map( 'trim', file( $proxyList ) );
648 }
649
650 $proxyListIPSet = new IPSet( $proxyList );
651 return $proxyListIPSet->match( $ip );
652 }
653
661 public function isDnsBlacklisted( $ip, $checkAllowed = false ) {
662 if ( !$this->options->get( MainConfigNames::EnableDnsBlacklist ) ||
663 ( $checkAllowed && in_array( $ip, $this->options->get( MainConfigNames::ProxyWhitelist ) ) )
664 ) {
665 return false;
666 }
667
668 return $this->inDnsBlacklist( $ip, $this->options->get( MainConfigNames::DnsBlacklistUrls ) );
669 }
670
678 private function inDnsBlacklist( $ip, array $bases ) {
679 $found = false;
680 // @todo FIXME: IPv6 ??? (https://bugs.php.net/bug.php?id=33170)
681 if ( IPUtils::isIPv4( $ip ) ) {
682 // Reverse IP, T23255
683 $ipReversed = implode( '.', array_reverse( explode( '.', $ip ) ) );
684
685 foreach ( $bases as $base ) {
686 // Make hostname
687 // If we have an access key, use that too (ProjectHoneypot, etc.)
688 $basename = $base;
689 if ( is_array( $base ) ) {
690 if ( count( $base ) >= 2 ) {
691 // Access key is 1, base URL is 0
692 $hostname = "{$base[1]}.$ipReversed.{$base[0]}";
693 } else {
694 $hostname = "$ipReversed.{$base[0]}";
695 }
696 $basename = $base[0];
697 } else {
698 $hostname = "$ipReversed.$base";
699 }
700
701 // Send query
702 $ipList = $this->checkHost( $hostname );
703
704 if ( $ipList ) {
705 $this->logger->info(
706 'Hostname {hostname} is {ipList}, it\'s a proxy says {basename}!',
707 [
708 'hostname' => $hostname,
709 'ipList' => $ipList[0],
710 'basename' => $basename,
711 ]
712 );
713 $found = true;
714 break;
715 }
716
717 $this->logger->debug( "Requested $hostname, not found in $basename." );
718 }
719 }
720
721 return $found;
722 }
723
730 protected function checkHost( $hostname ) {
731 return gethostbynamel( $hostname );
732 }
733
753 public function trackBlockWithCookie( User $user, WebResponse $response ) {
754 $request = $user->getRequest();
755
756 if ( $request->getCookie( 'BlockID' ) !== null ) {
757 $cookieBlock = $this->getBlockFromCookieValue( $user, $request );
758 if ( $cookieBlock && $this->shouldApplyCookieBlock( $cookieBlock, $user->isAnon() ) ) {
759 return;
760 }
761 // The block pointed to by the cookie is invalid or should not be tracked.
762 $this->clearBlockCookie( $response );
763 }
764
765 if ( !$user->isSafeToLoad() ) {
766 // Prevent a circular dependency by not allowing this method to be called
767 // before or while the user is being loaded.
768 // E.g. User > BlockManager > Block > Message > getLanguage > User.
769 // See also T180050 and T226777.
770 throw new LogicException( __METHOD__ . ' requires a loaded User object' );
771 }
772 if ( $response->headersSent() ) {
773 throw new LogicException( __METHOD__ . ' must be called pre-send' );
774 }
775
776 $block = $user->getBlock();
777 $isAnon = $user->isAnon();
778
779 if ( $block ) {
780 foreach ( $block->toArray() as $originalBlock ) {
781 // TODO: Improve on simply tracking the first trackable block (T225654)
782 if ( $originalBlock instanceof DatabaseBlock
783 && $this->shouldTrackBlockWithCookie( $originalBlock, $isAnon )
784 ) {
785 $this->setBlockCookie( $originalBlock, $response );
786 return;
787 }
788 }
789 }
790 }
791
800 private function setBlockCookie( DatabaseBlock $block, WebResponse $response ) {
801 // Calculate the default expiry time.
802 $maxExpiryTime = wfTimestamp( TS_MW, (int)wfTimestamp() + ( 24 * 60 * 60 ) );
803
804 // Use the block's expiry time only if it's less than the default.
805 $expiryTime = $block->getExpiry();
806 if ( $expiryTime === 'infinity' || $expiryTime > $maxExpiryTime ) {
807 $expiryTime = $maxExpiryTime;
808 }
809
810 // Set the cookie
811 $expiryValue = (int)wfTimestamp( TS_UNIX, $expiryTime );
812 $cookieOptions = [ 'httpOnly' => false ];
813 $cookieValue = $this->getCookieValue( $block );
814 $response->setCookie( 'BlockID', $cookieValue, $expiryValue, $cookieOptions );
815 }
816
824 private function shouldTrackBlockWithCookie( DatabaseBlock $block, $isAnon ) {
825 switch ( $block->getType() ) {
826 case DatabaseBlock::TYPE_IP:
827 case DatabaseBlock::TYPE_RANGE:
828 return $isAnon && $this->options->get( MainConfigNames::CookieSetOnIpBlock );
829 case DatabaseBlock::TYPE_USER:
830 return !$isAnon &&
831 $this->options->get( MainConfigNames::CookieSetOnAutoblock ) &&
832 $block->isAutoblocking();
833 default:
834 return false;
835 }
836 }
837
844 public static function clearBlockCookie( WebResponse $response ) {
845 $response->clearCookie( 'BlockID', [ 'httpOnly' => false ] );
846 }
847
856 private function getIdFromCookieValue( $cookieValue ) {
857 // The cookie value must start with a number
858 if ( !is_numeric( substr( $cookieValue, 0, 1 ) ) ) {
859 return null;
860 }
861
862 // Extract the ID prefix from the cookie value (may be the whole value, if no bang found).
863 $bangPos = strpos( $cookieValue, '!' );
864 $id = ( $bangPos === false ) ? $cookieValue : substr( $cookieValue, 0, $bangPos );
865 if ( !$this->options->get( MainConfigNames::SecretKey ) ) {
866 // If there's no secret key, just use the ID as given.
867 return (int)$id;
868 }
869 $storedHmac = substr( $cookieValue, $bangPos + 1 );
870 $calculatedHmac = MWCryptHash::hmac( $id, $this->options->get( MainConfigNames::SecretKey ), false );
871 if ( $calculatedHmac === $storedHmac ) {
872 return (int)$id;
873 } else {
874 return null;
875 }
876 }
877
887 private function getCookieValue( DatabaseBlock $block ) {
888 $id = (string)$block->getId();
889 if ( !$this->options->get( MainConfigNames::SecretKey ) ) {
890 // If there's no secret key, don't append a HMAC.
891 return $id;
892 }
893 $hmac = MWCryptHash::hmac( $id, $this->options->get( MainConfigNames::SecretKey ), false );
894 return $id . '!' . $hmac;
895 }
896}
wfTimestamp( $outputtype=TS_UNIX, $ts=0)
Get a timestamp string in one of various formats.
if(!defined('MW_SETUP_CALLBACK'))
Definition WebStart.php:81
static hmac( $data, $key, $raw=true)
Generate a keyed cryptographic hash value (HMAC) for a string, making use of the best hash algorithm ...
getExpiry()
Get the block expiry time.
A service class for checking blocks.
trackBlockWithCookie(User $user, WebResponse $response)
Set the 'BlockID' cookie depending on block type and user authentication status.
isDnsBlacklisted( $ip, $checkAllowed=false)
Whether the given IP is in a DNS blacklist.
getIpBlock(string $ip, bool $fromReplica)
Get the blocks that apply to an IP address.
__construct(ServiceOptions $options, UserFactory $userFactory, UserIdentityUtils $userIdentityUtils, LoggerInterface $logger, HookContainer $hookContainer, DatabaseBlockStore $blockStore, ProxyLookup $proxyLookup)
clearUserCache(UserIdentity $user)
Clear the cache of any blocks that refer to the specified user.
filter(?Block $block, $callback)
Remove elements of a block which fail a callback test.
checkHost( $hostname)
Wrapper for mocking in tests.
getBlocksForIPList(array $ipChain, bool $applySoftBlocks, bool $fromPrimary)
Get all blocks that match any IP from an array of IP addresses.
getCreateAccountBlock(UserIdentity $user, ?WebRequest $request, $fromReplica)
Get the block which applies to a create account action, if there is any.
getUserBlock(UserIdentity $user, $request, $fromReplica, $disableIpBlockExemptChecking=false)
Get the blocks that apply to a user.
static clearBlockCookie(WebResponse $response)
Unset the 'BlockID' cookie.
getBlock(UserIdentity $user, ?WebRequest $request, $fromReplica=true)
Get the blocks that apply to a user.
static createFromBlocks(AbstractBlock ... $blocks)
Helper method for merging multiple blocks into a composite block.
A DatabaseBlock (unlike a SystemBlock) is stored in the database, may give rise to autoblocks and may...
System blocks are temporary blocks that are created on enforcement (e.g.
A class for passing options to services.
assertRequiredOptions(array $expectedKeys)
Assert that the list of options provided in this instance exactly match $expectedKeys,...
This class provides an implementation of the core hook interfaces, forwarding hook calls to HookConta...
A class containing constants representing the names of configuration variables.
const DnsBlacklistUrls
Name constant for the DnsBlacklistUrls setting, for use with Config::get()
const SoftBlockRanges
Name constant for the SoftBlockRanges setting, for use with Config::get()
const CookieSetOnAutoblock
Name constant for the CookieSetOnAutoblock setting, for use with Config::get()
const EnableDnsBlacklist
Name constant for the EnableDnsBlacklist setting, for use with Config::get()
const ApplyIpBlocksToXff
Name constant for the ApplyIpBlocksToXff setting, for use with Config::get()
const ProxyList
Name constant for the ProxyList setting, for use with Config::get()
const ProxyWhitelist
Name constant for the ProxyWhitelist setting, for use with Config::get()
const CookieSetOnIpBlock
Name constant for the CookieSetOnIpBlock setting, for use with Config::get()
const SecretKey
Name constant for the SecretKey setting, for use with Config::get()
Service locator for MediaWiki core services.
static getInstance()
Returns the global default instance of the top level service locator.
The Message class deals with fetching and processing of interface message into a variety of formats.
Definition Message.php:158
The WebRequest class encapsulates getting at data passed in the URL or via a POSTed form,...
getHeader( $name, $flags=0)
Get a request header, or false if it isn't set.
getIP()
Work out the IP address based on various globals For trusted proxies, use the XFF client IP (first of...
Allow programs to request this object from WebRequest::response() and handle all outputting (or lack ...
headersSent()
Test if headers have been sent.
setCookie( $name, $value, $expire=0, $options=[])
Set the browser cookie.
clearCookie( $name, $options=[])
Unset a browser cookie.
Creates User objects.
Convenience functions for interpreting UserIdentity objects using additional services or config.
internal since 1.36
Definition User.php:93
isSafeToLoad()
Test if it's safe to load this User object.
Definition User.php:333
getBlock( $freshness=IDBAccessObject::READ_NORMAL, $disableIpBlockExemptChecking=false)
Get the block affecting the user, or null if the user is not blocked.
Definition User.php:1415
isAnon()
Get whether the user is anonymous.
Definition User.php:2136
getRequest()
Get the WebRequest object to use with this object.
Definition User.php:2234
Represents a block that may prevent users from performing specific operations.
Definition Block.php:45
Interface for objects representing user identity.