MediaWiki  master
GenderCache.php
Go to the documentation of this file.
1 <?php
27 
34 class GenderCache {
35  protected $cache = [];
36  protected $default;
37  protected $misses = 0;
38  protected $missLimit = 1000;
39 
41  private $nsInfo;
42 
44  private $loadBalancer;
45 
47  private $userOptionsLookup;
48 
49  public function __construct(
50  NamespaceInfo $nsInfo = null,
51  ILoadBalancer $loadBalancer = null,
52  UserOptionsLookup $userOptionsLookup = null
53  ) {
54  $this->nsInfo = $nsInfo ?? MediaWikiServices::getInstance()->getNamespaceInfo();
55  $this->loadBalancer = $loadBalancer;
56  $this->userOptionsLookup = $userOptionsLookup ?? MediaWikiServices::getInstance()->getUserOptionsLookup();
57  }
58 
63  protected function getDefault() {
64  if ( $this->default === null ) {
65  $this->default = $this->userOptionsLookup->getDefaultOption( 'gender' );
66  }
67 
68  return $this->default;
69  }
70 
77  public function getGenderOf( $username, $caller = '' ) {
78  if ( $username instanceof UserIdentity ) {
79  $username = $username->getName();
80  }
81 
82  $username = self::normalizeUsername( $username );
83  if ( !isset( $this->cache[$username] ) ) {
84  if ( $this->misses >= $this->missLimit &&
85  RequestContext::getMain()->getUser()->getName() !== $username
86  ) {
87  if ( $this->misses === $this->missLimit ) {
88  $this->misses++;
89  wfDebug( __METHOD__ . ": too many misses, returning default onwards" );
90  }
91 
92  return $this->getDefault();
93  } else {
94  $this->misses++;
95  $this->doQuery( $username, $caller );
96  }
97  }
98 
99  /* Undefined if there is a valid username which for some reason doesn't
100  * exist in the database.
101  */
102  return $this->cache[$username] ?? $this->getDefault();
103  }
104 
111  public function doLinkBatch( $data, $caller = '' ) {
112  $users = [];
113  foreach ( $data as $ns => $pagenames ) {
114  if ( !$this->nsInfo->hasGenderDistinction( $ns ) ) {
115  continue;
116  }
117  foreach ( array_keys( $pagenames ) as $username ) {
118  $users[$username] = true;
119  }
120  }
121 
122  $this->doQuery( array_keys( $users ), $caller );
123  }
124 
132  public function doTitlesArray( $titles, $caller = '' ) {
133  $users = [];
134  foreach ( $titles as $titleObj ) {
135  if ( !$this->nsInfo->hasGenderDistinction( $titleObj->getNamespace() ) ) {
136  continue;
137  }
138  $users[] = $titleObj->getText();
139  }
140 
141  $this->doQuery( $users, $caller );
142  }
143 
149  public function doQuery( $users, $caller = '' ) {
150  $default = $this->getDefault();
151 
152  $usersToCheck = [];
153  foreach ( (array)$users as $value ) {
154  $name = self::normalizeUsername( $value );
155  // Skip users whose gender setting we already know
156  if ( !isset( $this->cache[$name] ) ) {
157  // For existing users, this value will be overwritten by the correct value
158  $this->cache[$name] = $default;
159  // We no longer verify that only valid names are checked for, T267054
160  $usersToCheck[] = $name;
161  }
162  }
163 
164  if ( count( $usersToCheck ) === 0 ) {
165  return;
166  }
167 
168  // Only query database, when load balancer is provided by service wiring
169  // This maybe not happen when running as part of the installer
170  if ( $this->loadBalancer === null ) {
171  return;
172  }
173 
174  $dbr = $this->loadBalancer->getConnectionRef( DB_REPLICA );
175  $table = [ 'user', 'user_properties' ];
176  $fields = [ 'user_name', 'up_value' ];
177  $conds = [ 'user_name' => $usersToCheck ];
178  $joins = [ 'user_properties' =>
179  [ 'LEFT JOIN', [ 'user_id = up_user', 'up_property' => 'gender' ] ] ];
180 
181  $comment = __METHOD__;
182  if ( strval( $caller ) !== '' ) {
183  $comment .= "/$caller";
184  }
185  $res = $dbr->select( $table, $fields, $conds, $comment, [], $joins );
186 
187  foreach ( $res as $row ) {
188  $this->cache[$row->user_name] = $row->up_value ?: $default;
189  }
190  }
191 
192  private static function normalizeUsername( $username ) {
193  // Strip off subpages
194  $indexSlash = strpos( $username, '/' );
195  if ( $indexSlash !== false ) {
196  $username = substr( $username, 0, $indexSlash );
197  }
198 
199  // normalize underscore/spaces
200  return strtr( $username, '_', ' ' );
201  }
202 }
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:34
__construct(NamespaceInfo $nsInfo=null, ILoadBalancer $loadBalancer=null, UserOptionsLookup $userOptionsLookup=null)
Definition: GenderCache.php:49
getGenderOf( $username, $caller='')
Returns the gender for given username.
Definition: GenderCache.php:77
doLinkBatch( $data, $caller='')
Wrapper for doQuery that processes raw LinkBatch data.
getDefault()
Returns the default gender option in this wiki.
Definition: GenderCache.php:63
doTitlesArray( $titles, $caller='')
Wrapper for doQuery that processes a title array.
doQuery( $users, $caller='')
Preloads genders for given list of users.
Service locator for MediaWiki core services.
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.
Create and track the database connections and transactions for a given database cluster.
const DB_REPLICA
Definition: defines.php:26