diff --git a/RELEASE-NOTES-1.39 b/RELEASE-NOTES-1.39 index 68e3181edf1..0b250f011e2 100644 --- a/RELEASE-NOTES-1.39 +++ b/RELEASE-NOTES-1.39 @@ -45,6 +45,9 @@ For notes on 1.38.x and older releases, see HISTORY. Use the MW_WIKI_NAME environment variable to specifiy the name of the site to load configuration for. Using the WIKI_NAME environment variable for this purpose is deprecated. +* $wgParserCacheUseJson - the ParserCache now always uses JSON serialization. + Reading old non-JSON cache entries is still supported. The setting had been + deprecated since 1.36. * $wgAllowJavaUploads - To allow uploads of JAR files, remove application/java from $wgMimeTypeExclusions. * $wgMaxRedirects has been removed. This feature never worked as intended, diff --git a/docs/config-schema.yaml b/docs/config-schema.yaml index 523822362d9..c242d324a65 100644 --- a/docs/config-schema.yaml +++ b/docs/config-schema.yaml @@ -2567,21 +2567,6 @@ config-schema: ``` If set to false, the mtime for each individual JSON file will be checked, which can be slow if a large number of extensions are being loaded. - ParserCacheUseJson: - default: true - deprecated: 'since 1.36' - description: |- - Enable JSON serialization for ParserCache. - In 1.36 the default serialization format for ParserCache has been changed from PHP - serialization to JSON serialization. The cache is still compatible with old PHP-serialized - entries, so for the most part the change should be unnoticed. However in case some - extensions are installed which write non-JSON-serializable data to - ParserOutput::setExtensionData, the cache will break for some pages. Setting this to 'false' - makes ParserCache use PHP serialization format for writing new cache entries, and all the - cache entries already written in JSON are discarded. - @since 1.36 - @deprecated since 1.36 - @see https://phabricator.wikimedia.org/T263579 EnableRemoteBagOStuffTests: default: false description: |- diff --git a/includes/MainConfigNames.php b/includes/MainConfigNames.php index 08d69bb7ca8..d0961d4beb0 100644 --- a/includes/MainConfigNames.php +++ b/includes/MainConfigNames.php @@ -1628,13 +1628,6 @@ class MainConfigNames { */ public const ExtensionInfoMTime = 'ExtensionInfoMTime'; - /** - * Name constant for the ParserCacheUseJson setting, for use with Config::get() - * @see MainConfigSchema::ParserCacheUseJson - * @deprecated since 1.36 - */ - public const ParserCacheUseJson = 'ParserCacheUseJson'; - /** * Name constant for the EnableRemoteBagOStuffTests setting, for use with Config::get() * @see MainConfigSchema::EnableRemoteBagOStuffTests diff --git a/includes/MainConfigSchema.php b/includes/MainConfigSchema.php index 0523f073a67..8d3a0f90311 100644 --- a/includes/MainConfigSchema.php +++ b/includes/MainConfigSchema.php @@ -4017,26 +4017,6 @@ class MainConfigSchema { 'type' => 'integer|false', ]; - /** - * Enable JSON serialization for ParserCache. - * - * In 1.36 the default serialization format for ParserCache has been changed from PHP - * serialization to JSON serialization. The cache is still compatible with old PHP-serialized - * entries, so for the most part the change should be unnoticed. However in case some - * extensions are installed which write non-JSON-serializable data to - * ParserOutput::setExtensionData, the cache will break for some pages. Setting this to 'false' - * makes ParserCache use PHP serialization format for writing new cache entries, and all the - * cache entries already written in JSON are discarded. - * - * @since 1.36 - * @deprecated since 1.36 - * @see https://phabricator.wikimedia.org/T263579 - */ - public const ParserCacheUseJson = [ - 'default' => true, - 'deprecated' => 'since 1.36', - ]; - /** * If this is set to true, phpunit will run integration tests against remote * caches defined in $wgObjectCaches. diff --git a/includes/config-schema.php b/includes/config-schema.php index 6516d869c8f..835e045674a 100644 --- a/includes/config-schema.php +++ b/includes/config-schema.php @@ -538,7 +538,6 @@ return [ 'UseGzip' => false, 'InvalidateCacheOnLocalSettingsChange' => true, 'ExtensionInfoMTime' => false, - 'ParserCacheUseJson' => true, 'EnableRemoteBagOStuffTests' => false, 'UseCdn' => false, 'VaryOnXFP' => false, @@ -2889,9 +2888,6 @@ return [ 'ContentHandlerTextFallback' => [ 'deprecated' => 'since 1.37', ], - 'ParserCacheUseJson' => [ - 'deprecated' => 'since 1.36', - ], 'SquidPurgeUseHostHeader' => [ 'deprecated' => 'since 1.33', ], diff --git a/includes/config-vars.php b/includes/config-vars.php index 80ffba98c63..9e69f0ce1b5 100644 --- a/includes/config-vars.php +++ b/includes/config-vars.php @@ -1612,13 +1612,6 @@ $wgInvalidateCacheOnLocalSettingsChange = null; */ $wgExtensionInfoMTime = null; -/** - * Config variable stub for the ParserCacheUseJson setting, for use by phpdoc and IDEs. - * @see MediaWiki\MainConfigSchema::ParserCacheUseJson - * @deprecated since 1.36 - */ -$wgParserCacheUseJson = null; - /** * Config variable stub for the EnableRemoteBagOStuffTests setting, for use by phpdoc and IDEs. * @see MediaWiki\MainConfigSchema::EnableRemoteBagOStuffTests diff --git a/includes/parser/ParserCache.php b/includes/parser/ParserCache.php index 40246d17e8d..092a50e287c 100644 --- a/includes/parser/ParserCache.php +++ b/includes/parser/ParserCache.php @@ -121,18 +121,6 @@ class ParserCache { */ private $metadataProcCache; - /** - * @note Temporary feature flag, remove before 1.36 is released. - * @var bool - */ - private $writeJson = false; - - /** - * @note Temporary feature flag, remove before 1.36 is released. - * @var bool - */ - private $readJson = false; - /** * Setup a cache pathway with a given back-end storage mechanism. * @@ -148,7 +136,6 @@ class ParserCache { * @param LoggerInterface $logger * @param TitleFactory $titleFactory * @param WikiPageFactory $wikiPageFactory - * @param bool $useJson Temporary feature flag, remove before 1.36 is released. */ public function __construct( string $name, @@ -159,8 +146,7 @@ class ParserCache { IBufferingStatsdDataFactory $stats, LoggerInterface $logger, TitleFactory $titleFactory, - WikiPageFactory $wikiPageFactory, - $useJson = false + WikiPageFactory $wikiPageFactory ) { $this->name = $name; $this->cache = $cache; @@ -171,8 +157,6 @@ class ParserCache { $this->logger = $logger; $this->titleFactory = $titleFactory; $this->wikiPageFactory = $wikiPageFactory; - $this->readJson = $useJson; - $this->writeJson = $useJson; $this->metadataProcCache = new HashBagOStuff( [ 'maxKeys' => 2 ] ); } @@ -252,7 +236,7 @@ class ParserCache { // NOTE: Support for reading string values from the cache must be // deployed a while before starting to write JSON to the cache, // in case we have to revert either change. - if ( is_string( $metadata ) && $this->readJson ) { + if ( is_string( $metadata ) ) { $metadata = $this->restoreFromJson( $metadata, $pageKey, CacheTime::class ); } @@ -369,7 +353,7 @@ class ParserCache { // NOTE: Support for reading string values from the cache must be // deployed a while before starting to write JSON to the cache, // in case we have to revert either change. - if ( is_string( $value ) && $this->readJson ) { + if ( is_string( $value ) ) { $value = $this->restoreFromJson( $value, $parserOutputKey, ParserOutput::class ); } @@ -464,23 +448,12 @@ class ParserCache { $msg = "Saved in parser cache with key $parserOutputKey" . " and timestamp $cacheTime" . " and revision id $revId."; - if ( $this->writeJson ) { - $msg .= " Serialized with JSON."; - } else { - $msg .= " Serialized with PHP."; - } $parserOutput->addCacheMessage( $msg ); $pageKey = $this->makeMetadataKey( $page ); - if ( $this->writeJson ) { - $parserOutputData = $this->encodeAsJson( $parserOutput, $parserOutputKey ); - $metadataData = $this->encodeAsJson( $metadata, $pageKey ); - } else { - // rely on implicit PHP serialization in the cache - $parserOutputData = $parserOutput; - $metadataData = $metadata; - } + $parserOutputData = $this->convertForCache( $parserOutput, $parserOutputKey ); + $metadataData = $this->convertForCache( $metadata, $pageKey ); if ( !$parserOutputData || !$metadataData ) { $this->logger->warning( @@ -585,17 +558,6 @@ class ParserCache { return false; } - /** - * @note setter for temporary feature flags, for use in testing. - * @internal - * @param bool $readJson - * @param bool $writeJson - */ - public function setJsonSupport( bool $readJson, bool $writeJson ): void { - $this->readJson = $readJson; - $this->writeJson = $writeJson; - } - /** * @param string $jsonData * @param string $key @@ -622,7 +584,7 @@ class ParserCache { * @param string $key * @return string|null */ - private function encodeAsJson( CacheTime $obj, string $key ) { + protected function convertForCache( CacheTime $obj, string $key ) { try { return $this->jsonCodec->serialize( $obj ); } catch ( InvalidArgumentException $e ) { diff --git a/includes/parser/ParserCacheFactory.php b/includes/parser/ParserCacheFactory.php index b4a3aac0215..5b9ea2e8e5d 100644 --- a/includes/parser/ParserCacheFactory.php +++ b/includes/parser/ParserCacheFactory.php @@ -80,7 +80,6 @@ class ParserCacheFactory { * @internal */ public const CONSTRUCTOR_OPTIONS = [ - MainConfigNames::ParserCacheUseJson, // Temporary feature flag, remove before 1.36 is released. MainConfigNames::CacheEpoch, MainConfigNames::OldRevisionParserCacheExpireTime, ]; @@ -137,8 +136,7 @@ class ParserCacheFactory { $this->stats, $this->logger, $this->titleFactory, - $this->wikiPageFactory, - $this->options->get( MainConfigNames::ParserCacheUseJson ) + $this->wikiPageFactory ); $this->parserCaches[$name] = $cache; diff --git a/includes/parser/ParserOutput.php b/includes/parser/ParserOutput.php index 4fa2eed6923..ab2b2f2db3c 100644 --- a/includes/parser/ParserOutput.php +++ b/includes/parser/ParserOutput.php @@ -1501,15 +1501,6 @@ class ParserOutput extends CacheTime implements ContentMetadataCollector { * $output->getExtensionData( 'my_ext_foo' ); * @endcode * - * In MediaWiki 1.20 and older, you have to use a custom member variable - * within the ParserOutput object, but this will not work in modern - * MediaWiki with $wgParserCacheUseJson set to true (the default): - * - * @par Example: - * @code - * $parser->getOutput()->my_ext_foo = '...'; - * @endcode - * * @param string $name * @param int|float|string|bool|null $value * @since 1.38 diff --git a/tests/phpunit/includes/parser/ParserCacheTest.php b/tests/phpunit/includes/parser/ParserCacheTest.php index 033e7b53d0c..9664b1b3978 100644 --- a/tests/phpunit/includes/parser/ParserCacheTest.php +++ b/tests/phpunit/includes/parser/ParserCacheTest.php @@ -3,6 +3,7 @@ namespace MediaWiki\Tests\Parser; use BagOStuff; +use CacheTime; use EmptyBagOStuff; use HashBagOStuff; use InvalidArgumentException; @@ -484,7 +485,7 @@ class ParserCacheTest extends MediaWikiIntegrationTestCase { 'RejectParserCacheValue' => function ( ParserOutput $value, WikiPage $hookPage, ParserOptions $popts ) use ( $wikiPageMock, $parserOutput, $options ) { - $this->assertSame( $parserOutput, $value ); + $this->assertEquals( $parserOutput, $value ); $this->assertSame( $wikiPageMock, $hookPage ); $this->assertSame( $options, $popts ); return false; @@ -569,12 +570,11 @@ class ParserCacheTest extends MediaWikiIntegrationTestCase { } public function provideCorruptData() { - yield 'PHP serialization, bad data' => [ false, 'bla bla' ]; - yield 'JSON serialization, bad data' => [ true, 'bla bla' ]; - yield 'JSON serialization, no _class_' => [ true, '{"test":"test"}' ]; - yield 'JSON serialization, non-existing _class_' => [ true, '{"_class_":"NonExistentBogusClass"}' ]; + yield 'JSON serialization, bad data' => [ 'bla bla' ]; + yield 'JSON serialization, no _class_' => [ '{"test":"test"}' ]; + yield 'JSON serialization, non-existing _class_' => [ '{"_class_":"NonExistentBogusClass"}' ]; $wrongInstance = new JsonUnserializableSuperClass( 'test' ); - yield 'JSON serialization, wrong class' => [ true, json_encode( $wrongInstance->jsonSerialize() ) ]; + yield 'JSON serialization, wrong class' => [ json_encode( $wrongInstance->jsonSerialize() ) ]; } /** @@ -586,12 +586,10 @@ class ParserCacheTest extends MediaWikiIntegrationTestCase { * @dataProvider provideCorruptData * @covers ParserCache::get * @covers ParserCache::restoreFromJson - * @param bool $json * @param string $data */ - public function testCorruptData( bool $json, string $data ) { + public function testCorruptData( string $data ) { $cache = $this->createParserCache( null, new HashBagOStuff() ); - $cache->setJsonSupport( $json, $json ); $parserOutput = new ParserOutput( 'TEST_TEXT' ); $options1 = ParserOptions::newCanonical( 'canonical' ); @@ -639,14 +637,38 @@ class ParserCacheTest extends MediaWikiIntegrationTestCase { } /** - * Test whats happen when we turn on JSON support but there - * are still old entries in the cache. + * Test what happens when upgrading from 1.35 or earlier, + * when old cache entries do not yet use JSON. * * @covers ParserCache::get */ public function testMigrationToJson() { - $cache = $this->createParserCache(); - $cache->setJsonSupport( false, false ); + $bagOStuff = new HashBagOStuff(); + + $cache = $this->getMockBuilder( ParserCache::class ) + ->setConstructorArgs( [ + 'test', + $bagOStuff, + '19900220000000', + $this->createHookContainer( [] ), + new JsonCodec(), + new NullStatsdDataFactory(), + new NullLogger(), + $this->getServiceContainer()->getTitleFactory(), + $this->getServiceContainer()->getWikiPageFactory() + ] ) + ->onlyMethods( [ 'convertForCache' ] ) + ->getMock(); + + // Emulate pre-1.36 behavior: rely on native PHP serialization. + // Note that backwards compatibility of the actual serialization is covered + // by ParserOutputTest which uses various versions of serialized data + // under tests/phpunit/data/ParserCache. + $cache->method( 'convertForCache' )->willReturnCallback( + static function ( CacheTime $obj, string $key ) { + return $obj; + } + ); $parserOutput1 = new ParserOutput( 'Lorem Ipsum' ); @@ -654,7 +676,7 @@ class ParserCacheTest extends MediaWikiIntegrationTestCase { $cache->save( $parserOutput1, $this->page, $options, $this->cacheTime ); // emulate migration to JSON - $cache->setJsonSupport( true, true ); + $cache = $this->createParserCache( null, $bagOStuff ); // make sure we can load non-json cache data $cachedOutput = $cache->get( $this->page, $options ); @@ -670,50 +692,11 @@ class ParserCacheTest extends MediaWikiIntegrationTestCase { } /** - * Test whats happen when we have to roll back from supporting JSON - * to not supporting JSON. - * - * @covers ParserCache::get - */ - public function testRollbackFromJson() { - $cache = $this->createParserCache(); - $cache->setJsonSupport( true, true ); - - $parserOutput1 = new ParserOutput( 'Lorem Ipsum' ); - - $options = ParserOptions::newCanonical( 'canonical' ); - $cache->save( $parserOutput1, $this->page, $options, $this->cacheTime ); - - // emulate rolling back to not writing but still reading JSON - $cache->setJsonSupport( true, false ); - - // make sure we can load json cache data - $cachedOutput = $cache->get( $this->page, $options ); - $this->assertEquals( $parserOutput1, $cachedOutput ); - - // emulate rolling back to not reading JSON - $cache->setJsonSupport( false, false ); - - // make sure we don't crash and burn - $cachedOutput = $cache->get( $this->page, $options ); - $this->assertFalse( $cachedOutput ); - - // now test that the cache works without JSON - $parserOutput2 = new ParserOutput( 'dolor sit amet' ); - $cache->save( $parserOutput2, $this->page, $options, $this->cacheTime ); - - // make sure we can load non-json cache data - $cachedOutput = $cache->get( $this->page, $options ); - $this->assertEquals( $parserOutput2, $cachedOutput ); - } - - /** - * @covers ParserCache::encodeAsJson + * @covers ParserCache::convertForCache */ public function testNonSerializableJsonIsReported() { $testLogger = new TestLogger( true ); $cache = $this->createParserCache( null, null, $testLogger ); - $cache->setJsonSupport( true, true ); $parserOutput = $this->createDummyParserOutput(); $parserOutput->setExtensionData( 'test', new User() ); @@ -725,12 +708,11 @@ class ParserCacheTest extends MediaWikiIntegrationTestCase { } /** - * @covers ParserCache::encodeAsJson + * @covers ParserCache::convertForCache */ public function testCyclicStructuresDoNotBlowUpInJson() { $testLogger = new TestLogger( true ); $cache = $this->createParserCache( null, null, $testLogger ); - $cache->setJsonSupport( true, true ); $parserOutput = $this->createDummyParserOutput(); $cyclicArray = [ 'a' => 'b' ]; @@ -745,12 +727,12 @@ class ParserCacheTest extends MediaWikiIntegrationTestCase { /** * Tests that unicode characters are not \u escaped - * @covers ParserCache::encodeAsJson + * + * @covers ParserCache::convertForCache */ public function testJsonEncodeUnicode() { $unicodeCharacter = "Э"; $cache = $this->createParserCache( null, new HashBagOStuff() ); - $cache->setJsonSupport( true, true ); $parserOutput = $this->createDummyParserOutput(); $parserOutput->setText( $unicodeCharacter ); diff --git a/tests/phpunit/integration/includes/Rest/Handler/PageHTMLHandlerTest.php b/tests/phpunit/integration/includes/Rest/Handler/PageHTMLHandlerTest.php index 56a9ef10414..763d082212a 100644 --- a/tests/phpunit/integration/includes/Rest/Handler/PageHTMLHandlerTest.php +++ b/tests/phpunit/integration/includes/Rest/Handler/PageHTMLHandlerTest.php @@ -74,7 +74,6 @@ class PageHTMLHandlerTest extends MediaWikiIntegrationTestCase { */ private function newHandler( Parsoid $parsoid = null ): PageHTMLHandler { $parserCacheFactoryOptions = new ServiceOptions( ParserCacheFactory::CONSTRUCTOR_OPTIONS, [ - 'ParserCacheUseJson' => true, 'CacheEpoch' => '20200202112233', 'OldRevisionParserCacheExpireTime' => 60, ] ); diff --git a/tests/phpunit/integration/includes/Rest/Handler/RevisionHTMLHandlerTest.php b/tests/phpunit/integration/includes/Rest/Handler/RevisionHTMLHandlerTest.php index c81f2e642bc..279f135609a 100644 --- a/tests/phpunit/integration/includes/Rest/Handler/RevisionHTMLHandlerTest.php +++ b/tests/phpunit/integration/includes/Rest/Handler/RevisionHTMLHandlerTest.php @@ -77,7 +77,6 @@ class RevisionHTMLHandlerTest extends MediaWikiIntegrationTestCase { */ private function newHandler( Parsoid $parsoid = null ): RevisionHTMLHandler { $parserCacheFactoryOptions = new ServiceOptions( ParserCacheFactory::CONSTRUCTOR_OPTIONS, [ - 'ParserCacheUseJson' => true, 'CacheEpoch' => '20200202112233', 'OldRevisionParserCacheExpireTime' => 60 * 60, ] ); diff --git a/tests/phpunit/unit/includes/parser/ParserCacheFactoryTest.php b/tests/phpunit/unit/includes/parser/ParserCacheFactoryTest.php index f012fcd6cef..b1e10c0e396 100644 --- a/tests/phpunit/unit/includes/parser/ParserCacheFactoryTest.php +++ b/tests/phpunit/unit/includes/parser/ParserCacheFactoryTest.php @@ -17,7 +17,6 @@ class ParserCacheFactoryTest extends MediaWikiUnitTestCase { */ private function newParserCacheFactory() { $options = new ServiceOptions( ParserCacheFactory::CONSTRUCTOR_OPTIONS, [ - 'ParserCacheUseJson' => true, 'CacheEpoch' => '20200202112233', 'OldRevisionParserCacheExpireTime' => 60, ] );