Merge "Added a separate error message for mkdir failures"
[lhc/web/wiklou.git] / includes / cache / GenderCache.php
1 <?php
2 /**
3 * Caches user genders when needed to use correct namespace aliases.
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 * http://www.gnu.org/copyleft/gpl.html
19 *
20 * @file
21 * @author Niklas Laxström
22 * @ingroup Cache
23 */
24 use MediaWiki\MediaWikiServices;
25
26 /**
27 * Caches user genders when needed to use correct namespace aliases.
28 *
29 * @since 1.18
30 */
31 class GenderCache {
32 protected $cache = [];
33 protected $default;
34 protected $misses = 0;
35 protected $missLimit = 1000;
36
37 /**
38 * @deprecated in 1.28 see MediaWikiServices::getInstance()->getGenderCache()
39 * @return GenderCache
40 */
41 public static function singleton() {
42 return MediaWikiServices::getInstance()->getGenderCache();
43 }
44
45 /**
46 * Returns the default gender option in this wiki.
47 * @return string
48 */
49 protected function getDefault() {
50 if ( $this->default === null ) {
51 $this->default = User::getDefaultOption( 'gender' );
52 }
53
54 return $this->default;
55 }
56
57 /**
58 * Returns the gender for given username.
59 * @param string|User $username Username
60 * @param string $caller The calling method
61 * @return string
62 */
63 public function getGenderOf( $username, $caller = '' ) {
64 global $wgUser;
65
66 if ( $username instanceof User ) {
67 $username = $username->getName();
68 }
69
70 $username = self::normalizeUsername( $username );
71 if ( !isset( $this->cache[$username] ) ) {
72 if ( $this->misses >= $this->missLimit && $wgUser->getName() !== $username ) {
73 if ( $this->misses === $this->missLimit ) {
74 $this->misses++;
75 wfDebug( __METHOD__ . ": too many misses, returning default onwards\n" );
76 }
77
78 return $this->getDefault();
79 } else {
80 $this->misses++;
81 $this->doQuery( $username, $caller );
82 }
83 }
84
85 /* Undefined if there is a valid username which for some reason doesn't
86 * exist in the database.
87 */
88 return isset( $this->cache[$username] ) ? $this->cache[$username] : $this->getDefault();
89 }
90
91 /**
92 * Wrapper for doQuery that processes raw LinkBatch data.
93 *
94 * @param array $data
95 * @param string $caller
96 */
97 public function doLinkBatch( $data, $caller = '' ) {
98 $users = [];
99 foreach ( $data as $ns => $pagenames ) {
100 if ( !MWNamespace::hasGenderDistinction( $ns ) ) {
101 continue;
102 }
103 foreach ( array_keys( $pagenames ) as $username ) {
104 $users[$username] = true;
105 }
106 }
107
108 $this->doQuery( array_keys( $users ), $caller );
109 }
110
111 /**
112 * Wrapper for doQuery that processes a title or string array.
113 *
114 * @since 1.20
115 * @param array $titles Array of Title objects or strings
116 * @param string $caller The calling method
117 */
118 public function doTitlesArray( $titles, $caller = '' ) {
119 $users = [];
120 foreach ( $titles as $title ) {
121 $titleObj = is_string( $title ) ? Title::newFromText( $title ) : $title;
122 if ( !$titleObj ) {
123 continue;
124 }
125 if ( !MWNamespace::hasGenderDistinction( $titleObj->getNamespace() ) ) {
126 continue;
127 }
128 $users[] = $titleObj->getText();
129 }
130
131 $this->doQuery( $users, $caller );
132 }
133
134 /**
135 * Preloads genders for given list of users.
136 * @param array|string $users Usernames
137 * @param string $caller The calling method
138 */
139 public function doQuery( $users, $caller = '' ) {
140 $default = $this->getDefault();
141
142 $usersToCheck = [];
143 foreach ( (array)$users as $value ) {
144 $name = self::normalizeUsername( $value );
145 // Skip users whose gender setting we already know
146 if ( !isset( $this->cache[$name] ) ) {
147 // For existing users, this value will be overwritten by the correct value
148 $this->cache[$name] = $default;
149 // query only for valid names, which can be in the database
150 if ( User::isValidUserName( $name ) ) {
151 $usersToCheck[] = $name;
152 }
153 }
154 }
155
156 if ( count( $usersToCheck ) === 0 ) {
157 return;
158 }
159
160 $dbr = wfGetDB( DB_REPLICA );
161 $table = [ 'user', 'user_properties' ];
162 $fields = [ 'user_name', 'up_value' ];
163 $conds = [ 'user_name' => $usersToCheck ];
164 $joins = [ 'user_properties' =>
165 [ 'LEFT JOIN', [ 'user_id = up_user', 'up_property' => 'gender' ] ] ];
166
167 $comment = __METHOD__;
168 if ( strval( $caller ) !== '' ) {
169 $comment .= "/$caller";
170 }
171 $res = $dbr->select( $table, $fields, $conds, $comment, [], $joins );
172
173 foreach ( $res as $row ) {
174 $this->cache[$row->user_name] = $row->up_value ? $row->up_value : $default;
175 }
176 }
177
178 private static function normalizeUsername( $username ) {
179 // Strip off subpages
180 $indexSlash = strpos( $username, '/' );
181 if ( $indexSlash !== false ) {
182 $username = substr( $username, 0, $indexSlash );
183 }
184
185 // normalize underscore/spaces
186 return strtr( $username, '_', ' ' );
187 }
188 }