public function execute() { $users = 10; // For number of translations, limited to [0,20] $mean = 15; $stddev = 20; $stash = new TranslationStashStorage(wfGetDB(DB_MASTER)); $languages = array_keys(Language::fetchLanguageNames()); for ($i = 0; $i < $users; $i++) { $username = '******' . wfRandomString(6); $password = wfRandomString(12); $email = "{$username}.{$password}@blackhole.io"; $user = TranslateSandbox::addUser($username, $password, $email); $language = $languages[rand(0, count($languages) - 1)]; $count = wfGaussMs($mean, $stddev); $count = min(20, $count); $count = max(0, $count); for ($j = 0; $j < $count; $j++) { $title = Title::makeTitle(NS_MEDIAWIKI, wfRandomString(24) . '/' . $language); $value = array('Pupu söi'); for ($k = rand(0, 20); $k > 0; $k--) { $value[] = wfRandomString(rand(1, 28)); } $value = implode("\n", $value); $translation = new StashedTranslation($user, $title, $value); $stash->addTranslation($translation); } } }
/** * Make a new temporary file on the file system. * Temporary files may be purged when the file object falls out of scope. * * @param $prefix string * @param $extension string * @return TempFSFile|null */ public static function factory($prefix, $extension = '') { wfProfileIn(__METHOD__); $base = wfTempDir() . '/' . $prefix . wfRandomString(12); $ext = $extension != '' ? ".{$extension}" : ""; for ($attempt = 1; true; $attempt++) { $path = "{$base}-{$attempt}{$ext}"; wfSuppressWarnings(); $newFileHandle = fopen($path, 'x'); wfRestoreWarnings(); if ($newFileHandle) { fclose($newFileHandle); break; // got it } if ($attempt >= 5) { wfProfileOut(__METHOD__); return null; // give up } } $tmpFile = new self($path); $tmpFile->canDelete = true; // safely instantiated wfProfileOut(__METHOD__); return $tmpFile; }
/** * @param array[] $markers Markers generated by another instance of TextInjector, * for use by inject(); a map of string markers associated with * parameter arrays. */ public function __construct(array $markers = array()) { $this->markers = $markers; // idea stolen from Parser class in core $this->uniqPrefix = "UNIQ" . wfRandomString(16); $this->markerIndex = 0; }
public function testForAuthentication(array $reqs) { $req = AuthenticationRequest::getRequestByClass($reqs, PasswordAuthenticationRequest::class); if ($req) { $user = User::newFromName($req->username); $password = $req->password; } else { $user = null; foreach ($reqs as $req) { if ($req->username !== null) { $user = User::newFromName($req->username); break; } } if (!$user) { $this->logger->debug(__METHOD__ . ': No username in $reqs, skipping hooks'); return StatusValue::newGood(); } // Something random for the 'AbortLogin' hook. $password = wfRandomString(32); } $msg = null; if (!\Hooks::run('LoginUserMigrated', [$user, &$msg])) { return $this->makeFailResponse($user, null, LoginForm::USER_MIGRATED, $msg, 'LoginUserMigrated'); } $abort = LoginForm::ABORTED; $msg = null; if (!\Hooks::run('AbortLogin', [$user, $password, &$abort, &$msg])) { return $this->makeFailResponse($user, null, $abort, $msg, 'AbortLogin'); } return StatusValue::newGood(); }
/** * @covers ReplicatedBagOStuff::get */ public function testGetAbsent() { $key = wfRandomString(); $value = wfRandomString(); $this->writeCache->set($key, $value); // Don't read from master. No failover if value is absent. $this->assertEquals($this->cache->get($key), false); }
public function execute() { global $wgCaptchaSecret, $wgCaptchaDirectoryLevels; $instance = ConfirmEditHooks::getInstance(); if (!$instance instanceof FancyCaptcha) { $this->error("\$wgCaptchaClass is not FancyCaptcha.\n", 1); } $backend = $instance->getBackend(); $countAct = $instance->estimateCaptchaCount(); $this->output("Estimated number of captchas is {$countAct}.\n"); $countGen = (int) $this->getOption('fill') - $countAct; if ($countGen <= 0) { $this->output("No need to generate anymore captchas.\n"); return; } $tmpDir = wfTempDir() . '/mw-fancycaptcha-' . time() . '-' . wfRandomString(6); if (!wfMkdirParents($tmpDir)) { $this->error("Could not create temp directory.\n", 1); } $e = null; // exception try { $cmd = sprintf("python %s --key %s --output %s --count %s --dirs %s", wfEscapeShellArg(__DIR__ . '/../captcha.py'), wfEscapeShellArg($wgCaptchaSecret), wfEscapeShellArg($tmpDir), wfEscapeShellArg($countGen), wfEscapeShellArg($wgCaptchaDirectoryLevels)); foreach (array('wordlist', 'font', 'font-size', 'blacklist', 'verbose') as $par) { if ($this->hasOption($par)) { $cmd .= " --{$par} " . wfEscapeShellArg($this->getOption($par)); } } $this->output("Generating {$countGen} new captchas...\n"); $retVal = 1; wfShellExec($cmd, $retVal, array(), array('time' => 0)); if ($retVal != 0) { wfRecursiveRemoveDir($tmpDir); $this->error("Could not run generation script.\n", 1); } $flags = FilesystemIterator::SKIP_DOTS; $iter = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($tmpDir, $flags), RecursiveIteratorIterator::CHILD_FIRST); $this->output("Copying the new captchas to storage...\n"); foreach ($iter as $fileInfo) { if (!$fileInfo->isFile()) { continue; } list($salt, $hash) = $instance->hashFromImageName($fileInfo->getBasename()); $dest = $instance->imagePath($salt, $hash); $backend->prepare(array('dir' => dirname($dest))); $status = $backend->quickStore(array('src' => $fileInfo->getPathname(), 'dst' => $dest)); if (!$status->isOK()) { $this->error("Could not save file '{$fileInfo->getPathname()}'.\n"); } } } catch (Exception $e) { wfRecursiveRemoveDir($tmpDir); throw $e; } $this->output("Removing temporary files...\n"); wfRecursiveRemoveDir($tmpDir); $this->output("Done.\n"); }
protected function doGetLocalCopyMulti(array $params) { $tmpFiles = array(); // (path => MockFSFile) foreach ($params['srcs'] as $src) { $tmpFiles[$src] = new MockFSFile(wfTempDir() . '/' . wfRandomString(32)); } return $tmpFiles; }
/** * @covers GlobalVarConfig::has */ public function testHas() { $this->maybeStashGlobal('wgGlobalVarConfigTestHas'); $GLOBALS['wgGlobalVarConfigTestHas'] = wfRandomString(); $this->maybeStashGlobal('wgGlobalVarConfigTestNotHas'); $config = new GlobalVarConfig(); $this->assertTrue($config->has('GlobalVarConfigTestHas')); $this->assertFalse($config->has('GlobalVarConfigTestNotHas')); }
/** * @dataProvider provideSet * @covers GlobalVarConfig::set * @covers GlobalVarConfig::setWithPrefix */ public function testSet($name, $prefix, $var) { $this->maybeStashGlobal($var); $config = new GlobalVarConfig($prefix); $random = wfRandomString(); $config->set($name, $random); $this->assertArrayHasKey($var, $GLOBALS); $this->assertEquals($random, $GLOBALS[$var]); }
/** * Construct a new instance from configuration. * * @param array $config Parameters include: * - lockServers : Associative array of server names to "<IP>:<port>" strings. * - srvsByBucket : Array of 1-16 consecutive integer keys, starting from 0, * each having an odd-numbered list of server names (peers) as values. * - redisConfig : Configuration for RedisConnectionPool::__construct(). * @throws Exception */ public function __construct(array $config) { parent::__construct($config); $this->lockServers = $config['lockServers']; // Sanitize srvsByBucket config to prevent PHP errors $this->srvsByBucket = array_filter($config['srvsByBucket'], 'is_array'); $this->srvsByBucket = array_values($this->srvsByBucket); // consecutive $config['redisConfig']['serializer'] = 'none'; $this->redisPool = RedisConnectionPool::singleton($config['redisConfig']); $this->session = wfRandomString(32); }
public function testSetDelayed() { $key = wfRandomString(); $value = wfRandomString(); // XXX: DeferredUpdates bound to transactions in CLI mode $dbw = wfGetDB(DB_MASTER); $dbw->begin(); $this->cache->set($key, $value); // Set in tier 1 $this->assertEquals($value, $this->cache1->get($key), 'Written to tier 1'); // Not yet set in tier 2 $this->assertEquals(false, $this->cache2->get($key), 'Not written to tier 2'); $dbw->commit(); // Set in tier 2 $this->assertEquals($value, $this->cache2->get($key), 'Written to tier 2'); }
/** * Construct a new instance from configuration. * * $config paramaters include: * - lockServers : Associative array of server names to configuration. * Configuration is an associative array that includes: * - host : IP address/hostname * - port : TCP port * - authKey : Secret string the lock server uses * - srvsByBucket : Array of 1-16 consecutive integer keys, starting from 0, * each having an odd-numbered list of server names (peers) as values. * - connTimeout : Lock server connection attempt timeout. [optional] * * @param Array $config */ public function __construct(array $config) { parent::__construct($config); $this->lockServers = $config['lockServers']; // Sanitize srvsByBucket config to prevent PHP errors $this->srvsByBucket = array_filter($config['srvsByBucket'], 'is_array'); $this->srvsByBucket = array_values($this->srvsByBucket); // consecutive if (isset($config['connTimeout'])) { $this->connTimeout = $config['connTimeout']; } else { $this->connTimeout = 3; // use some sane amount } $this->session = wfRandomString(32); // 128 bits }
/** * Construct a new instance from configuration. * * @param array $config Parameters include: * - lockServers : Associative array of server names to "<IP>:<port>" strings. * - srvsByBucket : Array of 1-16 consecutive integer keys, starting from 0, * each having an odd-numbered list of server names (peers) as values. * - memcConfig : Configuration array for ObjectCache::newFromParams. [optional] * If set, this must use one of the memcached classes. * @throws Exception */ public function __construct(array $config) { parent::__construct($config); // Sanitize srvsByBucket config to prevent PHP errors $this->srvsByBucket = array_filter($config['srvsByBucket'], 'is_array'); $this->srvsByBucket = array_values($this->srvsByBucket); // consecutive $memcConfig = isset($config['memcConfig']) ? $config['memcConfig'] : ['class' => 'MemcachedPhpBagOStuff']; foreach ($config['lockServers'] as $name => $address) { $params = ['servers' => [$address]] + $memcConfig; $cache = ObjectCache::newFromParams($params); if ($cache instanceof MemcachedBagOStuff) { $this->bagOStuffs[$name] = $cache; } else { throw new Exception('Only MemcachedBagOStuff classes are supported by MemcLockManager.'); } } $this->session = wfRandomString(32); }
/** * Make a new temporary file on the file system. * Temporary files may be purged when the file object falls out of scope. * * @param string $prefix * @param string $extension * @return TempFSFile|null */ public static function factory($prefix, $extension = '') { $ext = $extension != '' ? ".{$extension}" : ''; $attempts = 5; while ($attempts--) { $path = wfTempDir() . '/' . $prefix . wfRandomString(12) . $ext; MediaWiki\suppressWarnings(); $newFileHandle = fopen($path, 'x'); MediaWiki\restoreWarnings(); if ($newFileHandle) { fclose($newFileHandle); $tmpFile = new self($path); $tmpFile->autocollect(); // Safely instantiated, end loop. return $tmpFile; } } // Give up return null; }
public function execute() { $user = User::newFromName($this->getOption('user')); if (!$user->getId()) { $this->error("No such user exists.", 1); } $count = $this->getOption('count'); $namespace = (int) $this->getOption('namespace', 0); for ($i = 0; $i < $count; ++$i) { $title = Title::makeTitleSafe($namespace, "Page " . wfRandomString(2)); $page = WikiPage::factory($title); $content = ContentHandler::makeContent(wfRandomString(), $title); $summary = "Change " . wfRandomString(6); $page->doEditContent($content, $summary, 0, false, $user); $this->output("Edited {$title}\n"); if ($i && $i % $this->mBatchSize == 0) { wfWaitForSlaves(); } } $this->output("Done\n"); }
/** * Get a TestUser object that the caller may not modify. * * Whenever possible, unit tests should use immutable users, because * immutable users can be reused in multiple tests, which helps keep * the unit tests fast. * * @since 1.28 * * @param string[] $groups Groups the test user should be added to. * @return TestUser */ public static function getImmutableTestUser($groups = []) { $groups = array_unique($groups); sort($groups); $key = implode(',', $groups); $testUser = isset(self::$testUsers[$key]) ? self::$testUsers[$key] : false; if (!$testUser || !$testUser->getUser()->isLoggedIn()) { $id = self::getNextId(); // Hack! If this is the primary sysop account, make the username // be 'UTSysop', for back-compat, and for the sake of PHPUnit data // provider methods, which are executed before the test database // is set up. See T136348. if ($groups === ['bureaucrat', 'sysop']) { $username = '******'; $password = '******'; } else { $username = "******"; $password = wfRandomString(20); } self::$testUsers[$key] = $testUser = new TestUser($username, "Name {$id}", "{$id}@mediawiki.test", $groups, $password); } $testUser->getUser()->clearInstanceCache(); return self::$testUsers[$key]; }
/** * Make a new temporary file on the file system. * Temporary files may be purged when the file object falls out of scope. * * @param string $prefix * @param string $extension * @return TempFSFile|null */ public static function factory($prefix, $extension = '') { $base = wfTempDir() . '/' . $prefix . wfRandomString(12); $ext = $extension != '' ? ".{$extension}" : ""; for ($attempt = 1; true; $attempt++) { $path = "{$base}-{$attempt}{$ext}"; MediaWiki\suppressWarnings(); $newFileHandle = fopen($path, 'x'); MediaWiki\restoreWarnings(); if ($newFileHandle) { fclose($newFileHandle); break; // got it } if ($attempt >= 5) { return null; // give up } } $tmpFile = new self($path); $tmpFile->autocollect(); // safely instantiated return $tmpFile; }
/** * Get the unique request ID. * This is either the value of the UNIQUE_ID envvar (if present) or a * randomly-generated 24-character string. * * @return string * @since 1.27 */ public static function getRequestId() { if (!self::$reqId) { self::$reqId = isset($_SERVER['UNIQUE_ID']) ? $_SERVER['UNIQUE_ID'] : wfRandomString(24); } return self::$reqId; }
/** * @param ImportSource $source * @return string */ static function registerSource(ImportSource $source) { $id = wfRandomString(); self::$sourceRegistrations[$id] = $source; return $id; }
/** * Get the ID for this exception. * * The ID is saved so that one can match the one output to the user (when * $wgShowExceptionDetails is set to false), to the entry in the debug log. * * @since 1.22 * @param Exception $e * @return string */ public static function getLogId(Exception $e) { if (!isset($e->_mwLogId)) { $e->_mwLogId = wfRandomString(8); } return $e->_mwLogId; }
/** * Dual purpose callback used as both a set_error_handler() callback and * a registered shutdown function. Receive a callback from the interpreter * for a raised error or system shutdown, check for a fatal error, and log * to the 'fatal' logging channel. * * Special handling is included for missing class errors as they may * indicate that the user needs to install 3rd-party libraries via * Composer or other means. * * @since 1.25 * * @param int $level Error level raised * @param string $message Error message * @param string $file File that error was raised in * @param int $line Line number error was raised at * @param array $context Active symbol table point of error * @param array $trace Backtrace at point of error (undocumented HHVM * feature) * @return bool Always returns false */ public static function handleFatalError($level = null, $message = null, $file = null, $line = null, $context = null, $trace = null) { // Free reserved memory so that we have space to process OOM // errors self::$reservedMemory = null; if ($level === null) { // Called as a shutdown handler, get data from error_get_last() if (static::$handledFatalCallback) { // Already called once (probably as an error handler callback // under HHVM) so don't log again. return false; } $lastError = error_get_last(); if ($lastError !== null) { $level = $lastError['type']; $message = $lastError['message']; $file = $lastError['file']; $line = $lastError['line']; } else { $level = 0; $message = ''; } } if (!in_array($level, self::$fatalErrorTypes)) { // Only interested in fatal errors, others should have been // handled by MWExceptionHandler::handleError return false; } $msg = "[{exception_id}] PHP Fatal Error: {$message}"; // Look at message to see if this is a class not found failure // HHVM: Class undefined: foo // PHP5: Class 'foo' not found if (preg_match("/Class (undefined: \\w+|'\\w+' not found)/", $msg)) { // @codingStandardsIgnoreStart Generic.Files.LineLength.TooLong $msg = <<<TXT {$msg} MediaWiki or an installed extension requires this class but it is not embedded directly in MediaWiki's git repository and must be installed separately by the end user. Please see <a href="https://www.mediawiki.org/wiki/Download_from_Git#Fetch_external_libraries">mediawiki.org</a> for help on installing the required components. TXT; // @codingStandardsIgnoreEnd } // We can't just create an exception and log it as it is likely that // the interpreter has unwound the stack already. If that is true the // stacktrace we would get would be functionally empty. If however we // have been called as an error handler callback *and* HHVM is in use // we will have been provided with a useful stacktrace that we can // log. $trace = $trace ?: debug_backtrace(); $logger = LoggerFactory::getInstance('fatal'); $logger->error($msg, ['exception' => ['class' => 'ErrorException', 'message' => "PHP Fatal Error: {$message}", 'code' => $level, 'file' => $file, 'line' => $line, 'trace' => static::redactTrace($trace)], 'exception_id' => wfRandomString(8)]); // Remember call so we don't double process via HHVM's fatal // notifications and the shutdown hook behavior static::$handledFatalCallback = true; return false; }
/** * Get a random string * * @return string */ public static function getRandomString() { return wfRandomString(16); }
/** * Get a random string * * @return string * @deprecated since 1.26; use wfRandomString() instead. */ public static function getRandomString() { wfDeprecated(__METHOD__, '1.26'); return wfRandomString(16); }
/** * @dataProvider provider_queueLists * @covers JobQueue */ public function testRootDeduplication($queue, $recycles, $desc) { $queue = $this->{$queue}; if (!$queue) { $this->markTestSkipped($desc); } $this->assertTrue($queue->isEmpty(), "Queue is empty ({$desc})"); $queue->flushCaches(); $this->assertEquals(0, $queue->getSize(), "Queue is empty ({$desc})"); $this->assertEquals(0, $queue->getAcquiredCount(), "Queue is empty ({$desc})"); $id = wfRandomString(32); $root1 = Job::newRootJobParams("nulljobspam:{$id}"); // task ID/timestamp for ($i = 0; $i < 5; ++$i) { $this->assertNull($queue->push($this->newJob(0, $root1)), "Push worked ({$desc})"); } $queue->deduplicateRootJob($this->newJob(0, $root1)); $root2 = $root1; # Add a second to UNIX epoch and format back to TS_MW $root2_ts = strtotime($root2['rootJobTimestamp']); $root2_ts++; $root2['rootJobTimestamp'] = wfTimestamp(TS_MW, $root2_ts); $this->assertNotEquals($root1['rootJobTimestamp'], $root2['rootJobTimestamp'], "Root job signatures have different timestamps."); for ($i = 0; $i < 5; ++$i) { $this->assertNull($queue->push($this->newJob(0, $root2)), "Push worked ({$desc})"); } $queue->deduplicateRootJob($this->newJob(0, $root2)); $this->assertFalse($queue->isEmpty(), "Queue is not empty ({$desc})"); $queue->flushCaches(); $this->assertEquals(10, $queue->getSize(), "Queue size is correct ({$desc})"); $this->assertEquals(0, $queue->getAcquiredCount(), "No jobs active ({$desc})"); $dupcount = 0; $jobs = []; do { $job = $queue->pop(); if ($job) { $jobs[] = $job; $queue->ack($job); } if ($job instanceof DuplicateJob) { ++$dupcount; } } while ($job); $this->assertEquals(10, count($jobs), "Correct number of jobs popped ({$desc})"); $this->assertEquals(5, $dupcount, "Correct number of duplicate jobs popped ({$desc})"); }
/** * @see JobQueue::doPop() * @return Job|bool */ protected function doPop() { $dbw = $this->getMasterDB(); try { $dbw->commit(__METHOD__, 'flush'); // flush existing transaction $autoTrx = $dbw->getFlag(DBO_TRX); // get current setting $dbw->clearFlag(DBO_TRX); // make each query its own transaction $scopedReset = new ScopedCallback(function () use($dbw, $autoTrx) { $dbw->setFlag($autoTrx ? DBO_TRX : 0); // restore old setting }); $uuid = wfRandomString(32); // pop attempt $job = false; // job popped off do { // retry when our row is invalid or deleted as a duplicate // Try to reserve a row in the DB... if (in_array($this->order, array('fifo', 'timestamp'))) { $row = $this->claimOldest($uuid); } else { // random first $rand = mt_rand(0, self::MAX_JOB_RANDOM); // encourage concurrent UPDATEs $gte = (bool) mt_rand(0, 1); // find rows with rand before/after $rand $row = $this->claimRandom($uuid, $rand, $gte); } // Check if we found a row to reserve... if (!$row) { break; // nothing to do } JobQueue::incrStats('pops', $this->type); // Get the job object from the row... $title = Title::makeTitle($row->job_namespace, $row->job_title); $job = Job::factory($row->job_cmd, $title, self::extractBlob($row->job_params), $row->job_id); $job->metadata['id'] = $row->job_id; $job->metadata['timestamp'] = $row->job_timestamp; break; // done } while (true); if (!$job || mt_rand(0, 9) == 0) { // Handled jobs that need to be recycled/deleted; // any recycled jobs will be picked up next attempt $this->recycleAndDeleteStaleJobs(); } } catch (DBError $e) { $this->throwDBException($e); } return $job; }
/** * @covers ::wfRandomString */ public function testRandomString() { $this->assertFalse(wfRandomString() == wfRandomString()); $this->assertEquals(strlen(wfRandomString(10)), 10); $this->assertTrue(preg_match('/^[0-9a-f]+$/i', wfRandomString()) === 1); }
/** * Begin a transaction. If a transaction is already in progress, * that transaction will be committed before the new transaction is started. * * Note that when the DBO_TRX flag is set (which is usually the case for web * requests, but not for maintenance scripts), any previous database query * will have started a transaction automatically. * * Nesting of transactions is not supported. Attempts to nest transactions * will cause a warning, unless the current transaction was started * automatically because of the DBO_TRX flag. * * @param string $fname * @throws DBError */ public final function begin($fname = __METHOD__) { if ($this->mTrxLevel) { // implicit commit if ($this->mTrxAtomicLevels) { // If the current transaction was an automatic atomic one, then we definitely have // a problem. Same if there is any unclosed atomic level. $levels = implode(', ', $this->mTrxAtomicLevels); throw new DBUnexpectedError($this, "Got explicit BEGIN while atomic sections {$levels} are still open."); } elseif (!$this->mTrxAutomatic) { // We want to warn about inadvertently nested begin/commit pairs, but not about // auto-committing implicit transactions that were started by query() via DBO_TRX $msg = "{$fname}: Transaction already in progress (from {$this->mTrxFname}), " . " performing implicit commit!"; wfWarn($msg); wfLogDBError($msg, $this->getLogContext(array('method' => __METHOD__, 'fname' => $fname))); } else { // if the transaction was automatic and has done write operations if ($this->mTrxDoneWrites) { wfDebug("{$fname}: Automatic transaction with writes in progress" . " (from {$this->mTrxFname}), performing implicit commit!\n"); } } $this->runOnTransactionPreCommitCallbacks(); $writeTime = $this->pendingWriteQueryDuration(); $this->doCommit($fname); if ($this->mTrxDoneWrites) { $this->mDoneWrites = microtime(true); $this->getTransactionProfiler()->transactionWritingOut($this->mServer, $this->mDBname, $this->mTrxShortId, $writeTime); } $this->runOnTransactionIdleCallbacks(); } # Avoid fatals if close() was called $this->assertOpen(); $this->doBegin($fname); $this->mTrxTimestamp = microtime(true); $this->mTrxFname = $fname; $this->mTrxDoneWrites = false; $this->mTrxAutomatic = false; $this->mTrxAutomaticAtomic = false; $this->mTrxAtomicLevels = array(); $this->mTrxIdleCallbacks = array(); $this->mTrxPreCommitCallbacks = array(); $this->mTrxShortId = wfRandomString(12); $this->mTrxWriteDuration = 0.0; // First SELECT after BEGIN will establish the snapshot in REPEATABLE-READ. // Get an estimate of the slave lag before then, treating estimate staleness // as lag itself just to be safe $status = $this->getApproximateLagStatus(); $this->mTrxSlaveLag = $status['lag'] + (microtime(true) - $status['since']); }
/** * Return an RFC4122 compliant v4 UUID * * @param int $flags Bitfield (supports UIDGenerator::QUICK_RAND) * @return string * @throws MWException */ public static function newUUIDv4($flags = 0) { $hex = $flags & self::QUICK_RAND ? wfRandomString(31) : MWCryptRand::generateHex(31); return sprintf('%s-%s-%s-%s-%s', substr($hex, 0, 8), substr($hex, 8, 4), '4' . substr($hex, 12, 3), dechex(0x8 | hexdec($hex[15]) & 0x3) . $hex[16] . substr($hex, 17, 2), substr($hex, 19, 12)); }
/** * @param File $image * @param string $dstPath * @param string $dstUrl * @param array $params * @param int $flags * @return bool|MediaTransformError|ThumbnailImage|TransformParameterError */ function doTransform($image, $dstPath, $dstUrl, $params, $flags = 0) { if (!$this->normaliseParams($image, $params)) { return new TransformParameterError($params); } $clientWidth = $params['width']; $clientHeight = $params['height']; $physicalWidth = $params['physicalWidth']; $physicalHeight = $params['physicalHeight']; $lang = isset($params['lang']) ? $params['lang'] : $this->getDefaultRenderLanguage($image); if ($flags & self::TRANSFORM_LATER) { return new ThumbnailImage($image, $dstUrl, $dstPath, $params); } $metadata = $this->unpackMetadata($image->getMetadata()); if (isset($metadata['error'])) { // sanity check $err = wfMessage('svg-long-error', $metadata['error']['message'])->text(); return new MediaTransformError('thumbnail_error', $clientWidth, $clientHeight, $err); } if (!wfMkdirParents(dirname($dstPath), null, __METHOD__)) { return new MediaTransformError('thumbnail_error', $clientWidth, $clientHeight, wfMessage('thumbnail_dest_directory')->text()); } $srcPath = $image->getLocalRefPath(); if ($srcPath === false) { // Failed to get local copy wfDebugLog('thumbnail', sprintf('Thumbnail failed on %s: could not get local copy of "%s"', wfHostname(), $image->getName())); return new MediaTransformError('thumbnail_error', $params['width'], $params['height'], wfMessage('filemissing')->text()); } // Make a temp dir with a symlink to the local copy in it. // This plays well with rsvg-convert policy for external entities. // https://git.gnome.org/browse/librsvg/commit/?id=f01aded72c38f0e18bc7ff67dee800e380251c8e $tmpDir = wfTempDir() . '/svg_' . wfRandomString(24); $lnPath = "{$tmpDir}/" . basename($srcPath); $ok = mkdir($tmpDir, 0771) && symlink($srcPath, $lnPath); /** @noinspection PhpUnusedLocalVariableInspection */ $cleaner = new ScopedCallback(function () use($tmpDir, $lnPath) { MediaWiki\suppressWarnings(); unlink($lnPath); rmdir($tmpDir); MediaWiki\restoreWarnings(); }); if (!$ok) { wfDebugLog('thumbnail', sprintf('Thumbnail failed on %s: could not link %s to %s', wfHostname(), $lnPath, $srcPath)); return new MediaTransformError('thumbnail_error', $params['width'], $params['height'], wfMessage('thumbnail-temp-create')->text()); } $status = $this->rasterize($lnPath, $dstPath, $physicalWidth, $physicalHeight, $lang); if ($status === true) { return new ThumbnailImage($image, $dstUrl, $dstPath, $params); } else { return $status; // MediaTransformError } }
/** * @see JobQueue::doPop() * @return Job|bool */ protected function doPop() { if ($this->cache->get($this->getCacheKey('empty')) === 'true') { return false; // queue is empty } list($dbw, $scope) = $this->getMasterDB(); $dbw->commit(__METHOD__, 'flush'); // flush existing transaction $autoTrx = $dbw->getFlag(DBO_TRX); // get current setting $dbw->clearFlag(DBO_TRX); // make each query its own transaction $scopedReset = new ScopedCallback(function () use($dbw, $autoTrx) { $dbw->setFlag($autoTrx ? DBO_TRX : 0); // restore old setting }); $uuid = wfRandomString(32); // pop attempt $job = false; // job popped off do { // retry when our row is invalid or deleted as a duplicate // Try to reserve a row in the DB... if (in_array($this->order, array('fifo', 'timestamp'))) { $row = $this->claimOldest($uuid); } else { // random first $rand = mt_rand(0, self::MAX_JOB_RANDOM); // encourage concurrent UPDATEs $gte = (bool) mt_rand(0, 1); // find rows with rand before/after $rand $row = $this->claimRandom($uuid, $rand, $gte); } // Check if we found a row to reserve... if (!$row) { $this->cache->set($this->getCacheKey('empty'), 'true', self::CACHE_TTL_LONG); break; // nothing to do } JobQueue::incrStats('job-pop', $this->type); // Get the job object from the row... $title = Title::makeTitleSafe($row->job_namespace, $row->job_title); if (!$title) { $dbw->delete('job', array('job_id' => $row->job_id), __METHOD__); wfDebug("Row has invalid title '{$row->job_title}'."); continue; // try again } $job = Job::factory($row->job_cmd, $title, self::extractBlob($row->job_params), $row->job_id); $job->metadata['id'] = $row->job_id; $job->id = $row->job_id; // XXX: work around broken subclasses break; // done } while (true); return $job; }