mediawiki-extensions-Templa.../includes/TemplateDataNormalizer.php
Thiemo Kreuz 7a32cba3ef Split validation and normalization into separate services
This makes it possible to use these steps independent from each
other. For example, a future patch can get rid of the re-validation
that's done over and over again when the API is called.

A significant change is that this gets rid of an expensive deep
clone. It was necessary before exactly because validation and
normalization was intertwined. Normalized properties would mess with
the later inheritance.

Strictly splitting validation and normalization (and executing them
in this order) solved this. The only downside of this is that
inherited properties are validated twice. But this is much less of a
problem, compared to the deep clone, I would like to argue.

This was always covered by tests. You can still see the tests fail
when you flip the execution order of inheritance and parameter
validation.

Bug: T301337
Change-Id: Ie5728094f9ed813f53b709d8b5283c4b99dc7d63
2023-03-06 13:05:51 +00:00

101 lines
2.6 KiB
PHP

<?php
namespace MediaWiki\Extension\TemplateData;
use stdClass;
class TemplateDataNormalizer {
public const DEPRECATED_PARAMETER_TYPES = [
'string/line' => 'line',
'string/wiki-page-name' => 'wiki-page-name',
'string/wiki-user-name' => 'wiki-user-name',
'string/wiki-file-name' => 'wiki-file-name',
];
/** @var string */
private string $contentLanguageCode;
/**
* @param string $contentLanguageCode
*/
public function __construct( string $contentLanguageCode ) {
$this->contentLanguageCode = $contentLanguageCode;
}
/**
* @param stdClass $data Expected to be valid according to the {@see TemplateDataValidator}
*/
public function normalize( stdClass $data ) {
$data->description ??= null;
$data->sets ??= [];
$data->maps ??= (object)[];
$data->format ??= null;
$this->normaliseInterfaceText( $data->description );
foreach ( $data->sets as $setObj ) {
$this->normaliseInterfaceText( $setObj->label );
}
if ( isset( $data->params ) ) {
foreach ( $data->params as $param ) {
if ( isset( $param->inherits ) && isset( $data->params->{ $param->inherits } ) ) {
$parent = $data->params->{ $param->inherits };
foreach ( $parent as $key => $value ) {
if ( !isset( $param->$key ) ) {
$param->$key = is_object( $parent->$key ) ?
clone $parent->$key :
$parent->$key;
}
}
unset( $param->inherits );
}
$this->normalizeParameter( $param );
}
}
}
/**
* @param stdClass $paramObj
*/
private function normalizeParameter( stdClass $paramObj ) {
$paramObj->label ??= null;
$paramObj->description ??= null;
$paramObj->required ??= false;
$paramObj->suggested ??= false;
$paramObj->deprecated ??= false;
$paramObj->aliases ??= [];
$paramObj->type ??= 'unknown';
$paramObj->autovalue ??= null;
$paramObj->default ??= null;
$paramObj->suggestedvalues ??= [];
$paramObj->example ??= null;
$this->normaliseInterfaceText( $paramObj->label );
$this->normaliseInterfaceText( $paramObj->description );
$this->normaliseInterfaceText( $paramObj->default );
$this->normaliseInterfaceText( $paramObj->example );
foreach ( $paramObj->aliases as &$alias ) {
if ( is_int( $alias ) ) {
$alias = (string)$alias;
}
}
// Map deprecated types to newer versions
if ( isset( self::DEPRECATED_PARAMETER_TYPES[$paramObj->type] ) ) {
$paramObj->type = self::DEPRECATED_PARAMETER_TYPES[$paramObj->type];
}
}
/**
* @param string|stdClass &$text
*/
private function normaliseInterfaceText( &$text ) {
if ( is_string( $text ) ) {
$text = (object)[ $this->contentLanguageCode => $text ];
}
}
}