MediaWiki  master
GenderCache.php
Go to the documentation of this file.
1 <?php
27 
33 class GenderCache {
34  protected $cache = [];
35  protected $default;
36  protected $misses = 0;
37  protected $missLimit = 1000;
38 
40  private $nsInfo;
41 
43  private $loadBalancer;
44 
45  public function __construct( NamespaceInfo $nsInfo = null, ILoadBalancer $loadBalancer = null ) {
46  $this->nsInfo = $nsInfo ?? MediaWikiServices::getInstance()->getNamespaceInfo();
47  $this->loadBalancer = $loadBalancer;
48  }
49 
54  public static function singleton() {
55  return MediaWikiServices::getInstance()->getGenderCache();
56  }
57 
62  protected function getDefault() {
63  if ( $this->default === null ) {
64  $this->default = User::getDefaultOption( 'gender' );
65  }
66 
67  return $this->default;
68  }
69 
76  public function getGenderOf( $username, $caller = '' ) {
77  global $wgUser;
78 
79  if ( $username instanceof User ) {
80  $username = $username->getName();
81  }
82 
83  $username = self::normalizeUsername( $username );
84  if ( !isset( $this->cache[$username] ) ) {
85  if ( $this->misses >= $this->missLimit && $wgUser->getName() !== $username ) {
86  if ( $this->misses === $this->missLimit ) {
87  $this->misses++;
88  wfDebug( __METHOD__ . ": too many misses, returning default onwards\n" );
89  }
90 
91  return $this->getDefault();
92  } else {
93  $this->misses++;
94  $this->doQuery( $username, $caller );
95  }
96  }
97 
98  /* Undefined if there is a valid username which for some reason doesn't
99  * exist in the database.
100  */
101  return $this->cache[$username] ?? $this->getDefault();
102  }
103 
110  public function doLinkBatch( $data, $caller = '' ) {
111  $users = [];
112  foreach ( $data as $ns => $pagenames ) {
113  if ( !$this->nsInfo->hasGenderDistinction( $ns ) ) {
114  continue;
115  }
116  foreach ( array_keys( $pagenames ) as $username ) {
117  $users[$username] = true;
118  }
119  }
120 
121  $this->doQuery( array_keys( $users ), $caller );
122  }
123 
131  public function doTitlesArray( $titles, $caller = '' ) {
132  $users = [];
133  foreach ( $titles as $title ) {
134  $titleObj = is_string( $title ) ? Title::newFromText( $title ) : $title;
135  if ( !$titleObj ) {
136  continue;
137  }
138  if ( !$this->nsInfo->hasGenderDistinction( $titleObj->getNamespace() ) ) {
139  continue;
140  }
141  $users[] = $titleObj->getText();
142  }
143 
144  $this->doQuery( $users, $caller );
145  }
146 
152  public function doQuery( $users, $caller = '' ) {
153  $default = $this->getDefault();
154 
155  $usersToCheck = [];
156  foreach ( (array)$users as $value ) {
157  $name = self::normalizeUsername( $value );
158  // Skip users whose gender setting we already know
159  if ( !isset( $this->cache[$name] ) ) {
160  // For existing users, this value will be overwritten by the correct value
161  $this->cache[$name] = $default;
162  // query only for valid names, which can be in the database
163  if ( User::isValidUserName( $name ) ) {
164  $usersToCheck[] = $name;
165  }
166  }
167  }
168 
169  if ( count( $usersToCheck ) === 0 ) {
170  return;
171  }
172 
173  // Only query database, when load balancer is provided by service wiring
174  // This maybe not happen when running as part of the installer
175  if ( $this->loadBalancer === null ) {
176  return;
177  }
178 
179  $dbr = $this->loadBalancer->getConnectionRef( DB_REPLICA );
180  $table = [ 'user', 'user_properties' ];
181  $fields = [ 'user_name', 'up_value' ];
182  $conds = [ 'user_name' => $usersToCheck ];
183  $joins = [ 'user_properties' =>
184  [ 'LEFT JOIN', [ 'user_id = up_user', 'up_property' => 'gender' ] ] ];
185 
186  $comment = __METHOD__;
187  if ( strval( $caller ) !== '' ) {
188  $comment .= "/$caller";
189  }
190  $res = $dbr->select( $table, $fields, $conds, $comment, [], $joins );
191 
192  foreach ( $res as $row ) {
193  $this->cache[$row->user_name] = $row->up_value ?: $default;
194  }
195  }
196 
197  private static function normalizeUsername( $username ) {
198  // Strip off subpages
199  $indexSlash = strpos( $username, '/' );
200  if ( $indexSlash !== false ) {
201  $username = substr( $username, 0, $indexSlash );
202  }
203 
204  // normalize underscore/spaces
205  return strtr( $username, '_', ' ' );
206  }
207 }
ILoadBalancer null $loadBalancer
Definition: GenderCache.php:43
static singleton()
Definition: GenderCache.php:54
doLinkBatch( $data, $caller='')
Wrapper for doQuery that processes raw LinkBatch data.
doTitlesArray( $titles, $caller='')
Wrapper for doQuery that processes a title or string array.
getDefault()
Returns the default gender option in this wiki.
Definition: GenderCache.php:62
The User object encapsulates all of the user-specific settings (user_id, name, rights, email address, options, last login time).
Definition: User.php:51
getGenderOf( $username, $caller='')
Returns the gender for given username.
Definition: GenderCache.php:76
static normalizeUsername( $username)
wfDebug( $text, $dest='all', array $context=[])
Sends a line to the debug log if enabled or, optionally, to a comment in output.
doQuery( $users, $caller='')
Preloads genders for given list of users.
__construct(NamespaceInfo $nsInfo=null, ILoadBalancer $loadBalancer=null)
Definition: GenderCache.php:45
NamespaceInfo $nsInfo
Definition: GenderCache.php:40
static getDefaultOption( $opt)
Get a given default option value.
Definition: User.php:1747
Database cluster connection, tracking, load balancing, and transaction manager interface.
static isValidUserName( $name)
Is the input a valid username?
Definition: User.php:973
const DB_REPLICA
Definition: defines.php:25
static newFromText( $text, $defaultNamespace=NS_MAIN)
Create a new Title from text, such as what one would find in a link.
Definition: Title.php:317