Page MenuHomeWickedGov Phorge

MockDataAccess.php
No OneTemporary

Size
17 KB
Referenced Files
None
Subscribers
None

MockDataAccess.php

<?php
declare( strict_types = 1 );
namespace Wikimedia\Parsoid\Mocks;
use Error;
use Wikimedia\Parsoid\Config\DataAccess;
use Wikimedia\Parsoid\Config\PageConfig;
use Wikimedia\Parsoid\Config\PageContent;
use Wikimedia\Parsoid\Config\SiteConfig;
use Wikimedia\Parsoid\Core\ContentMetadataCollector;
use Wikimedia\Parsoid\Core\LinkTarget;
use Wikimedia\Parsoid\ParserTests\MockApiHelper;
use Wikimedia\Parsoid\Utils\PHPUtils;
use Wikimedia\Parsoid\Utils\Title;
use Wikimedia\Parsoid\Utils\TitleValue;
/**
* This implements some of the functionality that the tests/ParserTests/MockAPIHelper.php
* provides. While originally implemented to support ParserTests, this is no longer used
* by parser tests.
*/
class MockDataAccess extends DataAccess {
private SiteConfig $siteConfig;
private static $PAGE_DATA = [
"Main_Page" => [
"title" => "Main Page",
"pageid" => 1,
"ns" => 0,
"revid" => 1,
"parentid" => 0,
'slots' => [
'main' => [
'contentmodel' => 'wikitext',
'contentformat' => 'text/x-wiki',
// phpcs:ignore Generic.Files.LineLength.TooLong
'*' => "<strong>MediaWiki has been successfully installed.</strong>\n\nConsult the [//meta.wikimedia.org/wiki/Help:Contents User's Guide] for information on using the wiki software.\n\n== Getting started ==\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Configuration_settings Configuration settings list]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ MediaWiki FAQ]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce MediaWiki release mailing list]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Localisation#Translation_resources Localise MediaWiki for your language]"
]
]
],
"Junk_Page" => [
"title" => "Junk Page",
"pageid" => 2,
"ns" => 0,
"revid" => 2,
"parentid" => 0,
'slots' => [
'main' => [
'contentmodel' => 'wikitext',
'contentformat' => 'text/x-wiki',
'*' => '2. This is just some junk. See the comment above.'
]
]
],
"Large_Page" => [
"title" => "Large_Page",
"pageid" => 3,
"ns" => 0,
"revid" => 3,
"parentid" => 0,
'slots' => [
'main' => [
'contentmodel' => 'wikitext',
'contentformat' => 'text/x-wiki',
'*' => '', // Will be fixed up in the constructor
]
]
],
"Reuse_Page" => [
"title" => "Reuse_Page",
"pageid" => 100,
"ns" => 0,
"revid" => 100,
"parentid" => 0,
'slots' => [
'main' => [
'contentmodel' => 'wikitext',
'contentformat' => 'text/x-wiki',
'*' => '{{colours of the rainbow}}'
]
]
],
"JSON_page" => [
"title" => "JSON_Page",
"pageid" => 101,
"ns" => 0,
"revid" => 101,
"parentid" => 0,
'slots' => [
'main' => [
'contentmodel' => 'json',
'contentformat' => 'text/json',
'*' => '[1]'
]
]
],
"Lint_Page" => [
"title" => "Lint Page",
"pageid" => 102,
"ns" => 0,
"revid" => 102,
"parentid" => 0,
'slots' => [
'main' => [
'contentmodel' => 'wikitext',
'contentformat' => 'text/x-wiki',
'*' => "{|\nhi\n|ho\n|}"
]
]
],
"Redlinks_Page" => [
"title" => "Redlinks Page",
"pageid" => 103,
"ns" => 0,
"revid" => 103,
"parentid" => 0,
'slots' => [
'main' => [
'contentmodel' => 'wikitext',
'contentformat' => 'text/x-wiki',
'*' => '[[Special:Version]] [[Doesnotexist]] [[Redirected]]'
]
]
],
"Variant_Page" => [
"title" => "Variant Page",
"pageid" => 104,
"ns" => 0,
"revid" => 104,
"parentid" => 0,
'pagelanguage' => 'sr',
'pagelanguagedir' => 'ltr',
'slots' => [
'main' => [
'contentmodel' => 'wikitext',
'contentformat' => 'text/x-wiki',
'*' => "абвг abcd"
]
]
],
"No_Variant_Page" => [
"title" => "No Variant Page",
"pageid" => 105,
"ns" => 0,
"revid" => 105,
"parentid" => 0,
'pagelanguage' => 'sr',
'pagelanguagedir' => 'ltr',
'slots' => [
'main' => [
'contentmodel' => 'wikitext',
'contentformat' => 'text/x-wiki',
'*' => "абвг abcd\n__NOCONTENTCONVERT__"
]
]
],
"Revision_ID" => [
"title" => "Revision ID",
"pageid" => 63,
"ns" => 0,
"revid" => 63,
"parentid" => 0,
'pagelanguage' => 'sr',
'pagelanguagedir' => 'ltr',
'slots' => [
'main' => [
'contentmodel' => 'wikitext',
'contentformat' => 'text/x-wiki',
'*' => '{{REVISIONID}}'
]
]
],
"Redirected" => [
"title" => "Revision ID",
"pageid" => 63,
"ns" => 0,
"revid" => 64,
"parentid" => 0,
"redirect" => true,
],
"Disambiguation" => [
"title" => "Disambiguation Page",
"pageid" => 106,
"ns" => 0,
"revid" => 106,
"parentid" => 0,
'slots' => [
'main' => [
'contentmodel' => 'wikitext',
'contentformat' => 'text/x-wiki',
'*' => "This is a mock disambiguation page with no more info!"
]
],
"linkclasses" => [
"mw-disambig",
]
],
"Special:Version" => [
"title" => "Version",
"pageid" => 107,
"ns" => -1,
"revid" => 107,
"parentid" => 0,
'slots' => [
'main' => [
'contentmodel' => 'wikitext',
'contentformat' => 'text/x-wiki',
'*' => "This is a mock special page."
]
],
]
];
// This templatedata description only provides a subset of fields
// that mediawiki API returns. Parsoid only uses the format and
// paramOrder fields at this point, so keeping these lean.
private const TEMPLATE_DATA = [
'Template:NoFormatWithParamOrder' => [
'paramOrder' => [ 'f0', 'f1', 'unused2', 'f2', 'unused3' ]
],
'Template:InlineTplNoParamOrder' => [
'format' => 'inline'
],
'Template:BlockTplNoParamOrder' => [
'format' => 'block'
],
'Template:InlineTplWithParamOrder' => [
'format' => 'inline',
'paramOrder' => [ 'f1', 'f2' ]
],
'Template:BlockTplWithParamOrder' => [
'format' => 'block',
'paramOrder' => [ 'f1', 'f2' ]
],
'Template:WithParamOrderAndAliases' => [
'params' => [
'f1' => [ 'aliases' => [ 'f4', 'f3' ] ]
],
'paramOrder' => [ 'f1', 'f2' ]
],
'Template:InlineFormattedTpl_1' => [
'format' => '{{_|_=_}}'
],
'Template:InlineFormattedTpl_2' => [
'format' => "\n{{_ | _ = _}}"
],
'Template:InlineFormattedTpl_3' => [
'format' => '{{_| _____ = _}}'
],
'Template:BlockFormattedTpl_1' => [
'format' => "{{_\n| _ = _\n}}"
],
'Template:BlockFormattedTpl_2' => [
'format' => "\n{{_\n| _ = _\n}}\n"
],
'Template:BlockFormattedTpl_3' => [
'format' => "{{_|\n _____ = _}}"
]
];
private const FNAMES = [
'Image:Foobar.jpg' => 'Foobar.jpg',
'File:Foobar.jpg' => 'Foobar.jpg',
'Archivo:Foobar.jpg' => 'Foobar.jpg',
'Mynd:Foobar.jpg' => 'Foobar.jpg',
"Датотека:Foobar.jpg" => 'Foobar.jpg',
'Image:Foobar.svg' => 'Foobar.svg',
'File:Foobar.svg' => 'Foobar.svg',
'Image:Thumb.png' => 'Thumb.png',
'File:Thumb.png' => 'Thumb.png',
'File:LoremIpsum.djvu' => 'LoremIpsum.djvu',
'File:Video.ogv' => 'Video.ogv',
'File:Audio.oga' => 'Audio.oga',
'File:Bad.jpg' => 'Bad.jpg',
];
private const PNAMES = [
'Image:Foobar.jpg' => 'File:Foobar.jpg',
'Image:Foobar.svg' => 'File:Foobar.svg',
'Image:Thumb.png' => 'File:Thumb.png'
];
// configuration to match PHP parserTests
// Note that parserTests use a MockLocalRepo with
// url=>'http://example.com/images' although $wgServer="http://example.org"
private const IMAGE_BASE_URL = 'http://example.com/images';
private const IMAGE_DESC_URL = self::IMAGE_BASE_URL;
private const FILE_PROPS = [
'Foobar.jpg' => [
'size' => 7881,
'width' => 1941,
'height' => 220,
'bits' => 8,
'mime' => 'image/jpeg',
'sha1' => '0000000000000000000000000000001', // Wikimedia\base_convert( '1', 16, 36, 31 )
'timestamp' => '20010115123500',
],
'Thumb.png' => [
'size' => 22589,
'width' => 135,
'height' => 135,
'bits' => 8,
'mime' => 'image/png',
'sha1' => '0000000000000000000000000000002', // Wikimedia\base_convert( '2', 16, 36, 31 )
'timestamp' => '20130225203040',
],
'Foobar.svg' => [
'size' => 12345,
'width' => 240,
'height' => 180,
'bits' => 24,
'mime' => 'image/svg+xml',
'sha1' => null, // Wikimedia\base_convert( '', 16, 36, 31 ) returns false
'timestamp' => '20010115123500',
],
'Bad.jpg' => [
'size' => 12345,
'width' => 320,
'height' => 240,
'bits' => 24,
'mime' => 'image/jpeg',
'sha1' => '0000000000000000000000000000003', // Wikimedia\base_convert( '3', 16, 36, 31 )
'timestamp' => '20010115123500',
],
'LoremIpsum.djvu' => [
'size' => 3249,
'width' => 2480,
'height' => 3508,
'bits' => 8,
'mime' => 'image/vnd.djvu',
'sha1' => null, // Wikimedia\base_convert( '', 16, 36, 31 ) returns false
'timestamp' => '20010115123600',
],
'Video.ogv' => [
'size' => 12345,
'width' => 320,
'height' => 240,
'bits' => 0,
# duration comes from
# TimedMediaHandler/tests/phpunit/mocks/MockOggHandler::getLength()
'duration' => 4.3666666666667,
'mime' => 'video/ogg; codecs="theora"',
'mediatype' => 'VIDEO',
'thumbtimes' => [
'1.2' => 'seek%3D1.2',
'85' => 'seek%3D3.3666666666667', # hard limited by duration
],
'sha1' => null, // Wikimedia\base_convert( '', 16, 36, 31 ) returns false
'timestamp' => '20010115123500',
],
'Audio.oga' => [
'size' => 12345,
'width' => 0,
'height' => 0,
'bits' => 0,
# duration comes from
# TimedMediaHandler/tests/phpunit/mocks/MockOggHandler::getLength()
'duration' => 0.99875,
'mime' => 'audio/ogg; codecs="vorbis"',
'mediatype' => 'AUDIO',
'sha1' => null, // Wikimedia\base_convert( '', 16, 36, 31 ) returns false
'timestamp' => '20010115123500',
]
];
/**
* @param string|LinkTarget $title
* @return string
*/
private function normTitle( $title ): string {
if ( !is_string( $title ) ) {
$title = Title::newFromLinkTarget(
$title, $this->siteConfig
);
return $title->getPrefixedDBKey();
}
return strtr( $title, ' ', '_' );
}
/**
* @param SiteConfig $siteConfig
* @param array $opts
*/
public function __construct( SiteConfig $siteConfig, array $opts ) {
$this->siteConfig = $siteConfig;
// Update data of the large page
$mainSlot = &self::$PAGE_DATA['Large_Page']['slots']['main'];
$mainSlot['*'] = str_repeat( 'a', $opts['maxWikitextSize'] ?? 1000000 );
}
/** @inheritDoc */
public function getPageInfo( $pageConfigOrTitle, array $titles ): array {
$ret = [];
foreach ( $titles as $title ) {
$normTitle = $this->normTitle( $title );
$pageData = self::$PAGE_DATA[$normTitle] ?? null;
$ret[$title] = [
'pageId' => $pageData['pageid'] ?? null,
'revId' => $pageData['revid'] ?? null,
'missing' => $pageData === null,
'known' => $pageData !== null || ( $pageData['known'] ?? false ),
'redirect' => $pageData['redirect'] ?? false,
'linkclasses' => $pageData['linkclasses'] ?? [],
];
}
return $ret;
}
/** @inheritDoc */
public function getFileInfo( PageConfig $pageConfig, array $files ): array {
$ret = [];
foreach ( $files as $f ) {
$name = $f[0];
$dims = $f[1];
// From mockAPI.js
$normFileName = self::FNAMES[$name] ?? $name;
$props = self::FILE_PROPS[$normFileName] ?? null;
if ( $props === null ) {
// We don't have info for this file
$ret[] = null;
continue;
}
$md5 = md5( $normFileName );
$md5prefix = $md5[0] . '/' . $md5[0] . $md5[1] . '/';
$baseurl = self::IMAGE_BASE_URL . '/' . $md5prefix . $normFileName;
$height = $props['height'] ?? 220;
$width = $props['width'] ?? 1941;
$turl = self::IMAGE_BASE_URL . '/thumb/' . $md5prefix . $normFileName;
$durl = self::IMAGE_DESC_URL . '/' . $normFileName;
$mediatype = $props['mediatype'] ??
( $props['mime'] === 'image/svg+xml' ? 'DRAWING' : 'BITMAP' );
$info = [
'size' => $props['size'] ?? 12345,
'height' => $height,
'width' => $width,
'url' => $baseurl,
'descriptionurl' => $durl,
'mediatype' => $mediatype,
'mime' => $props['mime'],
'badFile' => ( $normFileName === 'Bad.jpg' ),
'sha1' => $props['sha1'],
'timestamp' => $props['timestamp'],
];
if ( isset( $props['duration'] ) ) {
$info['duration'] = $props['duration'];
}
// See Config/Api/DataAccess.php
$txopts = [
'width' => null,
'height' => null,
];
if ( isset( $dims['width'] ) && $dims['width'] !== null ) {
$txopts['width'] = $dims['width'];
if ( isset( $dims['page'] ) ) {
$txopts['page'] = $dims['page'];
}
if ( isset( $dims['lang'] ) ) {
$txopts['lang'] = $dims['lang'];
}
}
if ( isset( $dims['height'] ) && $dims['height'] !== null ) {
$txopts['height'] = $dims['height'];
}
if ( isset( $dims['seek'] ) ) {
$txopts['thumbtime'] = $dims['seek'];
}
// From mockAPI.js
if ( $mediatype === 'VIDEO' && empty( $txopts['height'] ) && empty( $txopts['width'] ) ) {
$txopts['width'] = $width;
$txopts['height'] = $height;
}
if ( !empty( $txopts['height'] ) || !empty( $txopts['width'] ) ) {
// Set $txopts['width'] and $txopts['height']
$rtwidth = &$txopts['width'];
$rtheight = &$txopts['height'];
MockApiHelper::transformHelper( $width, $height, $rtwidth, $rtheight );
$urlWidth = $txopts['width'];
if ( $txopts['width'] > $width ) {
// The PHP api won't enlarge a bitmap ... but the batch api will.
// But, to match the PHP sections, don't scale.
if ( $mediatype !== 'DRAWING' ) {
$urlWidth = $width;
}
}
if ( $urlWidth !== $width || $mediatype === 'AUDIO' || $mediatype === 'VIDEO' ) {
$turl .= '/' . $urlWidth . 'px-';
if ( $mediatype === 'VIDEO' ) {
// Hack in a 'seek' option, if provided (T258767)
if ( isset( $txopts['thumbtime'] ) ) {
$turl .= $props['thumbtimes'][strval( $txopts['thumbtime'] )] ?? '';
}
$turl .= '-';
}
$turl .= $normFileName;
switch ( $mediatype ) {
case 'AUDIO':
// No thumbs are generated for audio
$turl = self::IMAGE_BASE_URL . '/w/resources/assets/file-type-icons/fileicon-ogg.png';
break;
case 'VIDEO':
$turl .= '.jpg';
break;
case 'DRAWING':
$turl .= '.png';
break;
}
} else {
$turl = $baseurl;
}
$info['thumbwidth'] = $txopts['width'];
$info['thumbheight'] = $txopts['height'];
$info['thumburl'] = $turl;
}
$ret[] = $info;
}
return $ret;
}
/** @inheritDoc */
public function doPst( PageConfig $pageConfig, string $wikitext ): string {
// FIXME: This is all mockAPI does
return preg_replace( '/\{\{subst:1x\|([^}]+)\}\}/', '$1', $wikitext, 1 );
}
/** @inheritDoc */
public function parseWikitext(
PageConfig $pageConfig,
ContentMetadataCollector $metadata,
string $wikitext
): string {
// Render to html the contents of known extension tags
preg_match( '#<([A-Za-z][^\t\n\v />\0]*)#', $wikitext, $match );
switch ( $match[1] ) {
case 'templatestyles':
// Silliness
$html = "<style data-mw-deduplicate='TemplateStyles:r123456'>" .
"small { font-size: 120% } big { font-size: 80% }</style>";
break;
case 'translate':
$html = $wikitext;
break;
case 'indicator':
case 'section':
$html = "";
break;
default:
throw new Error( 'Unhandled extension type encountered in: ' . $wikitext );
}
return $html;
}
/** @inheritDoc */
public function preprocessWikitext(
PageConfig $pageConfig,
ContentMetadataCollector $metadata,
string $wikitext
): string {
$revid = $pageConfig->getRevisionId();
$expanded = str_replace( '{{!}}', '|', $wikitext );
preg_match( '/{{1x\|(.*?)}}/s', $expanded, $match1 );
preg_match( '/{{#tag:ref\|(.*?)\|(.*?)}}/s', $expanded, $match2 );
if ( $match1 ) {
$ret = $match1[1];
} elseif ( $match2 ) {
$ret = "<ref {$match2[2]}>{$match2[1]}</ref>";
} elseif ( $wikitext === '{{colours of the rainbow}}' ) {
$ret = 'purple';
} elseif ( $wikitext === '{{REVISIONID}}' ) {
$ret = (string)$revid;
} elseif ( $wikitext === '{{mangle}}' ) {
$ret = 'hi';
$metadata->addCategory(
Title::newFromText( 'Category:Mangle', $this->siteConfig ),
'ho'
);
} else {
$ret = '';
}
return $ret;
}
/** @inheritDoc */
public function fetchTemplateSource(
PageConfig $pageConfig, LinkTarget $title
): ?PageContent {
$normTitle = $this->normTitle( $title );
$pageData = self::$PAGE_DATA[$normTitle] ?? null;
if ( $pageData ) {
$content = [];
foreach ( $pageData['slots'] as $role => $data ) {
$content['role'] = $data['*'];
}
return new MockPageContent( $content );
} else {
return null;
}
}
/** @inheritDoc */
public function fetchTemplateData( PageConfig $pageConfig, LinkTarget $title ): ?array {
return self::TEMPLATE_DATA[$this->normTitle( $title )] ?? null;
}
/** @inheritDoc */
public function logLinterData(
PageConfig $pageConfig, array $lints
): void {
foreach ( $lints as $l ) {
error_log( PHPUtils::jsonEncode( $l ) );
}
}
private const TRACKING_CATEGORIES = [
'broken-file-category' => 'Pages with broken file links',
'magiclink-tracking-rfc' => 'Pages using RFC magic links',
'magiclink-tracking-isbn' => 'Pages using ISBN magic links',
'magiclink-tracking-pmid' => 'Pages using PMID magic links',
];
/** @inheritDoc */
public function addTrackingCategory(
PageConfig $pageConfig,
ContentMetadataCollector $metadata,
string $key
): void {
if ( !isset( self::TRACKING_CATEGORIES[$key] ) ) {
throw new Error( 'Unknown tracking category: ' . $key );
}
$tv = TitleValue::tryNew(
14, // NS_CATEGORY,
self::TRACKING_CATEGORIES[$key]
);
$metadata->addCategory( $tv );
}
}

File Metadata

Mime Type
text/x-php
Expires
Aug 18 2026, 06:39 (7 w, 3 d ago)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
54/3d/e2b80b8479067665bf3bec7e3168
Default Alt Text
MockDataAccess.php (17 KB)

Event Timeline