Allow $len/$redir to be passed in from other points
[lhc/web/wiklou.git] / includes / LinkCache.php
1 <?php
2 /**
3 * Cache for article titles (prefixed DB keys) and ids linked from one source
4 *
5 * @addtogroup Cache
6 */
7 class LinkCache {
8 // Increment $mClassVer whenever old serialized versions of this class
9 // becomes incompatible with the new version.
10 /* private */ var $mClassVer = 4;
11
12 /* private */ var $mPageLinks;
13 /* private */ var $mGoodLinks, $mBadLinks;
14 /* private */ var $mForUpdate;
15
16 /**
17 * Get an instance of this class
18 */
19 static function &singleton() {
20 static $instance;
21 if ( !isset( $instance ) ) {
22 $instance = new LinkCache;
23 }
24 return $instance;
25 }
26
27 function __construct() {
28 $this->mForUpdate = false;
29 $this->mPageLinks = array();
30 $this->mGoodLinks = array();
31 $this->mGoodLinkFields = array();
32 $this->mBadLinks = array();
33 }
34
35 /* private */ function getKey( $title ) {
36 return wfMemcKey( 'lc', 'title', $title );
37 }
38
39 /**
40 * General accessor to get/set whether SELECT FOR UPDATE should be used
41 */
42 function forUpdate( $update = NULL ) {
43 return wfSetVar( $this->mForUpdate, $update );
44 }
45
46 function getGoodLinkID( $title ) {
47 if ( array_key_exists( $title, $this->mGoodLinks ) ) {
48 return $this->mGoodLinks[$title];
49 } else {
50 return 0;
51 }
52 }
53
54 /**
55 * Get a field of a title object from cache.
56 * If this link is not good, it will return NULL.
57 * @param Title $title
58 * @param string $field ('length','redirect')
59 * @return mixed
60 */
61 function getGoodLinkFieldObj( $title, $field ) {
62 $dbkey = $title->getPrefixedDbKey();
63 if ( array_key_exists( $dbkey, $this->mGoodLinkFields ) ) {
64 return $this->mGoodLinkFields[$dbkey][$field];
65 } else {
66 return NULL;
67 }
68 }
69
70 function isBadLink( $title ) {
71 return array_key_exists( $title, $this->mBadLinks );
72 }
73
74 /**
75 * Add a link for the title to the link cache
76 * @param int $id
77 * @param Title $title
78 * @param int $len
79 * @param int $redir
80 */
81 function addGoodLinkObj( $id, $title, $len = -1, $redir = NULL ) {
82 $dbkey = $title->getPrefixedDbKey();
83 $this->mGoodLinks[$dbkey] = $id;
84 $this->mGoodLinkFields[$dbkey] = array( 'length' => $len, 'redirect' => $redir );
85 $this->mPageLinks[$dbkey] = $title;
86 }
87
88 function addBadLinkObj( $title ) {
89 $dbkey = $title->getPrefixedDbKey();
90 if ( ! $this->isBadLink( $dbkey ) ) {
91 $this->mBadLinks[$dbkey] = 1;
92 $this->mPageLinks[$dbkey] = $title;
93 }
94 }
95
96 function clearBadLink( $title ) {
97 unset( $this->mBadLinks[$title] );
98 $this->clearLink( $title );
99 }
100
101 function clearLink( $title ) {
102 global $wgMemc, $wgLinkCacheMemcached;
103 if( $wgLinkCacheMemcached )
104 $wgMemc->delete( $this->getKey( $title ) );
105 }
106
107 function getPageLinks() { return $this->mPageLinks; }
108 function getGoodLinks() { return $this->mGoodLinks; }
109 function getBadLinks() { return array_keys( $this->mBadLinks ); }
110
111 /**
112 * Add a title to the link cache, return the page_id or zero if non-existent
113 * @param $title String: title to add
114 * @param $len int, page size
115 * @param $redir bool, is redirect?
116 * @return integer
117 */
118 function addLink( $title, $len = -1, $redir = NULL ) {
119 $nt = Title::newFromDBkey( $title );
120 if( $nt ) {
121 return $this->addLinkObj( $nt, $len, $redir );
122 } else {
123 return 0;
124 }
125 }
126
127 /**
128 * Add a title to the link cache, return the page_id or zero if non-existent
129 * @param $nt Title to add.
130 * @param $len int, page size
131 * @param $redir bool, is redirect?
132 * @return integer
133 */
134 function addLinkObj( &$nt, $len = -1, $redirect = NULL ) {
135 global $wgMemc, $wgLinkCacheMemcached, $wgAntiLockFlags;
136 $title = $nt->getPrefixedDBkey();
137 if ( $this->isBadLink( $title ) ) { return 0; }
138 $id = $this->getGoodLinkID( $title );
139 if ( 0 != $id ) { return $id; }
140
141 $fname = 'LinkCache::addLinkObj';
142 global $wgProfiler;
143 if ( isset( $wgProfiler ) ) {
144 $fname .= ' (' . $wgProfiler->getCurrentSection() . ')';
145 }
146
147 wfProfileIn( $fname );
148
149 $ns = $nt->getNamespace();
150 $t = $nt->getDBkey();
151
152 if ( '' == $title ) {
153 wfProfileOut( $fname );
154 return 0;
155 }
156 # Some fields heavily used for linking...
157 $id = NULL;
158
159 if( $wgLinkCacheMemcached ) {
160 $id = $wgMemc->get( $key = $this->getKey( $title ) );
161 }
162 if( !is_integer( $id ) ) {
163 if ( $this->mForUpdate ) {
164 $db = wfGetDB( DB_MASTER );
165 if ( !( $wgAntiLockFlags & ALF_NO_LINK_LOCK ) ) {
166 $options = array( 'FOR UPDATE' );
167 } else {
168 $options = array();
169 }
170 } else {
171 $db = wfGetDB( DB_SLAVE );
172 $options = array();
173 }
174
175 $s = $db->selectRow( 'page',
176 array( 'page_id', 'page_len', 'page_is_redirect' ),
177 array( 'page_namespace' => $ns, 'page_title' => $t ),
178 $fname, $options );
179 # Set fields...
180 $id = $s ? $s->page_id : 0;
181 $len = $s ? $s->page_len : -1;
182 $redirect = $s ? $s->page_is_redirect : 0;
183
184 if( $wgLinkCacheMemcached ) {
185 $wgMemc->add( $key, $id, 3600*24 );
186 }
187 }
188
189 if( 0 == $id ) {
190 $this->addBadLinkObj( $nt );
191 } else {
192 $this->addGoodLinkObj( $id, $nt, $len, $redirect );
193 }
194 wfProfileOut( $fname );
195 return $id;
196 }
197
198 /**
199 * Clears cache
200 */
201 function clear() {
202 $this->mPageLinks = array();
203 $this->mGoodLinks = array();
204 $this->mGoodLinkFields = array();
205 $this->mBadLinks = array();
206 }
207 }
208