MediaWiki  master
GenderCache.php
Go to the documentation of this file.
1 <?php
30 
36 class GenderCache {
37  protected $cache = [];
38  protected $default;
39  protected $misses = 0;
40  protected $missLimit = 1000;
41 
43  private $nsInfo;
44 
46  private $loadBalancer;
47 
50 
51  public function __construct(
52  NamespaceInfo $nsInfo = null,
55  ) {
56  $this->nsInfo = $nsInfo ?? MediaWikiServices::getInstance()->getNamespaceInfo();
57  $this->loadBalancer = $loadBalancer;
58  $this->userOptionsLookup = $userOptionsLookup ?? MediaWikiServices::getInstance()->getUserOptionsLookup();
59  }
60 
65  protected function getDefault() {
66  if ( $this->default === null ) {
67  $this->default = $this->userOptionsLookup->getDefaultOption( 'gender' );
68  }
69 
70  return $this->default;
71  }
72 
79  public function getGenderOf( $username, $caller = '' ) {
80  if ( $username instanceof UserIdentity ) {
81  $username = $username->getName();
82  }
83 
84  $username = self::normalizeUsername( $username );
85  if ( !isset( $this->cache[$username] ) ) {
86  if ( $this->misses >= $this->missLimit &&
87  RequestContext::getMain()->getUser()->getName() !== $username
88  ) {
89  if ( $this->misses === $this->missLimit ) {
90  $this->misses++;
91  wfDebug( __METHOD__ . ": too many misses, returning default onwards" );
92  }
93 
94  return $this->getDefault();
95  } else {
96  $this->misses++;
97  $this->doQuery( $username, $caller );
98  }
99  }
100 
101  /* Undefined if there is a valid username which for some reason doesn't
102  * exist in the database.
103  */
104  return $this->cache[$username] ?? $this->getDefault();
105  }
106 
113  public function doLinkBatch( $data, $caller = '' ) {
114  $users = [];
115  foreach ( $data as $ns => $pagenames ) {
116  if ( !$this->nsInfo->hasGenderDistinction( $ns ) ) {
117  continue;
118  }
119  foreach ( array_keys( $pagenames ) as $username ) {
120  $users[$username] = true;
121  }
122  }
123 
124  $this->doQuery( array_keys( $users ), $caller );
125  }
126 
134  public function doTitlesArray( $titles, $caller = '' ) {
135  $users = [];
136  foreach ( $titles as $titleObj ) {
137  if ( !$this->nsInfo->hasGenderDistinction( $titleObj->getNamespace() ) ) {
138  continue;
139  }
140  $users[] = $titleObj->getText();
141  }
142 
143  $this->doQuery( $users, $caller );
144  }
145 
151  public function doQuery( $users, $caller = '' ) {
152  $default = $this->getDefault();
153 
154  $usersToCheck = [];
155  foreach ( (array)$users as $value ) {
156  $name = self::normalizeUsername( $value );
157  // Skip users whose gender setting we already know
158  if ( !isset( $this->cache[$name] ) ) {
159  // For existing users, this value will be overwritten by the correct value
160  $this->cache[$name] = $default;
161  // We no longer verify that only valid names are checked for, T267054
162  $usersToCheck[] = $name;
163  }
164  }
165 
166  if ( count( $usersToCheck ) === 0 ) {
167  return;
168  }
169 
170  // Only query database, when load balancer is provided by service wiring
171  // This maybe not happen when running as part of the installer
172  if ( $this->loadBalancer === null ) {
173  return;
174  }
175 
176  $dbr = $this->loadBalancer->getConnectionRef( DB_REPLICA );
177  $table = [ 'user', 'user_properties' ];
178  $fields = [ 'user_name', 'up_value' ];
179  $conds = [ 'user_name' => $usersToCheck ];
180  $joins = [ 'user_properties' =>
181  [ 'LEFT JOIN', [ 'user_id = up_user', 'up_property' => 'gender' ] ] ];
182 
183  $comment = __METHOD__;
184  if ( strval( $caller ) !== '' ) {
185  $comment .= "/$caller";
186  }
187  $res = $dbr->select( $table, $fields, $conds, $comment, [], $joins );
188 
189  foreach ( $res as $row ) {
190  $this->cache[$row->user_name] = $row->up_value ?: $default;
191  }
192  }
193 
194  private static function normalizeUsername( $username ) {
195  // Strip off subpages
196  $indexSlash = strpos( $username, '/' );
197  if ( $indexSlash !== false ) {
198  $username = substr( $username, 0, $indexSlash );
199  }
200 
201  // normalize underscore/spaces
202  return strtr( $username, '_', ' ' );
203  }
204 }
getUser()
wfDebug( $text, $dest='all', array $context=[])
Sends a line to the debug log if enabled or, optionally, to a comment in output.
Caches user genders when needed to use correct namespace aliases.
Definition: GenderCache.php:36
__construct(NamespaceInfo $nsInfo=null, ILoadBalancer $loadBalancer=null, UserOptionsLookup $userOptionsLookup=null)
Definition: GenderCache.php:51
getGenderOf( $username, $caller='')
Returns the gender for given username.
Definition: GenderCache.php:79
static normalizeUsername( $username)
NamespaceInfo $nsInfo
Definition: GenderCache.php:43
UserOptionsLookup $userOptionsLookup
Definition: GenderCache.php:49
doLinkBatch( $data, $caller='')
Wrapper for doQuery that processes raw LinkBatch data.
getDefault()
Returns the default gender option in this wiki.
Definition: GenderCache.php:65
ILoadBalancer null $loadBalancer
Definition: GenderCache.php:46
doTitlesArray( $titles, $caller='')
Wrapper for doQuery that processes a title array.
doQuery( $users, $caller='')
Preloads genders for given list of users.
MediaWikiServices is the service locator for the application scope of MediaWiki.
Provides access to user options.
This is a utility class for dealing with namespaces that encodes all the "magic" behaviors of them ba...
static getMain()
Get the RequestContext object associated with the main request.
Interface for objects representing user identity.
Database cluster connection, tracking, load balancing, and transaction manager interface.
const DB_REPLICA
Definition: defines.php:25