8f9a5a60aa10cd61759b45ab7d931ecbd1175bab
[lhc/web/wiklou.git] / includes / BlockCache.php
1 <?php
2 /**
3 * Contain the blockcache class
4 * @package MediaWiki
5 */
6
7 /**
8 * Object for fast lookup of IP blocks
9 * Represents a memcached value, and in some sense, the entire ipblocks table
10 * @package MediaWiki
11 */
12 class BlockCache
13 {
14 var $mData = false, $mMemcKey;
15
16 /**
17 * Constructor
18 * Create a new BlockCache object
19 *
20 * @param Boolean $deferLoad specifies whether to immediately load the data from memcached.
21 * @param String $dbName specifies the memcached dbName prefix to be used. Defaults to $wgDBname.
22 */
23 function BlockCache( $deferLoad = false, $dbName = '' ) {
24 global $wgDBname;
25
26 if ( $dbName == '' ) {
27 $dbName = $wgDBname;
28 }
29
30 $this->mMemcKey = $dbName.':ipblocks';
31
32 if ( !$deferLoad ) {
33 $this->load();
34 }
35 }
36
37 /**
38 * Load the blocks from the database and save them to memcached
39 */
40 function loadFromDB() {
41 global $wgUseMemCached, $wgMemc;
42 $this->mData = array();
43 # Selecting FOR UPDATE is a convenient way to serialise the memcached and DB operations,
44 # which is necessary even though we don't update the DB
45 if ( $wgUseMemCached ) {
46 Block::enumBlocks( 'wfBlockCacheInsert', '', EB_FOR_UPDATE );
47 $wgMemc->set( $this->mMemcKey, $this->mData, 0 );
48 } else {
49 Block::enumBlocks( 'wfBlockCacheInsert', '' );
50 }
51 }
52
53 /**
54 * Load the cache from memcached or, if that's not possible, from the DB
55 */
56 function load() {
57 global $wgUseMemCached, $wgMemc;
58
59 if ( $this->mData === false) {
60 # Try memcached
61 if ( $wgUseMemCached ) {
62 $this->mData = $wgMemc->get( $this->mMemcKey );
63 }
64
65 if ( !is_array( $this->mData ) ) {
66 $this->loadFromDB();
67 }
68 }
69 }
70
71 /**
72 * Add a block to the cache
73 *
74 * @param Object &$block Reference to a "Block" object.
75 */
76 function insert( &$block ) {
77 if ( $block->mUser == 0 ) {
78 $nb = $block->getNetworkBits();
79 $ipint = $block->getIntegerAddr();
80 $index = $ipint >> ( 32 - $nb );
81
82 if ( !array_key_exists( $nb, $this->mData ) ) {
83 $this->mData[$nb] = array();
84 }
85
86 $this->mData[$nb][$index] = 1;
87 }
88 }
89
90 /**
91 * Find out if a given IP address is blocked
92 *
93 * @param String $ip IP address
94 */
95 function get( $ip ) {
96 $this->load();
97 $ipint = ip2long( $ip );
98 $blocked = false;
99
100 foreach ( $this->mData as $networkBits => $blockInts ) {
101 if ( array_key_exists( $ipint >> ( 32 - $networkBits ), $blockInts ) ) {
102 $blocked = true;
103 break;
104 }
105 }
106 if ( $blocked ) {
107 # Clear low order bits
108 if ( $networkBits != 32 ) {
109 $ip .= '/'.$networkBits;
110 $ip = Block::normaliseRange( $ip );
111 }
112 $block = new Block();
113 $block->load( $ip );
114 } else {
115 $block = false;
116 }
117
118 return $block;
119 }
120
121 /**
122 * Clear the local cache
123 * There was once a clear() to clear memcached too, but I deleted it
124 */
125 function clearLocal() {
126 $this->mData = false;
127 }
128 }
129
130 /**
131 * Add a block to the global $wgBlockCache
132 *
133 * @param Object $block A "Block"-object
134 * @param Any $tag unused
135 */
136 function wfBlockCacheInsert( $block, $tag ) {
137 global $wgBlockCache;
138 $wgBlockCache->insert( $block );
139 }