aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAmir Sarabadani <ladsgroup@gmail.com>2022-04-01 17:16:26 +0200
committerKrinkle <krinkle@fastmail.com>2022-04-07 01:11:46 +0000
commit5da58d60289089651240a16baf90cde2235579e3 (patch)
treeae6108ccd3049ac0b177384a61f108791e8934fa
parente4bbad4ffeff8e39c45cd13d835621670d3d339d (diff)
downloadmediawikicore-5da58d60289089651240a16baf90cde2235579e3.tar.gz
mediawikicore-5da58d60289089651240a16baf90cde2235579e3.zip
file: Move json metadata storage out of LocalFile
So ArchivedFile could use it as well Bug: T298417 Bug: T298398 Change-Id: Idd258437d9f91824195ed0c12b5694841cb365b0
-rw-r--r--autoload.php1
-rw-r--r--includes/filerepo/file/LocalFile.php108
-rw-r--r--includes/filerepo/file/MetadataStorageHelper.php154
-rw-r--r--includes/filerepo/file/OldLocalFile.php8
4 files changed, 173 insertions, 98 deletions
diff --git a/autoload.php b/autoload.php
index 43549820c972..f6566a043e23 100644
--- a/autoload.php
+++ b/autoload.php
@@ -1131,6 +1131,7 @@ $wgAutoloadLocalClasses = [
'MessageContent' => __DIR__ . '/includes/content/MessageContent.php',
'MessageLocalizer' => __DIR__ . '/includes/language/MessageLocalizer.php',
'MessageSpecifier' => __DIR__ . '/includes/libs/MessageSpecifier.php',
+ 'MetadataStorageHelper' => __DIR__ . '/includes/filerepo/file/MetadataStorageHelper.php',
'MigrateActors' => __DIR__ . '/maintenance/includes/MigrateActors.php',
'MigrateArchiveText' => __DIR__ . '/maintenance/migrateArchiveText.php',
'MigrateComments' => __DIR__ . '/maintenance/migrateComments.php',
diff --git a/includes/filerepo/file/LocalFile.php b/includes/filerepo/file/LocalFile.php
index 6ba0ea93e7ff..9ca0a6a5b95e 100644
--- a/includes/filerepo/file/LocalFile.php
+++ b/includes/filerepo/file/LocalFile.php
@@ -26,7 +26,6 @@ use MediaWiki\Logger\LoggerFactory;
use MediaWiki\MediaWikiServices;
use MediaWiki\Permissions\Authority;
use MediaWiki\Revision\RevisionRecord;
-use MediaWiki\Storage\BlobStore;
use MediaWiki\User\UserIdentity;
use MediaWiki\User\UserIdentityValue;
use Wikimedia\Rdbms\Blob;
@@ -174,6 +173,9 @@ class LocalFile extends File {
/** @var bool True if file is not present in file system. Not to be cached in memcached */
private $missing;
+ /** @var MetadataStorageHelper */
+ private $metadataStorageHelper;
+
// @note: higher than IDBAccessObject constants
private const LOAD_ALL = 16; // integer; load all the lazy fields too (like metadata)
@@ -188,7 +190,7 @@ class LocalFile extends File {
* @stable to override
*
* @param Title $title
- * @param FileRepo $repo
+ * @param LocalRepo $repo
* @param null $unused
*
* @return static
@@ -204,7 +206,7 @@ class LocalFile extends File {
* @stable to override
*
* @param stdClass $row
- * @param FileRepo $repo
+ * @param LocalRepo $repo
*
* @return static
*/
@@ -310,10 +312,11 @@ class LocalFile extends File {
* @stable to call
*
* @param Title $title
- * @param FileRepo $repo
+ * @param LocalRepo $repo
*/
public function __construct( $title, $repo ) {
parent::__construct( $title, $repo );
+ $this->metadataStorageHelper = new MetadataStorageHelper( $repo );
$this->assertRepoDefined();
$this->assertTitleDefined();
@@ -1090,25 +1093,13 @@ class LocalFile extends File {
$addresses[$itemName] = $this->unloadedMetadataBlobs[$itemName];
}
}
+
if ( $addresses ) {
- $blobStore = $this->repo->getBlobStore();
- if ( !$blobStore ) {
- LoggerFactory::getInstance( 'LocalFile' )->warning(
- "Unable to load metadata: repo has no blob store" );
- return $result;
- }
- $status = $blobStore->getBlobBatch( $addresses );
- if ( !$status->isGood() ) {
- $msg = Status::wrap( $status )->getWikiText(
- false, false, 'en' );
- LoggerFactory::getInstance( 'LocalFile' )->warning(
- "Error loading metadata from BlobStore: $msg" );
- }
+ $resultFromBlob = $this->metadataStorageHelper->getMetadataFromBlobStore( $addresses );
foreach ( $addresses as $itemName => $address ) {
unset( $this->unloadedMetadataBlobs[$itemName] );
- $json = $status->getValue()[$address] ?? null;
- if ( $json !== null ) {
- $value = $this->jsonDecode( $json );
+ $value = $resultFromBlob[$itemName] ?? null;
+ if ( $value !== null ) {
$result[$itemName] = $value;
$this->metadataArray[$itemName] = $value;
}
@@ -1118,42 +1109,6 @@ class LocalFile extends File {
}
/**
- * Do JSON encoding with local flags. Throw an exception if the data cannot be
- * serialized.
- *
- * @throws MWException
- * @param mixed $data
- * @return string
- */
- private function jsonEncode( $data ): string {
- $s = json_encode( $data,
- JSON_INVALID_UTF8_IGNORE |
- JSON_UNESCAPED_SLASHES |
- JSON_UNESCAPED_UNICODE );
- if ( $s === false ) {
- throw new MWException( __METHOD__ . ': metadata is not JSON-serializable ' .
- '(type = ' . $this->getMimeType() . ')' );
- }
- return $s;
- }
-
- /**
- * Do JSON decoding with local flags.
- *
- * This doesn't use JsonCodec because JsonCodec can construct objects,
- * which we don't want.
- *
- * Does not throw. Returns false on failure.
- *
- * @param string $s
- * @return mixed The decoded value, or false on failure
- */
- private function jsonDecode( string $s ) {
- // phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged
- return @json_decode( $s, true, 512, JSON_INVALID_UTF8_IGNORE );
- }
-
- /**
* Serialize the metadata array for insertion into img_metadata, oi_metadata
* or fa_metadata.
*
@@ -1196,42 +1151,7 @@ class LocalFile extends File {
$envelope['blobs'] = $this->metadataBlobs;
}
- // Try encoding
- $s = $this->jsonEncode( $envelope );
-
- // Decide whether to try splitting the metadata.
- // Return early if it's not going to happen.
- if ( !$this->repo->isSplitMetadataEnabled()
- || !$this->getHandler()
- || !$this->getHandler()->useSplitMetadata()
- ) {
- return $s;
- }
- $threshold = $this->repo->getSplitMetadataThreshold();
- if ( !$threshold || strlen( $s ) <= $threshold ) {
- return $s;
- }
- $blobStore = $this->repo->getBlobStore();
- if ( !$blobStore ) {
- return $s;
- }
-
- // The data as a whole is above the item threshold. Look for
- // large items that can be split out.
- $blobAddresses = [];
- foreach ( $envelope['data'] as $name => $value ) {
- $encoded = $this->jsonEncode( $value );
- if ( strlen( $encoded ) > $threshold ) {
- $blobAddresses[$name] = $blobStore->storeBlob(
- $encoded,
- [ BlobStore::IMAGE_HINT => $this->getName() ]
- );
- }
- }
- // Remove any items that were split out
- $envelope['data'] = array_diff_key( $envelope['data'], $blobAddresses );
- $envelope['blobs'] = $blobAddresses;
- $s = $this->jsonEncode( $envelope );
+ list( $s, $blobAddresses ) = $this->metadataStorageHelper->getJsonMetadata( $this, $envelope );
// Repeated calls to this function should not keep inserting more blobs
$this->metadataBlobs += $blobAddresses;
@@ -1252,7 +1172,7 @@ class LocalFile extends File {
$threshold = $this->repo->getSplitMetadataThreshold();
$directItems = array_diff_key( $this->metadataArray, $this->metadataBlobs );
foreach ( $directItems as $value ) {
- if ( strlen( $this->jsonEncode( $value ) ) > $threshold ) {
+ if ( strlen( $this->metadataStorageHelper->jsonEncode( $value ) ) > $threshold ) {
return true;
}
}
@@ -1289,7 +1209,7 @@ class LocalFile extends File {
return;
}
if ( $metadataString[0] === '{' ) {
- $envelope = $this->jsonDecode( $metadataString );
+ $envelope = $this->metadataStorageHelper->jsonDecode( $metadataString );
if ( !$envelope ) {
// Legacy error encoding
$this->metadataArray = [ '_error' => $metadataString ];
diff --git a/includes/filerepo/file/MetadataStorageHelper.php b/includes/filerepo/file/MetadataStorageHelper.php
new file mode 100644
index 000000000000..1c3282ae6bca
--- /dev/null
+++ b/includes/filerepo/file/MetadataStorageHelper.php
@@ -0,0 +1,154 @@
+<?php
+/**
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ * @ingroup FileAbstraction
+ */
+
+use MediaWiki\Logger\LoggerFactory;
+use MediaWiki\Storage\BlobStore;
+
+/**
+ * Helper for storage of metadata. Sharing the code between LocalFile and ArchivedFile
+ *
+ * @internal
+ * @ingroup FileAbstraction
+ */
+class MetadataStorageHelper {
+ /** @var LocalRepo */
+ private $repo;
+
+ public function __construct( LocalRepo $repo ) {
+ $this->repo = $repo;
+ }
+
+ /**
+ * Get metadata in JSON format ready for DB insertion, optionally splitting
+ * items out to BlobStore.
+ *
+ * @param LocalFile|ArchivedFile $file
+ * @param array $envelope
+ * @return array
+ */
+ public function getJsonMetadata( $file, $envelope ) {
+ // Try encoding
+ $s = $this->jsonEncode( $envelope );
+
+ // Decide whether to try splitting the metadata.
+ // Return early if it's not going to happen.
+ if ( !$this->repo->isSplitMetadataEnabled()
+ || !$file->getHandler()
+ || !$file->getHandler()->useSplitMetadata()
+ ) {
+ return [ $s, [] ];
+ }
+ $threshold = $this->repo->getSplitMetadataThreshold();
+ if ( !$threshold || strlen( $s ) <= $threshold ) {
+ return [ $s, [] ];
+ }
+ $blobStore = $this->repo->getBlobStore();
+ if ( !$blobStore ) {
+ return [ $s, [] ];
+ }
+
+ // The data as a whole is above the item threshold. Look for
+ // large items that can be split out.
+ $blobAddresses = [];
+ foreach ( $envelope['data'] as $name => $value ) {
+ $encoded = $this->jsonEncode( $value );
+ if ( strlen( $encoded ) > $threshold ) {
+ $blobAddresses[$name] = $blobStore->storeBlob(
+ $encoded,
+ [ BlobStore::IMAGE_HINT => $file->getName() ]
+ );
+ }
+ }
+ // Remove any items that were split out
+ $envelope['data'] = array_diff_key( $envelope['data'], $blobAddresses );
+ $envelope['blobs'] = $blobAddresses;
+ $s = $this->jsonEncode( $envelope );
+
+ return [ $s, $blobAddresses ];
+ }
+
+ /**
+ * Do JSON encoding with local flags. Throw an exception if the data cannot be
+ * serialized.
+ *
+ * @throws MWException
+ * @param mixed $data
+ * @return string
+ */
+ public function jsonEncode( $data ): string {
+ $s = json_encode( $data,
+ JSON_INVALID_UTF8_IGNORE |
+ JSON_UNESCAPED_SLASHES |
+ JSON_UNESCAPED_UNICODE );
+ if ( $s === false ) {
+ throw new MWException( __METHOD__ . ': metadata is not JSON-serializable ' );
+ }
+ return $s;
+ }
+
+ /**
+ * @param array $addresses
+ * @return array
+ */
+ public function getMetadataFromBlobStore( array $addresses ): array {
+ $result = [];
+ if ( $addresses ) {
+ $blobStore = $this->repo->getBlobStore();
+ if ( !$blobStore ) {
+ LoggerFactory::getInstance( 'LocalFile' )->warning(
+ "Unable to load metadata: repo has no blob store" );
+ return $result;
+ }
+ $status = $blobStore->getBlobBatch( $addresses );
+ if ( !$status->isGood() ) {
+ $msg = Status::wrap( $status )->getWikiText(
+ false, false, 'en' );
+ LoggerFactory::getInstance( 'LocalFile' )->warning(
+ "Error loading metadata from BlobStore: $msg" );
+ }
+ foreach ( $addresses as $itemName => $address ) {
+ $json = $status->getValue()[$address] ?? null;
+ if ( $json !== null ) {
+ $value = $this->jsonDecode( $json );
+ $result[$itemName] = $value;
+ }
+ }
+ }
+ return $result;
+ }
+
+ /**
+ * Do JSON decoding with local flags.
+ *
+ * This doesn't use JsonCodec because JsonCodec can construct objects,
+ * which we don't want.
+ *
+ * Does not throw. Returns false on failure.
+ *
+ * @param string $s
+ * @return mixed The decoded value, or false on failure
+ */
+ public function jsonDecode( string $s ) {
+ // phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged
+ return @json_decode( $s, true, 512, JSON_INVALID_UTF8_IGNORE );
+ }
+
+}
diff --git a/includes/filerepo/file/OldLocalFile.php b/includes/filerepo/file/OldLocalFile.php
index 7e6123faa46b..031fe4bd9a2c 100644
--- a/includes/filerepo/file/OldLocalFile.php
+++ b/includes/filerepo/file/OldLocalFile.php
@@ -44,7 +44,7 @@ class OldLocalFile extends LocalFile {
/**
* @stable to override
* @param Title $title
- * @param FileRepo $repo
+ * @param LocalRepo $repo
* @param string|int|null $time
* @return static
* @throws MWException
@@ -62,7 +62,7 @@ class OldLocalFile extends LocalFile {
* @stable to override
*
* @param Title $title
- * @param FileRepo $repo
+ * @param LocalRepo $repo
* @param string $archiveName
* @return static
*/
@@ -74,7 +74,7 @@ class OldLocalFile extends LocalFile {
* @stable to override
*
* @param stdClass $row
- * @param FileRepo $repo
+ * @param LocalRepo $repo
* @return static
*/
public static function newFromRow( $row, $repo ) {
@@ -179,7 +179,7 @@ class OldLocalFile extends LocalFile {
* @stable to call
*
* @param Title $title
- * @param FileRepo $repo
+ * @param LocalRepo $repo
* @param string|int|null $time Timestamp or null to load by archive name
* @param string|null $archiveName Archive name or null to load by timestamp
* @throws MWException