wiki.techinc.nl/includes/filerepo/file/MetadataStorageHelper.php
Daimona Eaytoy 175c0c4abf Replace more instances of deprecated MWException
Bug: T328220
Change-Id: Iba90f7f9b5766bccc05380d040138d74d5e9558a
2024-01-19 23:11:59 +00:00

152 lines
4.3 KiB
PHP

<?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
*/
use MediaWiki\Logger\LoggerFactory;
use MediaWiki\Status\Status;
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. Callers must make sure that the data can be serialized.
*
* @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 InvalidArgumentException( __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 );
}
}