$this->readOnlyReason = $conf['readOnlyReason'];
}
- $this->srvCache = isset( $conf['srvCache'] ) ? $conf['srvCache'] : new EmptyBagOStuff();
- $this->memStash = isset( $conf['memStash'] ) ? $conf['memStash'] : new EmptyBagOStuff();
- $this->wanCache = isset( $conf['wanCache'] )
- ? $conf['wanCache']
- : WANObjectCache::newEmpty();
+ $this->srvCache = $conf['srvCache'] ?? new EmptyBagOStuff();
+ $this->memStash = $conf['memStash'] ?? new EmptyBagOStuff();
+ $this->wanCache = $conf['wanCache'] ?? WANObjectCache::newEmpty();
foreach ( self::$loggerFields as $key ) {
- $this->$key = isset( $conf[$key] ) ? $conf[$key] : new \Psr\Log\NullLogger();
+ $this->$key = $conf[$key] ?? new \Psr\Log\NullLogger();
}
- $this->errorLogger = isset( $conf['errorLogger'] )
- ? $conf['errorLogger']
- : function ( Exception $e ) {
- trigger_error( get_class( $e ) . ': ' . $e->getMessage(), E_USER_WARNING );
- };
- $this->deprecationLogger = isset( $conf['deprecationLogger'] )
- ? $conf['deprecationLogger']
- : function ( $msg ) {
- trigger_error( $msg, E_USER_DEPRECATED );
- };
-
- $this->profiler = isset( $conf['profiler'] ) ? $conf['profiler'] : null;
- $this->trxProfiler = isset( $conf['trxProfiler'] )
- ? $conf['trxProfiler']
- : new TransactionProfiler();
+ $this->errorLogger = $conf['errorLogger'] ?? function ( Exception $e ) {
+ trigger_error( get_class( $e ) . ': ' . $e->getMessage(), E_USER_WARNING );
+ };
+ $this->deprecationLogger = $conf['deprecationLogger'] ?? function ( $msg ) {
+ trigger_error( $msg, E_USER_DEPRECATED );
+ };
+
+ $this->profiler = $conf['profiler'] ?? null;
+ $this->trxProfiler = $conf['trxProfiler'] ?? new TransactionProfiler();
$this->requestInfo = [
- 'IPAddress' => isset( $_SERVER[ 'REMOTE_ADDR' ] ) ? $_SERVER[ 'REMOTE_ADDR' ] : '',
- 'UserAgent' => isset( $_SERVER['HTTP_USER_AGENT'] ) ? $_SERVER['HTTP_USER_AGENT'] : '',
- 'ChronologyProtection' => 'true',
- 'ChronologyPositionIndex' => isset( $_GET['cpPosIndex'] ) ? $_GET['cpPosIndex'] : null
+ 'IPAddress' => $_SERVER[ 'REMOTE_ADDR' ] ?? '',
+ 'UserAgent' => $_SERVER['HTTP_USER_AGENT'] ?? '',
+ // Headers application can inject via LBFactory::setRequestInfo()
+ 'ChronologyClientId' => null, // prior $cpClientId value from LBFactory::shutdown()
+ 'ChronologyPositionIndex' => null // prior $cpIndex value from LBFactory::shutdown()
];
- $this->cliMode = isset( $conf['cliMode'] )
- ? $conf['cliMode']
- : ( PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg' );
- $this->hostname = isset( $conf['hostname'] ) ? $conf['hostname'] : gethostname();
- $this->agent = isset( $conf['agent'] ) ? $conf['agent'] : '';
+ $this->cliMode = $conf['cliMode'] ?? ( PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg' );
+ $this->hostname = $conf['hostname'] ?? gethostname();
+ $this->agent = $conf['agent'] ?? '';
$this->ticket = mt_rand();
}
}
public function shutdown(
- $mode = self::SHUTDOWN_CHRONPROT_SYNC, callable $workCallback = null, &$cpIndex = null
+ $mode = self::SHUTDOWN_CHRONPROT_SYNC,
+ callable $workCallback = null,
+ &$cpIndex = null,
+ &$cpClientId = null
) {
$chronProt = $this->getChronologyProtector();
if ( $mode === self::SHUTDOWN_CHRONPROT_SYNC ) {
$this->shutdownChronologyProtector( $chronProt, null, 'async', $cpIndex );
}
+ $cpClientId = $chronProt->getClientId();
+
$this->commitMasterChanges( __METHOD__ ); // sanity
}
/**
* @see ILBFactory::newMainLB()
* @param bool $domain
- * @return LoadBalancer
+ * @return ILoadBalancer
*/
abstract public function newMainLB( $domain = false );
/**
* @see ILBFactory::getMainLB()
* @param bool $domain
- * @return LoadBalancer
+ * @return ILoadBalancer
*/
abstract public function getMainLB( $domain = false );
/**
* @see ILBFactory::newExternalLB()
* @param string $cluster
- * @return LoadBalancer
+ * @return ILoadBalancer
*/
abstract public function newExternalLB( $cluster );
/**
* @see ILBFactory::getExternalLB()
* @param string $cluster
- * @return LoadBalancer
+ * @return ILoadBalancer
*/
abstract public function getExternalLB( $cluster );
[
'ip' => $this->requestInfo['IPAddress'],
'agent' => $this->requestInfo['UserAgent'],
+ 'clientId' => $this->requestInfo['ChronologyClientId']
],
$this->requestInfo['ChronologyPositionIndex']
);
}
/**
- * Base parameters to LoadBalancer::__construct()
+ * Base parameters to ILoadBalancer::__construct()
* @return array
*/
final protected function baseLoadBalancerParams() {
return strpos( $url, '?' ) === false ? "$url?cpPosIndex=$index" : "$url&cpPosIndex=$index";
}
+ /**
+ * @param int $index Write index
+ * @param int $time UNIX timestamp; can be used to detect stale cookies (T190082)
+ * @param string $clientId Agent ID hash from ILBFactory::shutdown()
+ * @return string Timestamp-qualified write index of the form "<index>@<timestamp>#<hash>"
+ * @since 1.32
+ */
+ public static function makeCookieValueFromCPIndex( $index, $time, $clientId ) {
+ return "$index@$time#$clientId";
+ }
+
+ /**
+ * @param string $value Possible result of LBFactory::makeCookieValueFromCPIndex()
+ * @param int $minTimestamp Lowest UNIX timestamp of non-expired values (if present)
+ * @return array (index: int or null, clientId: string or null)
+ * @since 1.32
+ */
+ public static function getCPInfoFromCookieValue( $value, $minTimestamp ) {
+ static $placeholder = [ 'index' => null, 'clientId' => null ];
+
+ if ( !preg_match( '/^(\d+)(?:@(\d+))?(?:#([0-9a-f]{32}))?$/', $value, $m ) ) {
+ return $placeholder; // invalid
+ }
+
+ $index = (int)$m[1];
+ if ( $index <= 0 ) {
+ return $placeholder; // invalid
+ } elseif ( isset( $m[2] ) && $m[2] !== '' && (int)$m[2] < $minTimestamp ) {
+ return $placeholder; // expired
+ }
+
+ $clientId = ( isset( $m[3] ) && $m[3] !== '' ) ? $m[3] : null;
+
+ return [ 'index' => $index, 'clientId' => $clientId ];
+ }
+
public function setRequestInfo( array $info ) {
if ( $this->chronProt ) {
throw new LogicException( 'ChronologyProtector already initialized.' );
}
}
+/**
+ * @deprecated since 1.29
+ */
class_alias( LBFactory::class, 'LBFactory' );