2004-06-15 15:05:56 +00:00
|
|
|
<?php
|
2004-09-02 23:28:24 +00:00
|
|
|
/**
|
|
|
|
|
*
|
|
|
|
|
*/
|
2004-06-15 15:05:56 +00:00
|
|
|
|
2004-09-02 23:28:24 +00:00
|
|
|
/**
|
|
|
|
|
* Pure virtual parent
|
2007-04-04 05:22:37 +00:00
|
|
|
* @todo document (needs a one-sentence top-level class description, that answers the question: "what is a HistoryBlob?")
|
2004-09-02 23:28:24 +00:00
|
|
|
*/
|
2007-04-21 15:04:51 +00:00
|
|
|
interface HistoryBlob
|
2004-06-15 15:05:56 +00:00
|
|
|
{
|
2005-01-27 19:51:47 +00:00
|
|
|
/**
|
|
|
|
|
* setMeta and getMeta currently aren't used for anything, I just thought
|
|
|
|
|
* they might be useful in the future.
|
2006-04-19 15:46:24 +00:00
|
|
|
* @param $meta String: a single string.
|
2005-01-27 19:51:47 +00:00
|
|
|
*/
|
2007-04-21 15:04:51 +00:00
|
|
|
public function setMeta( $meta );
|
2004-10-30 14:39:40 +00:00
|
|
|
|
2005-01-27 19:51:47 +00:00
|
|
|
/**
|
|
|
|
|
* setMeta and getMeta currently aren't used for anything, I just thought
|
|
|
|
|
* they might be useful in the future.
|
|
|
|
|
* Gets the meta-value
|
|
|
|
|
*/
|
2007-04-21 15:04:51 +00:00
|
|
|
public function getMeta();
|
2004-10-30 14:39:40 +00:00
|
|
|
|
2005-01-27 19:51:47 +00:00
|
|
|
/**
|
|
|
|
|
* Adds an item of text, returns a stub object which points to the item.
|
|
|
|
|
* You must call setLocation() on the stub object before storing it to the
|
|
|
|
|
* database
|
|
|
|
|
*/
|
2007-04-21 15:04:51 +00:00
|
|
|
public function addItem( $text );
|
2004-10-30 14:39:40 +00:00
|
|
|
|
2006-01-07 13:09:30 +00:00
|
|
|
/**
|
2005-01-27 19:51:47 +00:00
|
|
|
* Get item by hash
|
|
|
|
|
*/
|
2007-04-21 15:04:51 +00:00
|
|
|
public function getItem( $hash );
|
2006-01-07 13:31:29 +00:00
|
|
|
|
2004-10-30 14:39:40 +00:00
|
|
|
# Set the "default text"
|
|
|
|
|
# This concept is an odd property of the current DB schema, whereby each text item has a revision
|
2006-01-07 13:09:30 +00:00
|
|
|
# associated with it. The default text is the text of the associated revision. There may, however,
|
2004-10-30 14:39:40 +00:00
|
|
|
# be other revisions in the same object
|
2007-04-21 15:04:51 +00:00
|
|
|
public function setText( $text );
|
2004-10-30 14:39:40 +00:00
|
|
|
|
2005-01-27 19:51:47 +00:00
|
|
|
/**
|
|
|
|
|
* Get default text. This is called from Revision::getRevisionText()
|
|
|
|
|
*/
|
2007-04-21 15:04:51 +00:00
|
|
|
function getText();
|
2004-06-15 15:05:56 +00:00
|
|
|
}
|
|
|
|
|
|
2004-09-02 23:28:24 +00:00
|
|
|
/**
|
|
|
|
|
* The real object
|
2007-04-04 05:22:37 +00:00
|
|
|
* @todo document (needs one-sentence top-level class description + function descriptions).
|
2004-09-02 23:28:24 +00:00
|
|
|
*/
|
2007-04-21 15:04:51 +00:00
|
|
|
class ConcatenatedGzipHistoryBlob implements HistoryBlob
|
2006-05-11 22:40:38 +00:00
|
|
|
{
|
2007-04-21 15:12:56 +00:00
|
|
|
public $mVersion = 0, $mCompressed = false, $mItems = array(), $mDefaultHash = '';
|
|
|
|
|
public $mFast = 0, $mSize = 0;
|
2004-06-15 15:05:56 +00:00
|
|
|
|
2007-04-21 15:04:51 +00:00
|
|
|
/** Constructor */
|
|
|
|
|
public function ConcatenatedGzipHistoryBlob() {
|
2004-06-15 15:05:56 +00:00
|
|
|
if ( !function_exists( 'gzdeflate' ) ) {
|
2006-06-07 06:40:24 +00:00
|
|
|
throw new MWException( "Need zlib support to read or write this kind of history object (ConcatenatedGzipHistoryBlob)\n" );
|
2004-06-15 15:05:56 +00:00
|
|
|
}
|
|
|
|
|
}
|
2005-01-27 19:51:47 +00:00
|
|
|
|
2007-04-21 15:04:51 +00:00
|
|
|
#
|
|
|
|
|
# HistoryBlob implementation:
|
|
|
|
|
#
|
|
|
|
|
|
2005-01-27 19:51:47 +00:00
|
|
|
/** @todo document */
|
2007-04-21 15:04:51 +00:00
|
|
|
public function setMeta( $metaData ) {
|
2004-06-15 15:05:56 +00:00
|
|
|
$this->uncompress();
|
|
|
|
|
$this->mItems['meta'] = $metaData;
|
|
|
|
|
}
|
|
|
|
|
|
2005-01-27 19:51:47 +00:00
|
|
|
/** @todo document */
|
2007-04-21 15:04:51 +00:00
|
|
|
public function getMeta() {
|
2004-06-15 15:05:56 +00:00
|
|
|
$this->uncompress();
|
|
|
|
|
return $this->mItems['meta'];
|
|
|
|
|
}
|
2005-01-27 19:51:47 +00:00
|
|
|
|
|
|
|
|
/** @todo document */
|
2007-04-21 15:04:51 +00:00
|
|
|
public function addItem( $text ) {
|
2004-06-15 15:05:56 +00:00
|
|
|
$this->uncompress();
|
2004-10-30 14:39:40 +00:00
|
|
|
$hash = md5( $text );
|
|
|
|
|
$this->mItems[$hash] = $text;
|
|
|
|
|
$this->mSize += strlen( $text );
|
|
|
|
|
|
|
|
|
|
$stub = new HistoryBlobStub( $hash );
|
|
|
|
|
return $stub;
|
2004-06-15 15:05:56 +00:00
|
|
|
}
|
|
|
|
|
|
2005-01-27 19:51:47 +00:00
|
|
|
/** @todo document */
|
2007-04-21 15:04:51 +00:00
|
|
|
public function getItem( $hash ) {
|
2004-10-30 14:39:40 +00:00
|
|
|
$this->uncompress();
|
|
|
|
|
if ( array_key_exists( $hash, $this->mItems ) ) {
|
|
|
|
|
return $this->mItems[$hash];
|
|
|
|
|
} else {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2004-06-15 15:05:56 +00:00
|
|
|
}
|
|
|
|
|
|
2005-01-27 19:51:47 +00:00
|
|
|
/** @todo document */
|
2007-04-21 15:04:51 +00:00
|
|
|
public function setText( $text ) {
|
|
|
|
|
$this->uncompress();
|
|
|
|
|
$stub = $this->addItem( $text );
|
|
|
|
|
$this->mDefaultHash = $stub->mHash;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** @todo document */
|
|
|
|
|
public function getText() {
|
|
|
|
|
$this->uncompress();
|
|
|
|
|
return $this->getItem( $this->mDefaultHash );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# HistoryBlob implemented.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** @todo document */
|
|
|
|
|
public function removeItem( $hash ) {
|
2004-10-30 14:39:40 +00:00
|
|
|
$this->mSize -= strlen( $this->mItems[$hash] );
|
|
|
|
|
unset( $this->mItems[$hash] );
|
|
|
|
|
}
|
2005-01-27 19:51:47 +00:00
|
|
|
|
|
|
|
|
/** @todo document */
|
2007-04-21 15:12:56 +00:00
|
|
|
public function compress() {
|
2004-06-15 15:05:56 +00:00
|
|
|
if ( !$this->mCompressed ) {
|
|
|
|
|
$this->mItems = gzdeflate( serialize( $this->mItems ) );
|
|
|
|
|
$this->mCompressed = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2005-01-27 19:51:47 +00:00
|
|
|
/** @todo document */
|
2007-04-21 15:12:56 +00:00
|
|
|
public function uncompress() {
|
2004-06-15 15:05:56 +00:00
|
|
|
if ( $this->mCompressed ) {
|
|
|
|
|
$this->mItems = unserialize( gzinflate( $this->mItems ) );
|
2004-10-30 14:39:40 +00:00
|
|
|
$this->mCompressed = false;
|
2004-06-15 15:05:56 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2004-10-30 14:39:40 +00:00
|
|
|
|
2005-01-27 19:51:47 +00:00
|
|
|
/** @todo document */
|
2004-06-15 15:05:56 +00:00
|
|
|
function __sleep() {
|
2004-10-30 14:39:40 +00:00
|
|
|
$this->compress();
|
|
|
|
|
return array( 'mVersion', 'mCompressed', 'mItems', 'mDefaultHash' );
|
2004-06-15 15:05:56 +00:00
|
|
|
}
|
|
|
|
|
|
2005-01-27 19:51:47 +00:00
|
|
|
/** @todo document */
|
2004-06-15 15:05:56 +00:00
|
|
|
function __wakeup() {
|
2004-10-30 14:39:40 +00:00
|
|
|
$this->uncompress();
|
|
|
|
|
}
|
|
|
|
|
|
2005-01-27 19:51:47 +00:00
|
|
|
/**
|
|
|
|
|
* Determines if this object is happy
|
|
|
|
|
*/
|
2007-04-21 15:04:51 +00:00
|
|
|
public function isHappy( $maxFactor, $factorThreshold ) {
|
2004-10-30 14:39:40 +00:00
|
|
|
if ( count( $this->mItems ) == 0 ) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2004-12-12 05:16:24 +00:00
|
|
|
if ( !$this->mFast ) {
|
2004-10-30 14:39:40 +00:00
|
|
|
$this->uncompress();
|
|
|
|
|
$record = serialize( $this->mItems );
|
|
|
|
|
$size = strlen( $record );
|
|
|
|
|
$avgUncompressed = $size / count( $this->mItems );
|
|
|
|
|
$compressed = strlen( gzdeflate( $record ) );
|
|
|
|
|
|
|
|
|
|
if ( $compressed < $factorThreshold * 1024 ) {
|
|
|
|
|
return true;
|
|
|
|
|
} else {
|
|
|
|
|
return $avgUncompressed * $maxFactor < $compressed;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
return count( $this->mItems ) <= 10;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2006-06-01 08:19:02 +00:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* One-step cache variable to hold base blobs; operations that
|
|
|
|
|
* pull multiple revisions may often pull multiple times from
|
|
|
|
|
* the same blob. By keeping the last-used one open, we avoid
|
|
|
|
|
* redundant unserialization and decompression overhead.
|
|
|
|
|
*/
|
2006-06-08 15:12:26 +00:00
|
|
|
global $wgBlobCache;
|
|
|
|
|
$wgBlobCache = array();
|
|
|
|
|
|
2006-06-01 08:19:02 +00:00
|
|
|
|
2005-01-27 19:51:47 +00:00
|
|
|
/**
|
2007-04-04 05:22:37 +00:00
|
|
|
* @todo document (needs one-sentence top-level class description + some function descriptions).
|
2005-01-27 19:51:47 +00:00
|
|
|
*/
|
|
|
|
|
class HistoryBlobStub {
|
2006-05-11 22:40:38 +00:00
|
|
|
var $mOldId, $mHash, $mRef;
|
2004-10-30 14:39:40 +00:00
|
|
|
|
2005-01-27 19:51:47 +00:00
|
|
|
/** @todo document */
|
2004-10-30 14:39:40 +00:00
|
|
|
function HistoryBlobStub( $hash = '', $oldid = 0 ) {
|
|
|
|
|
$this->mHash = $hash;
|
|
|
|
|
}
|
2006-01-07 13:31:29 +00:00
|
|
|
|
2005-01-27 19:51:47 +00:00
|
|
|
/**
|
|
|
|
|
* Sets the location (old_id) of the main object to which this object
|
|
|
|
|
* points
|
|
|
|
|
*/
|
2004-10-30 14:39:40 +00:00
|
|
|
function setLocation( $id ) {
|
|
|
|
|
$this->mOldId = $id;
|
|
|
|
|
}
|
2005-01-27 19:51:47 +00:00
|
|
|
|
2007-04-04 05:22:37 +00:00
|
|
|
/**
|
|
|
|
|
* Sets the location (old_id) of the referring object
|
|
|
|
|
*/
|
2006-03-07 01:10:39 +00:00
|
|
|
function setReferrer( $id ) {
|
|
|
|
|
$this->mRef = $id;
|
|
|
|
|
}
|
2005-08-21 13:53:49 +00:00
|
|
|
|
2007-04-04 05:22:37 +00:00
|
|
|
/**
|
|
|
|
|
* Gets the location of the referring object
|
|
|
|
|
*/
|
2006-03-07 01:10:39 +00:00
|
|
|
function getReferrer() {
|
|
|
|
|
return $this->mRef;
|
|
|
|
|
}
|
2005-08-21 13:53:49 +00:00
|
|
|
|
2005-01-27 19:51:47 +00:00
|
|
|
/** @todo document */
|
2004-10-30 14:39:40 +00:00
|
|
|
function getText() {
|
Prevent some unnecessary lstat system calls, generated by include or require directives.
This can be done either by:
* Using explicit full paths, using the $IP global for the installation directory full path, and then working down the tree from there.
* Using explicit full paths, using the "dirname(__FILE__)" directive to get a full directory path for the includer file.
* Occasionally removing the line altogether, and then for some files the inclusion is handled by the autoloader.
For example, if the "extensions/wikihiero/wh_main.php" file does an include or require on "wh_list.php", then PHP does the following:
* tries to open "wiki/wh_list.php", and fails.
* tries to open "wiki/includes/wh_list.php", and fails.
* tries to open "wiki/languages/wh_list.php", and fails.
* tries to open "wiki/extensions/wikihiero/wh_list.php", and succeeds.
So in this example, the first 3 calls can be prevented if PHP is told where the file is.
Testing Method: On a Linux box, run these commands to attach strace to all the apache2 processes, and log their system calls to a temporary file, then generate some activity, and then stop the strace:
-----------------------------------
rm /tmp/strace-log.txt
strace -tt -o /tmp/strace-log.txt -p `pidof apache2 | sed 's/ / -p /g'` &
php maintenance/fuzz-tester.php --keep-passed-tests --include-binary --max-runtime=3 > /tmp/strace-tests.txt
killall -9 strace
grep "No such file or directory" /tmp/strace-log.txt | sort -u
-----------------------------------
Any failed file stats will be marked with: "-1 ENOENT (No such file or directory)".
Also:
* Strict Standards: Undefined offset: 230 in includes/normal/UtfNormal.php on line 637
* Strict Standards: iconv() [<a href='function.iconv'>function.iconv</a>]: Detected an illegal character in input string in languages/Language.php on line 776
[Note: Partial only - despite adding "//IGNORE", it still seems to be possible with some
messed- up binary input to cause PHP 5.1.2's iconv() function to squeal like a stuck pig].
* Update one $fname variable (method belongs to HistoryBlobStub class).
2007-02-09 05:36:56 +00:00
|
|
|
$fname = 'HistoryBlobStub::getText';
|
2006-06-08 15:12:26 +00:00
|
|
|
global $wgBlobCache;
|
|
|
|
|
if( isset( $wgBlobCache[$this->mOldId] ) ) {
|
|
|
|
|
$obj = $wgBlobCache[$this->mOldId];
|
2005-05-05 23:27:34 +00:00
|
|
|
} else {
|
2007-01-22 23:50:42 +00:00
|
|
|
$dbr = wfGetDB( DB_SLAVE );
|
2005-05-08 08:17:12 +00:00
|
|
|
$row = $dbr->selectRow( 'text', array( 'old_flags', 'old_text' ), array( 'old_id' => $this->mOldId ) );
|
|
|
|
|
if( !$row ) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
$flags = explode( ',', $row->old_flags );
|
2005-05-23 06:30:29 +00:00
|
|
|
if( in_array( 'external', $flags ) ) {
|
2005-08-14 07:18:34 +00:00
|
|
|
$url=$row->old_text;
|
2006-11-29 11:43:58 +00:00
|
|
|
@list( /* $proto */ ,$path)=explode('://',$url,2);
|
2005-08-14 07:18:34 +00:00
|
|
|
if ($path=="") {
|
|
|
|
|
wfProfileOut( $fname );
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
$row->old_text=ExternalStore::fetchFromUrl($url);
|
2005-05-23 06:30:29 +00:00
|
|
|
|
|
|
|
|
}
|
2005-05-08 08:17:12 +00:00
|
|
|
if( !in_array( 'object', $flags ) ) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2006-01-07 13:31:29 +00:00
|
|
|
|
2005-05-08 08:17:12 +00:00
|
|
|
if( in_array( 'gzip', $flags ) ) {
|
|
|
|
|
// This shouldn't happen, but a bug in the compress script
|
|
|
|
|
// may at times gzip-compress a HistoryBlob object row.
|
|
|
|
|
$obj = unserialize( gzinflate( $row->old_text ) );
|
|
|
|
|
} else {
|
|
|
|
|
$obj = unserialize( $row->old_text );
|
|
|
|
|
}
|
2006-01-07 13:31:29 +00:00
|
|
|
|
2005-05-08 08:17:12 +00:00
|
|
|
if( !is_object( $obj ) ) {
|
|
|
|
|
// Correct for old double-serialization bug.
|
|
|
|
|
$obj = unserialize( $obj );
|
|
|
|
|
}
|
2006-01-07 13:31:29 +00:00
|
|
|
|
2005-05-08 08:17:12 +00:00
|
|
|
// Save this item for reference; if pulling many
|
|
|
|
|
// items in a row we'll likely use it again.
|
|
|
|
|
$obj->uncompress();
|
2006-06-08 15:12:26 +00:00
|
|
|
$wgBlobCache = array( $this->mOldId => $obj );
|
2004-10-30 14:39:40 +00:00
|
|
|
}
|
|
|
|
|
return $obj->getItem( $this->mHash );
|
|
|
|
|
}
|
|
|
|
|
|
2005-01-27 19:51:47 +00:00
|
|
|
/** @todo document */
|
2004-10-30 14:39:40 +00:00
|
|
|
function getHash() {
|
|
|
|
|
return $this->mHash;
|
2004-06-15 15:05:56 +00:00
|
|
|
}
|
|
|
|
|
}
|
2005-05-06 11:31:18 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* To speed up conversion from 1.4 to 1.5 schema, text rows can refer to the
|
|
|
|
|
* leftover cur table as the backend. This avoids expensively copying hundreds
|
|
|
|
|
* of megabytes of data during the conversion downtime.
|
|
|
|
|
*
|
|
|
|
|
* Serialized HistoryBlobCurStub objects will be inserted into the text table
|
|
|
|
|
* on conversion if $wgFastSchemaUpgrades is set to true.
|
|
|
|
|
*/
|
|
|
|
|
class HistoryBlobCurStub {
|
2006-05-11 22:40:38 +00:00
|
|
|
var $mCurId;
|
2005-05-06 11:31:18 +00:00
|
|
|
|
|
|
|
|
/** @todo document */
|
|
|
|
|
function HistoryBlobCurStub( $curid = 0 ) {
|
|
|
|
|
$this->mCurId = $curid;
|
|
|
|
|
}
|
2006-01-07 13:31:29 +00:00
|
|
|
|
2005-05-06 11:31:18 +00:00
|
|
|
/**
|
|
|
|
|
* Sets the location (cur_id) of the main object to which this object
|
|
|
|
|
* points
|
|
|
|
|
*/
|
|
|
|
|
function setLocation( $id ) {
|
|
|
|
|
$this->mCurId = $id;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** @todo document */
|
|
|
|
|
function getText() {
|
2007-01-22 23:50:42 +00:00
|
|
|
$dbr = wfGetDB( DB_SLAVE );
|
2005-05-06 11:31:18 +00:00
|
|
|
$row = $dbr->selectRow( 'cur', array( 'cur_text' ), array( 'cur_id' => $this->mCurId ) );
|
|
|
|
|
if( !$row ) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return $row->cur_text;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2005-05-08 08:17:12 +00:00
|
|
|
|
2007-06-29 01:19:14 +00:00
|
|
|
|