Example #1
0
 /**
  * Perform an HTTP request
  *
  * @param $method String: HTTP method. Usually GET/POST
  * @param $url String: full URL to act on. If protocol-relative, will be expanded to an http:// URL
  * @param $options Array: options to pass to MWHttpRequest object.
  *	Possible keys for the array:
  *    - timeout             Timeout length in seconds
  *    - postData            An array of key-value pairs or a url-encoded form data
  *    - proxy               The proxy to use.
  *                          Will use $wgHTTPProxy (if set) otherwise.
  *    - noProxy             Override $wgHTTPProxy (if set) and don't use any proxy at all.
  *    - sslVerifyHost       (curl only) Verify hostname against certificate
  *    - sslVerifyCert       (curl only) Verify SSL certificate
  *    - caInfo              (curl only) Provide CA information
  *    - maxRedirects        Maximum number of redirects to follow (defaults to 5)
  *    - followRedirects     Whether to follow redirects (defaults to false).
  *		                    Note: this should only be used when the target URL is trusted,
  *		                    to avoid attacks on intranet services accessible by HTTP.
  *    - userAgent           A user agent, if you want to override the default
  *                          MediaWiki/$wgVersion
  *    - headers             Additional headers for request
  *    - returnInstance      If set the method will return MWHttpRequest instance instead of string|boolean
  * @return Mixed: (bool)false on failure or a string on success or MWHttpRequest instance if returnInstance option is set
  */
 public static function request($method, $url, $options = array())
 {
     $fname = __METHOD__ . '::' . $method;
     wfProfileIn($fname);
     wfDebug("HTTP: {$method}: {$url}\n");
     $options['method'] = strtoupper($method);
     if (!isset($options['timeout'])) {
         $options['timeout'] = 'default';
     }
     $req = MWHttpRequest::factory($url, $options);
     // Wikia change - @author: suchy - begin
     if (isset($options['headers']) && is_array($options['headers'])) {
         foreach ($options['headers'] as $name => $value) {
             $req->setHeader($name, $value);
         }
     }
     // Wikia change - end
     if (isset($options['userAgent'])) {
         $req->setUserAgent($options['userAgent']);
     }
     // Wikia change - @author: mech - begin
     $requestTime = microtime(true);
     // Wikia change - end
     $status = $req->execute();
     // Wikia change - @author: mech - begin
     // log all the requests we make
     $caller = wfGetCallerClassMethod([__CLASS__, 'Hooks', 'ApiService', 'Solarium_Client', 'Solarium_Client_Adapter_Curl']);
     $isOk = $status->isOK();
     if (class_exists('Wikia\\Logger\\WikiaLogger')) {
         $requestTime = (int) ((microtime(true) - $requestTime) * 1000.0);
         $backendTime = $req->getResponseHeader('x-backend-response-time') ?: 0;
         $params = ['statusCode' => $req->getStatus(), 'reqMethod' => $method, 'reqUrl' => $url, 'caller' => $caller, 'isOk' => $isOk, 'requestTimeMS' => $requestTime, 'backendTimeMS' => intval(1000 * $backendTime)];
         if (!$isOk) {
             $params['statusMessage'] = $status->getMessage();
         }
         \Wikia\Logger\WikiaLogger::instance()->debug('Http request', $params);
     }
     // Wikia change - @author: nAndy - begin
     // Introduced new returnInstance options to return MWHttpRequest instance instead of string-bool mix
     if (!empty($options['returnInstance'])) {
         $ret = $req;
     } else {
         if ($isOk) {
             $ret = $req->getContent();
             // Wikia change - end
         } else {
             $ret = false;
         }
     }
     wfProfileOut($fname);
     return $ret;
 }
Example #2
0
 /**
  * loadVariableFromDB
  *
  * Read variable data from database in most efficient way. If you've found
  * faster version - fix this one.
  *
  * @author eloy@wikia
  * @access private
  * @static
  *
  * @param integer $cv_id        variable id in city_variables_pool
  * @param string $cv_name    variable name in city_variables_pool
  * @param integer $city_id    wiki id in city_list
  * @param boolean $master        use master or slave connection
  *
  * @return string: path to file or null if id is not a number
  */
 private static function loadVariableFromDB($cv_id, $cv_name, $city_id, $master = false)
 {
     if (!self::isUsed()) {
         Wikia::log(__METHOD__, "", "WikiFactory is not used.");
         return false;
     }
     /**
      * $wiki could be empty, but we have to know which variable read
      */
     if (!$cv_id && !$cv_name) {
         return false;
     }
     wfProfileIn(__METHOD__);
     /**
      * if both are defined cv_id has precedence
      */
     if ($cv_id) {
         $condition = ["cv_id" => $cv_id];
         $cacheKey = "id:{$cv_id}";
     } else {
         $condition = ["cv_name" => $cv_name];
         $cacheKey = "name:{$cv_name}";
     }
     $dbr = $master ? self::db(DB_MASTER) : self::db(DB_SLAVE);
     $caller = wfGetCallerClassMethod(__CLASS__);
     $fname = __METHOD__ . " (from {$caller})";
     if ($master || !isset(self::$variablesCache[$cacheKey])) {
         $oRow = WikiaDataAccess::cache(self::getVarMetadataKey($cacheKey), WikiaResponse::CACHE_STANDARD, function () use($dbr, $condition, $fname) {
             $oRow = $dbr->selectRow(["city_variables_pool"], ["cv_id", "cv_name", "cv_description", "cv_variable_type", "cv_variable_group", "cv_access_level", "cv_is_unique"], $condition, $fname);
             // log typos in calls to WikiFactory::loadVariableFromDB
             if (!is_object($oRow)) {
                 WikiaLogger::instance()->error('WikiFactory - variable not found', ['condition' => $condition, 'exception' => new Exception()]);
             }
             return $oRow;
         }, $master ? WikiaDataAccess::REFRESH_CACHE : WikiaDataAccess::USE_CACHE);
         self::$variablesCache[$cacheKey] = $oRow;
     }
     $oRow = self::$variablesCache[$cacheKey];
     if (is_object($oRow)) {
         $oRow = clone $oRow;
     }
     if (!isset($oRow->cv_id)) {
         /**
          * variable doesn't exist
          */
         wfProfileOut(__METHOD__);
         return null;
     }
     if (!empty($city_id)) {
         $oRow2 = WikiaDataAccess::cache(self::getVarValueKey($city_id, $oRow->cv_id), 3600, function () use($dbr, $oRow, $city_id, $fname) {
             return $dbr->selectRow(["city_variables"], ["cv_city_id", "cv_variable_id", "cv_value"], ["cv_variable_id" => $oRow->cv_id, "cv_city_id" => $city_id], $fname);
         });
         if (isset($oRow2->cv_variable_id)) {
             $oRow->cv_city_id = $oRow2->cv_city_id;
             $oRow->cv_variable_id = $oRow2->cv_variable_id;
             $oRow->cv_value = $oRow2->cv_value;
         } else {
             $oRow->cv_city_id = $city_id;
             $oRow->cv_variable_id = $oRow->cv_id;
             $oRow->cv_value = null;
         }
     } else {
         $oRow->cv_city_id = null;
         $oRow->cv_variable_id = $oRow->cv_id;
         $oRow->cv_value = null;
     }
     wfProfileOut(__METHOD__);
     return $oRow;
 }
Example #3
0
 /**
  * Return the name of the method (outside of the internal code) that triggered purge request
  *
  * @return string method name
  */
 private static function getPurgeCaller()
 {
     return wfGetCallerClassMethod([__CLASS__, 'SquidUpdate', 'WikiPage', 'Article', 'Title', 'WikiaDispatchableObject']);
 }
Example #4
0
 /**
  * Send a message to a destination
  *
  * @param string $message JSON encoded message
  * @return boolean
  */
 public function send($message)
 {
     $messages = array();
     if (!is_array($message)) {
         $message = array($message);
     }
     foreach ($message as $msg) {
         $__message = array(self::CATEGORY_KEY => $this->category, self::MESSAGE_KEY => $msg);
         $logEntry = new ScribeLogEntry($__message);
         $messages[] = $logEntry;
     }
     $result = false;
     if (!empty($messages)) {
         try {
             $this->connect();
             $this->transport->open();
             $result = $this->client->Log($messages);
             if ($result == $GLOBALS['E_ResultCode']['TRY_LATER']) {
                 Wikia::log(__METHOD__, "scribe", "Returned 'TRY_LATER' value");
             }
             if ($result != $GLOBALS['E_ResultCode']['OK']) {
                 Wikia::log(__METHOD__, "scribe", "Unknown result ({$result})");
             }
             $this->transport->close();
             WikiaLogger::instance()->info('Scribe', ['cmd' => 'send', 'category' => $this->category, 'caller' => wfGetCallerClassMethod(__CLASS__)]);
         } catch (TException $e) {
             // socket error
             Wikia::log(__METHOD__, 'scribeClient log', $e->getMessage());
             $this->connected = false;
         }
     }
     return $result;
 }
Example #5
0
 /**
  * Really opens a connection. Uncached.
  * Returns a Database object whether or not the connection was successful.
  * @access private
  *
  * @param $server
  * @param $dbNameOverride bool
  * @return DatabaseBase
  */
 function reallyOpenConnection($server, $dbNameOverride = false)
 {
     if (!is_array($server)) {
         throw new MWException('You must update your load-balancing configuration. ' . 'See DefaultSettings.php entry for $wgDBservers.');
     }
     $host = $server['host'];
     $dbname = $server['dbname'];
     if ($dbNameOverride !== false) {
         $server['dbname'] = $dbname = $dbNameOverride;
     }
     # Create object
     wfDebug("Connecting to {$host} {$dbname}...\n");
     try {
         $db = DatabaseBase::factory($server['type'], $server);
     } catch (DBConnectionError $e) {
         // FIXME: This is probably the ugliest thing I have ever done to
         // PHP. I'm half-expecting it to segfault, just out of disgust. -- TS
         $db = $e->db;
     }
     $db->setLBInfo($server);
     if (isset($server['fakeSlaveLag'])) {
         $db->setFakeSlaveLag($server['fakeSlaveLag']);
     }
     if (isset($server['fakeMaster'])) {
         $db->setFakeMaster(true);
     }
     // Wikia change - begin
     if ($db->getSampler()->shouldSample()) {
         $db->getWikiaLogger()->info("LoadBalancer::reallyOpenConnection", ['caller' => wfGetCallerClassMethod(__CLASS__), 'host' => $server['hostName'], 'dbname' => $dbname]);
     }
     // Wikia change - end
     return $db;
 }
Example #6
0
 /**
  * Run an SQL query and return the result. Normally throws a DBQueryError
  * on failure. If errors are ignored, returns false instead.
  *
  * In new code, the query wrappers select(), insert(), update(), delete(),
  * etc. should be used where possible, since they give much better DBMS
  * independence and automatically quote or validate user input in a variety
  * of contexts. This function is generally only useful for queries which are
  * explicitly DBMS-dependent and are unsupported by the query wrappers, such
  * as CREATE TABLE.
  *
  * However, the query wrappers themselves should call this function.
  *
  * @param string $sql : SQL query
  * @param string $fname : Name of the calling function, for profiling/SHOW PROCESSLIST
  *     comment (you can use __METHOD__ or add some extra info)
  * @param bool $tempIgnore : Whether to avoid throwing an exception on errors...
  *     maybe best to catch the exception instead?
  *
  * @return bool|ResultWrapper true for a successful write query, ResultWrapper object
  *     for a successful read query, or false on failure if $tempIgnore set
  *
  * @throws DBQueryError
  * @throws MWException
  */
 public function query($sql, $fname = '', $tempIgnore = false)
 {
     $isMaster = !is_null($this->getLBInfo('master'));
     if (!Profiler::instance()->isStub()) {
         # generalizeSQL will probably cut down the query to reasonable
         # logging size most of the time. The substr is really just a sanity check.
         if ($isMaster) {
             $queryProf = 'query-m: ' . substr(DatabaseBase::generalizeSQL($sql), 0, 255);
             $totalProf = 'DatabaseBase::query-master';
         } else {
             $queryProf = 'query: ' . substr(DatabaseBase::generalizeSQL($sql), 0, 255);
             $totalProf = 'DatabaseBase::query';
         }
         wfProfileIn($totalProf);
         wfProfileIn($queryProf);
     }
     $this->mLastQuery = $sql;
     $is_writeable = $this->isWriteQuery($sql);
     if (!$this->mDoneWrites && $is_writeable) {
         # Set a flag indicating that writes have been done
         wfDebug(__METHOD__ . ": Writes done: {$sql}\n");
         $this->mDoneWrites = true;
     }
     # <Wikia>
     global $wgDBReadOnly;
     if ($is_writeable && $wgDBReadOnly) {
         if (!Profiler::instance()->isStub()) {
             wfProfileOut($queryProf);
             wfProfileOut($totalProf);
         }
         WikiaLogger::instance()->error('DB readonly mode', ['exception' => new Exception($sql), 'server' => $this->mServer]);
         wfDebug(sprintf("%s: DB read-only mode prevented the following query: %s\n", __METHOD__, $sql));
         return false;
     }
     # </Wikia>
     # Add a comment for easy SHOW PROCESSLIST interpretation
     global $wgUser;
     if (is_object($wgUser) && $wgUser->isItemLoaded('name')) {
         $userName = $wgUser->getName();
         if (mb_strlen($userName) > 15) {
             $userName = mb_substr($userName, 0, 15) . '...';
         }
         $userName = str_replace('/', '', $userName);
     } else {
         $userName = '';
     }
     # Wikia change - begin
     # @author macbre
     if ($fname === '') {
         wfDebug("Query: \$fname autogenerated, please pass __METHOD__ to the query() call!\n");
         $fname = wfGetCallerClassMethod(['WikiaSQL', 'FluentSql\\SQL', 'WikiaDataAccess', __CLASS__]);
     }
     # Wikia change - end
     $commentedSql = preg_replace('/\\s/', " /* {$fname} {$userName} */ ", $sql, 1);
     # Wikia change - begin
     # @author macbre
     # Add profiling data to queries like BEGIN or COMMIT (preg_replace above will not handle them)
     if (strpos($sql, ' ') === false) {
         $commentedSql = "{$sql} /* {$fname} {$userName} */";
     }
     # Wikia change - end
     # If DBO_TRX is set, start a transaction
     if ($this->mFlags & DBO_TRX && !$this->trxLevel() && $sql != 'BEGIN' && $sql != 'COMMIT' && $sql != 'ROLLBACK') {
         # avoid establishing transactions for SHOW and SET statements too -
         # that would delay transaction initializations to once connection
         # is really used by application
         $sqlstart = substr($sql, 0, 10);
         // very much worth it, benchmark certified(tm)
         if (strpos($sqlstart, "SHOW ") !== 0 && strpos($sqlstart, "SET ") !== 0) {
             $this->begin(__METHOD__ . " ({$fname})");
         }
     }
     if ($this->debug()) {
         static $cnt = 0;
         $cnt++;
         $sqlx = substr($commentedSql, 0, 500);
         $sqlx = strtr($sqlx, "\t\n", '  ');
         $master = $isMaster ? 'master' : 'slave';
         wfDebug("Query {$this->mDBname} ({$cnt}) ({$master}): {$sqlx}\n");
     }
     if (istainted($sql) & TC_MYSQL) {
         throw new MWException('Tainted query found');
     }
     $ret = $this->executeAndProfileQuery($commentedSql, $fname, $isMaster);
     # Try reconnecting if the connection was lost
     if (false === $ret && $this->wasErrorReissuable()) {
         # Transaction is gone, like it or not
         $this->mTrxLevel = 0;
         wfDebug("Connection lost, reconnecting...\n");
         if ($this->ping()) {
             wfDebug("Reconnected\n");
             $sqlx = substr($commentedSql, 0, 500);
             $sqlx = strtr($sqlx, "\t\n", '  ');
             global $wgRequestTime;
             $elapsed = round(microtime(true) - $wgRequestTime, 3);
             if ($elapsed < 300) {
                 # Not a database error to lose a transaction after a minute or two
                 wfLogDBError("Connection lost and reconnected after {$elapsed}s, query: {$sqlx}\n");
             }
             $ret = $this->executeAndProfileQuery($commentedSql, $fname, $isMaster);
         } else {
             wfDebug("Failed\n");
         }
     }
     if (false === $ret) {
         $this->reportQueryError($this->lastError(), $this->lastErrno(), $sql, $fname, $tempIgnore);
     }
     if (!Profiler::instance()->isStub()) {
         wfProfileOut($queryProf);
         wfProfileOut($totalProf);
     }
     return $this->resultObject($ret);
 }
Example #7
0
 /**
  * Load items into $ret from $sock
  *
  * @param $sock Resource: socket to read from
  * @param $ret Array: returned values
  *
  * @access private
  */
 function _load_items($sock, &$ret)
 {
     while (1) {
         $decl = fgets($sock);
         if ($decl == "END\r\n") {
             return true;
         } elseif (preg_match('/^VALUE (\\S+) (\\d+) (\\d+)\\r\\n$/', $decl, $match)) {
             list($rkey, $flags, $len) = array($match[1], $match[2], $match[3]);
             $bneed = $len + 2;
             $offset = 0;
             while ($bneed > 0) {
                 $data = fread($sock, $bneed);
                 $n = strlen($data);
                 if ($n == 0) {
                     break;
                 }
                 $offset += $n;
                 $bneed -= $n;
                 if (isset($ret[$rkey])) {
                     $ret[$rkey] .= $data;
                 } else {
                     $ret[$rkey] = $data;
                 }
             }
             if ($offset != $len + 2) {
                 // Something is borked!
                 if ($this->_debug) {
                     $this->_debugprint(sprintf("Something is borked!  key %s expecting %d got %d length\n", $rkey, $len + 2, $offset));
                 }
                 unset($ret[$rkey]);
                 $this->_close_sock($sock);
                 return false;
             }
             if ($this->_have_zlib && $flags & self::COMPRESSED) {
                 wfProfileIn(__METHOD__ . '::uncompress');
                 $ret[$rkey] = gzuncompress($ret[$rkey]);
                 wfProfileOut(__METHOD__ . '::uncompress');
             }
             $ret[$rkey] = rtrim($ret[$rkey]);
             if ($flags & self::SERIALIZED) {
                 wfProfileIn(__METHOD__ . '::unserialize');
                 $ret[$rkey] = unserialize($ret[$rkey]);
                 wfProfileOut(__METHOD__ . '::unserialize');
             }
         } else {
             $this->_debugprint("Error parsing memcached response\n");
             // Wikia change - begin
             // @author macbre (BugId:27916 / PLATFORM-774)
             $method = explode(':', wfGetCallerClassMethod(__CLASS__));
             // eg. MemcachedPhpBagOStuff::get
             $caller = wfGetCallerClassMethod([__CLASS__, 'MemcachedPhpBagOStuff']);
             // eg. WikiFactory::getWikiByDB
             $this->error('MemcachedClient: error parsing the response', ['caller' => $caller, 'cmd' => substr($this->_current_cmd, 0, 1024), 'operation' => end($method), 'response' => $decl, 'exception' => new Exception(), 'host' => $this->_current_host]);
             // Wikia change - end
             return 0;
         }
     }
 }