Viewing File: /home/omtekel/www/wp-content/upgrade/backup/ParamValidator.tar
TypeDef/UserDef.php 0000666 00000025704 15133473745 0010202 0 ustar 00 <?php
namespace MediaWiki\ParamValidator\TypeDef;
use MediaWiki\Title\MalformedTitleException;
use MediaWiki\Title\TitleParser;
use MediaWiki\User\ExternalUserNames;
use MediaWiki\User\UserIdentity;
use MediaWiki\User\UserIdentityLookup;
use MediaWiki\User\UserIdentityValue;
use MediaWiki\User\UserNameUtils;
use MediaWiki\User\UserRigorOptions;
use Wikimedia\IPUtils;
use Wikimedia\Message\MessageValue;
use Wikimedia\ParamValidator\Callbacks;
use Wikimedia\ParamValidator\ParamValidator;
use Wikimedia\ParamValidator\TypeDef;
/**
* Type definition for user types
*
* Failure codes:
* - 'baduser': The value was not a valid MediaWiki user. No data.
*
* @since 1.35
*/
class UserDef extends TypeDef {
/**
* (string[]) Allowed types of user.
*
* One or more of the following values:
* - 'name': User names are allowed.
* - 'ip': IP ("anon") usernames are allowed.
* - 'cidr': IP ranges are allowed.
* - 'interwiki': Interwiki usernames are allowed.
* - 'id': Allow specifying user IDs, formatted like "#123".
*
* Default is `[ 'name', 'ip', 'cidr', 'interwiki' ]`.
*
* Avoid combining 'id' with PARAM_ISMULTI, as it may result in excessive
* DB lookups. If you do combine them, consider setting low values for
* PARAM_ISMULTI_LIMIT1 and PARAM_ISMULTI_LIMIT2 to mitigate it.
*/
public const PARAM_ALLOWED_USER_TYPES = 'param-allowed-user-types';
/**
* (bool) Whether to return a UserIdentity object.
*
* If false, the validated user name is returned as a string. Default is false.
*
* Avoid setting true with PARAM_ISMULTI, as it may result in excessive DB
* lookups. If you do combine them, consider setting low values for
* PARAM_ISMULTI_LIMIT1 and PARAM_ISMULTI_LIMIT2 to mitigate it.
*/
public const PARAM_RETURN_OBJECT = 'param-return-object';
/** @var UserIdentityLookup */
private $userIdentityLookup;
/** @var TitleParser */
private $titleParser;
/** @var UserNameUtils */
private $userNameUtils;
/**
* @param Callbacks $callbacks
* @param UserIdentityLookup $userIdentityLookup
* @param TitleParser $titleParser
* @param UserNameUtils $userNameUtils
*/
public function __construct(
Callbacks $callbacks,
UserIdentityLookup $userIdentityLookup,
TitleParser $titleParser,
UserNameUtils $userNameUtils
) {
parent::__construct( $callbacks );
$this->userIdentityLookup = $userIdentityLookup;
$this->titleParser = $titleParser;
$this->userNameUtils = $userNameUtils;
}
public function validate( $name, $value, array $settings, array $options ) {
[ $type, $user ] = $this->processUser( $value );
if ( !$user || !in_array( $type, $settings[self::PARAM_ALLOWED_USER_TYPES], true ) ) {
// Message used: paramvalidator-baduser
$this->failure( 'baduser', $name, $value, $settings, $options );
}
return empty( $settings[self::PARAM_RETURN_OBJECT] ) ? $user->getName() : $user;
}
public function normalizeSettings( array $settings ) {
if ( isset( $settings[self::PARAM_ALLOWED_USER_TYPES] ) ) {
$settings[self::PARAM_ALLOWED_USER_TYPES] = array_values( array_intersect(
[ 'name', 'ip', 'cidr', 'interwiki', 'id' ],
$settings[self::PARAM_ALLOWED_USER_TYPES]
) );
}
if ( empty( $settings[self::PARAM_ALLOWED_USER_TYPES] ) ) {
$settings[self::PARAM_ALLOWED_USER_TYPES] = [ 'name', 'ip', 'cidr', 'interwiki' ];
}
return parent::normalizeSettings( $settings );
}
public function checkSettings( string $name, $settings, array $options, array $ret ): array {
$ret = parent::checkSettings( $name, $settings, $options, $ret );
$ret['allowedKeys'] = array_merge( $ret['allowedKeys'], [
self::PARAM_ALLOWED_USER_TYPES, self::PARAM_RETURN_OBJECT,
] );
if ( !is_bool( $settings[self::PARAM_RETURN_OBJECT] ?? false ) ) {
$ret['issues'][self::PARAM_RETURN_OBJECT] = 'PARAM_RETURN_OBJECT must be boolean, got '
. gettype( $settings[self::PARAM_RETURN_OBJECT] );
}
$hasId = false;
if ( isset( $settings[self::PARAM_ALLOWED_USER_TYPES] ) ) {
if ( !is_array( $settings[self::PARAM_ALLOWED_USER_TYPES] ) ) {
$ret['issues'][self::PARAM_ALLOWED_USER_TYPES] = 'PARAM_ALLOWED_USER_TYPES must be an array, '
. 'got ' . gettype( $settings[self::PARAM_ALLOWED_USER_TYPES] );
} elseif ( $settings[self::PARAM_ALLOWED_USER_TYPES] === [] ) {
$ret['issues'][self::PARAM_ALLOWED_USER_TYPES] = 'PARAM_ALLOWED_USER_TYPES cannot be empty';
} else {
$bad = array_diff(
$settings[self::PARAM_ALLOWED_USER_TYPES],
[ 'name', 'ip', 'cidr', 'interwiki', 'id' ]
);
if ( $bad ) {
$ret['issues'][self::PARAM_ALLOWED_USER_TYPES] =
'PARAM_ALLOWED_USER_TYPES contains invalid values: ' . implode( ', ', $bad );
}
$hasId = in_array( 'id', $settings[self::PARAM_ALLOWED_USER_TYPES], true );
}
}
if ( !empty( $settings[ParamValidator::PARAM_ISMULTI] ) &&
( $hasId || !empty( $settings[self::PARAM_RETURN_OBJECT] ) ) &&
(
( $settings[ParamValidator::PARAM_ISMULTI_LIMIT1] ?? 100 ) > 10 ||
( $settings[ParamValidator::PARAM_ISMULTI_LIMIT2] ?? 100 ) > 10
)
) {
$ret['issues'][] = 'Multi-valued user-type parameters with PARAM_RETURN_OBJECT or allowing IDs '
. 'should set low values (<= 10) for PARAM_ISMULTI_LIMIT1 and PARAM_ISMULTI_LIMIT2.'
. ' (Note that "<= 10" is arbitrary. If something hits this, we can investigate a real limit '
. 'once we have a real use case to look at.)';
}
return $ret;
}
/**
* Process $value to a UserIdentity, if possible
* @param string $value
* @return array [ string $type, UserIdentity|null $user ]
* @phan-return array{0:string,1:UserIdentity|null}
*/
private function processUser( string $value ): array {
// A user ID?
if ( preg_match( '/^#(\d+)$/D', $value, $m ) ) {
// This used to use the IP address of the current request if the
// id was 0, to match the behavior of User objects, but was switched
// to "Unknown user" because the former behavior is likely unexpected.
// If the id corresponds to a user in the database, use that user, otherwise
// return a UserIdentityValue with id 0 (regardless of the input id) and
// the name "Unknown user"
$userId = (int)$m[1];
if ( $userId !== 0 ) {
// Check the database.
$userIdentity = $this->userIdentityLookup->getUserIdentityByUserId( $userId );
if ( $userIdentity ) {
return [ 'id', $userIdentity ];
}
}
// Fall back to "Unknown user"
return [
'id',
new UserIdentityValue( 0, "Unknown user" )
];
}
// An interwiki username?
if ( ExternalUserNames::isExternal( $value ) ) {
$name = $this->userNameUtils->getCanonical( $value, UserRigorOptions::RIGOR_NONE );
// UserIdentityValue has the username which includes the > separating the external
// wiki database and the actual name, but is created for the *local* wiki, like
// for User objects (local is the default, but we specify it anyway to show
// that its intentional even though the username is for a different wiki)
// NOTE: We deliberately use the raw $value instead of the canonical $name
// to avoid converting the first character of the interwiki prefix to uppercase
$user = $name !== false ? new UserIdentityValue( 0, $value, UserIdentityValue::LOCAL ) : null;
return [ 'interwiki', $user ];
}
// A valid user name?
// Match behavior of UserFactory::newFromName with RIGOR_VALID and User::getId()
// we know that if there is a canonical form from UserNameUtils then this can't
// look like an IP, and since we checked for external user names above it isn't
// that either, so if this is a valid user name then we check the database for
// the id, and if there is no user with this name the id is 0
$canonicalName = $this->userNameUtils->getCanonical( $value, UserRigorOptions::RIGOR_VALID );
if ( $canonicalName !== false ) {
$userIdentity = $this->userIdentityLookup->getUserIdentityByName( $canonicalName );
if ( $userIdentity ) {
return [ 'name', $userIdentity ];
}
// Fall back to id 0
return [
'name',
new UserIdentityValue( 0, $canonicalName )
];
}
// (T232672) Reproduce the normalization applied in UserNameUtils::getCanonical() when
// performing the checks below.
if ( strpos( $value, '#' ) !== false ) {
return [ '', null ];
}
try {
$t = $this->titleParser->parseTitle( $value );
} catch ( MalformedTitleException $_ ) {
$t = null;
}
if ( !$t || $t->getNamespace() !== NS_USER || $t->isExternal() ) { // likely
try {
$t = $this->titleParser->parseTitle( "User:$value" );
} catch ( MalformedTitleException $_ ) {
$t = null;
}
}
if ( !$t || $t->getNamespace() !== NS_USER || $t->isExternal() ) {
// If it wasn't a valid User-namespace title, fail.
return [ '', null ];
}
$value = $t->getText();
// An IP?
$b = IPUtils::RE_IP_BYTE;
if ( IPUtils::isValid( $value ) ||
// See comment for UserNameUtils::isIP. We don't just call that function
// here because it also returns true for things like
// 300.300.300.300 that are neither valid usernames nor valid IP
// addresses.
preg_match( "/^$b\.$b\.$b\.xxx$/D", $value )
) {
$name = IPUtils::sanitizeIP( $value );
// We don't really need to use UserNameUtils::getCanonical() because for anonymous
// users the only validation is that there is no `#` (which is already the case if its
// a valid IP or matches the regex) and the only normalization is making the first
// character uppercase (doesn't matter for numbers) and replacing underscores with
// spaces (doesn't apply to IPs). But, better safe than sorry?
$name = $this->userNameUtils->getCanonical( $name, UserRigorOptions::RIGOR_NONE );
return [ 'ip', UserIdentityValue::newAnonymous( $name ) ];
}
// A range?
if ( IPUtils::isValidRange( $value ) ) {
$name = IPUtils::sanitizeIP( $value );
// Per above, the UserNameUtils call isn't strictly needed, but doesn't hurt
$name = $this->userNameUtils->getCanonical( $name, UserRigorOptions::RIGOR_NONE );
return [ 'cidr', UserIdentityValue::newAnonymous( $name ) ];
}
// Fail.
return [ '', null ];
}
public function getParamInfo( $name, array $settings, array $options ) {
$info = parent::getParamInfo( $name, $settings, $options );
$info['subtypes'] = $settings[self::PARAM_ALLOWED_USER_TYPES];
return $info;
}
public function getHelpInfo( $name, array $settings, array $options ) {
$info = parent::getParamInfo( $name, $settings, $options );
$isMulti = !empty( $settings[ParamValidator::PARAM_ISMULTI] );
$subtypes = [];
foreach ( $settings[self::PARAM_ALLOWED_USER_TYPES] as $st ) {
// Messages: paramvalidator-help-type-user-subtype-name,
// paramvalidator-help-type-user-subtype-ip, paramvalidator-help-type-user-subtype-cidr,
// paramvalidator-help-type-user-subtype-interwiki, paramvalidator-help-type-user-subtype-id
$subtypes[] = MessageValue::new( "paramvalidator-help-type-user-subtype-$st" );
}
$info[ParamValidator::PARAM_TYPE] = MessageValue::new( 'paramvalidator-help-type-user' )
->params( $isMulti ? 2 : 1 )
->textListParams( $subtypes )
->numParams( count( $subtypes ) );
return $info;
}
}
TypeDef/NamespaceDef.php 0000666 00000006346 15133473745 0011161 0 ustar 00 <?php
namespace MediaWiki\ParamValidator\TypeDef;
use ApiResult;
use MediaWiki\Title\NamespaceInfo;
use Wikimedia\ParamValidator\Callbacks;
use Wikimedia\ParamValidator\ParamValidator;
use Wikimedia\ParamValidator\TypeDef\EnumDef;
/**
* Type definition for namespace types
*
* A namespace type is an enum type that accepts MediaWiki namespace IDs.
*
* @since 1.35
*/
class NamespaceDef extends EnumDef {
/**
* (int[]) Additional namespace IDs to recognize.
*
* Generally this will be used to include NS_SPECIAL and/or NS_MEDIA.
*/
public const PARAM_EXTRA_NAMESPACES = 'param-extra-namespaces';
/** @var NamespaceInfo */
private $nsInfo;
public function __construct( Callbacks $callbacks, NamespaceInfo $nsInfo ) {
parent::__construct( $callbacks );
$this->nsInfo = $nsInfo;
}
public function validate( $name, $value, array $settings, array $options ) {
if ( !is_int( $value ) && preg_match( '/^[+-]?\d+$/D', $value ) ) {
// Convert to int since that's what getEnumValues() returns.
$value = (int)$value;
}
return parent::validate( $name, $value, $settings, $options );
}
public function getEnumValues( $name, array $settings, array $options ) {
$namespaces = $this->nsInfo->getValidNamespaces();
$extra = $settings[self::PARAM_EXTRA_NAMESPACES] ?? [];
if ( is_array( $extra ) && $extra !== [] ) {
$namespaces = array_merge( $namespaces, $extra );
}
sort( $namespaces );
return $namespaces;
}
public function normalizeSettings( array $settings ) {
// Force PARAM_ALL
if ( !empty( $settings[ParamValidator::PARAM_ISMULTI] ) ) {
$settings[ParamValidator::PARAM_ALL] = true;
}
return parent::normalizeSettings( $settings );
}
public function checkSettings( string $name, $settings, array $options, array $ret ): array {
$ret = parent::checkSettings( $name, $settings, $options, $ret );
$ret['allowedKeys'] = array_merge( $ret['allowedKeys'], [
self::PARAM_EXTRA_NAMESPACES,
] );
if ( !empty( $settings[ParamValidator::PARAM_ISMULTI] ) &&
( $settings[ParamValidator::PARAM_ALL] ?? true ) !== true &&
!isset( $ret['issues'][ParamValidator::PARAM_ALL] )
) {
$ret['issues'][ParamValidator::PARAM_ALL] =
'PARAM_ALL cannot be false or a string for namespace-type parameters';
}
$ns = $settings[self::PARAM_EXTRA_NAMESPACES] ?? [];
if ( !is_array( $ns ) ) {
$type = gettype( $ns );
} elseif ( $ns === [] ) {
$type = 'integer[]';
} else {
$types = array_unique( array_map( 'gettype', $ns ) );
$type = implode( '|', $types );
$type = count( $types ) > 1 ? "($type)[]" : "{$type}[]";
}
if ( $type !== 'integer[]' ) {
$ret['issues'][self::PARAM_EXTRA_NAMESPACES] =
"PARAM_EXTRA_NAMESPACES must be an integer[], got $type";
}
return $ret;
}
public function getParamInfo( $name, array $settings, array $options ) {
$info = parent::getParamInfo( $name, $settings, $options );
$info['type'] = 'namespace';
$extra = $settings[self::PARAM_EXTRA_NAMESPACES] ?? [];
if ( is_array( $extra ) && $extra !== [] ) {
$info['extranamespaces'] = array_values( $extra );
if ( isset( $options['module'] ) ) {
// ApiResult metadata when used with the Action API.
ApiResult::setIndexedTagName( $info['extranamespaces'], 'ns' );
}
}
return $info;
}
}
TypeDef/TagsDef.php 0000666 00000004446 15133473745 0010162 0 ustar 00 <?php
namespace MediaWiki\ParamValidator\TypeDef;
use ChangeTags;
use MediaWiki\ChangeTags\ChangeTagsStore;
use MediaWiki\Message\Converter as MessageConverter;
use Wikimedia\Message\DataMessageValue;
use Wikimedia\ParamValidator\Callbacks;
use Wikimedia\ParamValidator\TypeDef\EnumDef;
use Wikimedia\ParamValidator\ValidationException;
/**
* Type definition for tags type
*
* A tags type is an enum type for selecting MediaWiki change tags.
*
* Failure codes:
* - 'badtags': The value was not a valid set of tags. Data:
* - 'disallowedtags': The tags that were disallowed.
*
* @since 1.35
*/
class TagsDef extends EnumDef {
private ChangeTagsStore $changeTagsStore;
/** @var MessageConverter */
private $messageConverter;
public function __construct( Callbacks $callbacks, ChangeTagsStore $changeTagsStore ) {
parent::__construct( $callbacks );
$this->changeTagsStore = $changeTagsStore;
$this->messageConverter = new MessageConverter();
}
public function validate( $name, $value, array $settings, array $options ) {
// Validate the full list of tags at once, because the caller will
// *probably* stop at the first exception thrown.
if ( isset( $options['values-list'] ) ) {
$ret = $value;
$tagsStatus = ChangeTags::canAddTagsAccompanyingChange( $options['values-list'] );
} else {
// The 'tags' type always returns an array.
$ret = [ $value ];
$tagsStatus = ChangeTags::canAddTagsAccompanyingChange( $ret );
}
if ( !$tagsStatus->isGood() ) {
$msg = $this->messageConverter->convertMessage( $tagsStatus->getMessage() );
$data = [];
if ( $tagsStatus->value ) {
// Specific tags are not allowed.
$data['disallowedtags'] = $tagsStatus->value;
// @codeCoverageIgnoreStart
} else {
// All are disallowed, I guess
$data['disallowedtags'] = $settings['values-list'] ?? $ret;
}
// @codeCoverageIgnoreEnd
// Only throw if $value is among the disallowed tags
if ( in_array( $value, $data['disallowedtags'], true ) ) {
throw new ValidationException(
DataMessageValue::new( $msg->getKey(), $msg->getParams(), 'badtags', $data ),
$name, $value, $settings
);
}
}
return $ret;
}
public function getEnumValues( $name, array $settings, array $options ) {
return $this->changeTagsStore->listExplicitlyDefinedTags();
}
}
TypeDef/TitleDef.php 0000666 00000011123 15133473745 0010333 0 ustar 00 <?php
namespace MediaWiki\ParamValidator\TypeDef;
use MediaWiki\Linker\LinkTarget;
use MediaWiki\Title\TitleFactory;
use Wikimedia\Message\MessageValue;
use Wikimedia\ParamValidator\Callbacks;
use Wikimedia\ParamValidator\ParamValidator;
use Wikimedia\ParamValidator\TypeDef;
/**
* Type definition for page titles.
*
* Failure codes:
* - 'badtitle': invalid title (e.g. containing disallowed characters). No data.
* - 'missingtitle': the page with this title does not exist (when PARAM_MUST_EXIST
* was specified). No data.
*
* @since 1.36
*/
class TitleDef extends TypeDef {
/**
* (bool) Whether the page with the given title needs to exist.
*
* Defaults to false.
*/
public const PARAM_MUST_EXIST = 'param-must-exist';
/**
* (bool) Whether to return a LinkTarget.
*
* If false, the validated title is returned as a string (in getPrefixedText() format).
* Default is false.
*
* Avoid setting true with PARAM_ISMULTI, as it may result in excessive DB
* lookups. If you do combine them, consider setting low values for
* PARAM_ISMULTI_LIMIT1 and PARAM_ISMULTI_LIMIT2 to mitigate it.
*/
public const PARAM_RETURN_OBJECT = 'param-return-object';
/** @var TitleFactory */
private $titleFactory;
/**
* @param Callbacks $callbacks
* @param TitleFactory $titleFactory
*/
public function __construct( Callbacks $callbacks, TitleFactory $titleFactory ) {
parent::__construct( $callbacks );
$this->titleFactory = $titleFactory;
}
/**
* @inheritDoc
* @return string|LinkTarget Depending on the PARAM_RETURN_OBJECT setting.
*/
public function validate( $name, $value, array $settings, array $options ) {
$mustExist = !empty( $settings[self::PARAM_MUST_EXIST] );
$returnObject = !empty( $settings[self::PARAM_RETURN_OBJECT] );
$title = $this->titleFactory->newFromText( $value );
if ( !$title ) {
// Message used: paramvalidator-badtitle
$this->failure( 'badtitle', $name, $value, $settings, $options );
} elseif ( $mustExist && !$title->exists() ) {
// Message used: paramvalidator-missingtitle
$this->failure( 'missingtitle', $name, $value, $settings, $options );
}
if ( $returnObject ) {
return $title->getTitleValue();
} else {
return $title->getPrefixedText();
}
}
/** @inheritDoc */
public function stringifyValue( $name, $value, array $settings, array $options ) {
if ( $value instanceof LinkTarget ) {
return $this->titleFactory->newFromLinkTarget( $value )->getPrefixedText();
}
return parent::stringifyValue( $name, $value, $settings, $options );
}
/** @inheritDoc */
public function checkSettings( string $name, $settings, array $options, array $ret ): array {
$ret = parent::checkSettings( $name, $settings, $options, $ret );
$ret['allowedKeys'] = array_merge( $ret['allowedKeys'], [
self::PARAM_MUST_EXIST, self::PARAM_RETURN_OBJECT,
] );
if ( !is_bool( $settings[self::PARAM_MUST_EXIST] ?? false ) ) {
$ret['issues'][self::PARAM_MUST_EXIST] = 'PARAM_MUST_EXIST must be boolean, got '
. gettype( $settings[self::PARAM_MUST_EXIST] );
}
if ( !is_bool( $settings[self::PARAM_RETURN_OBJECT] ?? false ) ) {
$ret['issues'][self::PARAM_RETURN_OBJECT] = 'PARAM_RETURN_OBJECT must be boolean, got '
. gettype( $settings[self::PARAM_RETURN_OBJECT] );
}
if ( !empty( $settings[ParamValidator::PARAM_ISMULTI] ) &&
!empty( $settings[self::PARAM_RETURN_OBJECT] ) &&
(
( $settings[ParamValidator::PARAM_ISMULTI_LIMIT1] ?? 100 ) > 10 ||
( $settings[ParamValidator::PARAM_ISMULTI_LIMIT2] ?? 100 ) > 10
)
) {
$ret['issues'][] = 'Multi-valued title-type parameters with PARAM_RETURN_OBJECT '
. 'should set low values (<= 10) for PARAM_ISMULTI_LIMIT1 and PARAM_ISMULTI_LIMIT2.'
. ' (Note that "<= 10" is arbitrary. If something hits this, we can investigate a real limit '
. 'once we have a real use case to look at.)';
}
return $ret;
}
/** @inheritDoc */
public function getParamInfo( $name, array $settings, array $options ) {
$info = parent::getParamInfo( $name, $settings, $options );
$info['mustExist'] = !empty( $settings[self::PARAM_MUST_EXIST] );
return $info;
}
/** @inheritDoc */
public function getHelpInfo( $name, array $settings, array $options ) {
$info = parent::getParamInfo( $name, $settings, $options );
$info[ParamValidator::PARAM_TYPE] = MessageValue::new( 'paramvalidator-help-type-title' );
$mustExist = !empty( $settings[self::PARAM_MUST_EXIST] );
$info[self::PARAM_MUST_EXIST] = $mustExist
? MessageValue::new( 'paramvalidator-help-type-title-must-exist' )
: MessageValue::new( 'paramvalidator-help-type-title-no-must-exist' );
return $info;
}
}
TypeDef/EnumDef.php 0000666 00000016715 15133503751 0010161 0 ustar 00 <?php
namespace Wikimedia\ParamValidator\TypeDef;
use Wikimedia\Message\DataMessageValue;
use Wikimedia\Message\ListParam;
use Wikimedia\Message\ListType;
use Wikimedia\Message\MessageParam;
use Wikimedia\Message\MessageValue;
use Wikimedia\Message\ParamType;
use Wikimedia\Message\ScalarParam;
use Wikimedia\ParamValidator\ParamValidator;
use Wikimedia\ParamValidator\TypeDef;
/**
* Type definition for enumeration types.
*
* This class expects that PARAM_TYPE is an array of allowed values. Subclasses
* may override getEnumValues() to determine the allowed values differently.
*
* The result from validate() is one of the defined values.
*
* Failure codes:
* - 'badvalue': The value is not a recognized value. No data.
*
* Additional codes may be generated when using certain PARAM constants. See
* the constants' documentation for details.
*
* @since 1.34
* @unstable
*/
class EnumDef extends TypeDef {
/**
* (array) Associative array of deprecated values.
*
* Keys are the deprecated parameter values. Value is one of the following:
* - null: Parameter isn't actually deprecated.
* - true: Parameter is deprecated.
* - MessageValue: Parameter is deprecated, and this message (converted to a DataMessageValue)
* is used in place of the default for passing to $this->failure().
*
* Note that this does not add any values to the enumeration, it only
* documents existing values as being deprecated.
*
* Failure codes: (non-fatal)
* - 'deprecated-value': A deprecated value was encountered. No data.
*/
public const PARAM_DEPRECATED_VALUES = 'param-deprecated-values';
public function validate( $name, $value, array $settings, array $options ) {
$values = $this->getEnumValues( $name, $settings, $options );
if ( in_array( $value, $values, true ) ) {
// Set a warning if a deprecated parameter value has been passed
if ( empty( $options['is-default'] ) &&
isset( $settings[self::PARAM_DEPRECATED_VALUES][$value] )
) {
$msg = $settings[self::PARAM_DEPRECATED_VALUES][$value];
if ( $msg instanceof MessageValue ) {
$message = DataMessageValue::new(
$msg->getKey(),
$msg->getParams(),
'deprecated-value',
$msg instanceof DataMessageValue ? $msg->getData() : null
);
} else {
$message = $this->failureMessage( 'deprecated-value' );
}
$this->failure( $message, $name, $value, $settings, $options, false );
}
return $value;
}
$isMulti = isset( $options['values-list'] );
$this->failure(
$this->failureMessage( 'badvalue', [], $isMulti ? 'enummulti' : 'enumnotmulti' )
->textListParams( array_map( static function ( $v ) {
return new ScalarParam( ParamType::PLAINTEXT, $v );
}, $values ) )
->numParams( count( $values ) ),
$name, $value, $settings, $options
);
}
public function checkSettings( string $name, $settings, array $options, array $ret ): array {
$ret = parent::checkSettings( $name, $settings, $options, $ret );
$ret['allowedKeys'][] = self::PARAM_DEPRECATED_VALUES;
$dv = $settings[self::PARAM_DEPRECATED_VALUES] ?? [];
if ( !is_array( $dv ) ) {
$ret['issues'][self::PARAM_DEPRECATED_VALUES] = 'PARAM_DEPRECATED_VALUES must be an array, got '
. gettype( $dv );
} else {
$values = array_map( function ( $v ) use ( $name, $settings, $options ) {
return $this->stringifyValue( $name, $v, $settings, $options );
}, $this->getEnumValues( $name, $settings, $options ) );
foreach ( $dv as $k => $v ) {
$k = $this->stringifyValue( $name, $k, $settings, $options );
if ( !in_array( $k, $values, true ) ) {
$ret['issues'][] = "PARAM_DEPRECATED_VALUES contains \"$k\", which is not "
. 'one of the enumerated values';
} elseif ( $v instanceof MessageValue ) {
$ret['messages'][] = $v;
} elseif ( $v !== null && $v !== true ) {
$type = $v === false ? 'false' : ( is_object( $v ) ? get_class( $v ) : gettype( $v ) );
$ret['issues'][] = 'Values in PARAM_DEPRECATED_VALUES must be null, true, or MessageValue, '
. "but value for \"$k\" is $type";
}
}
}
return $ret;
}
public function getEnumValues( $name, array $settings, array $options ) {
return array_values( $settings[ParamValidator::PARAM_TYPE] );
}
public function stringifyValue( $name, $value, array $settings, array $options ) {
if ( !is_array( $value ) ) {
return parent::stringifyValue( $name, $value, $settings, $options );
}
return ParamValidator::implodeMultiValue( $value );
}
public function getParamInfo( $name, array $settings, array $options ) {
$info = parent::getParamInfo( $name, $settings, $options );
$info['type'] = $this->sortEnumValues(
$name,
$this->getEnumValues( $name, $settings, $options ),
$settings,
$options
);
if ( !empty( $settings[self::PARAM_DEPRECATED_VALUES] ) ) {
$deprecatedValues = array_intersect(
array_keys( $settings[self::PARAM_DEPRECATED_VALUES] ),
$this->getEnumValues( $name, $settings, $options )
);
if ( $deprecatedValues ) {
$deprecatedValues = $this->sortEnumValues( $name, $deprecatedValues, $settings, $options );
$info['deprecatedvalues'] = array_values( $deprecatedValues );
}
}
return $info;
}
public function getHelpInfo( $name, array $settings, array $options ) {
$info = parent::getHelpInfo( $name, $settings, $options );
$isMulti = !empty( $settings[ParamValidator::PARAM_ISMULTI] );
$values = $this->getEnumValuesForHelp( $name, $settings, $options );
$count = count( $values );
$i = array_search( '', $values, true );
if ( $i === false ) {
$valuesParam = new ListParam( ListType::COMMA, $values );
} else {
unset( $values[$i] );
$valuesParam = MessageValue::new( 'paramvalidator-help-type-enum-can-be-empty' )
->commaListParams( $values )
->numParams( count( $values ) );
}
$info[ParamValidator::PARAM_TYPE] = MessageValue::new( 'paramvalidator-help-type-enum' )
->params( $isMulti ? 2 : 1 )
->params( $valuesParam )
->numParams( $count );
// Suppress standard ISMULTI message, it should be incorporated into our type message.
$info[ParamValidator::PARAM_ISMULTI] = null;
return $info;
}
/**
* Sort enum values for help/param info output
*
* @param string $name Parameter name being described.
* @param string[] $values Values being sorted
* @param array $settings Parameter settings array.
* @param array $options Options array.
* @return string[]
*/
protected function sortEnumValues(
string $name, array $values, array $settings, array $options
): array {
// sort values by deprecation status and name
$flags = [];
foreach ( $values as $k => $value ) {
$flag = 0;
if ( isset( $settings[self::PARAM_DEPRECATED_VALUES][$value] ) ) {
$flag |= 1;
}
$flags[$k] = $flag;
}
array_multisort( $flags, $values, SORT_NATURAL );
return $values;
}
/**
* Return enum values formatted for the help message
*
* @param string $name Parameter name being described.
* @param array $settings Parameter settings array.
* @param array $options Options array.
* @return (MessageParam|string)[]
*/
protected function getEnumValuesForHelp( $name, array $settings, array $options ) {
$values = $this->getEnumValues( $name, $settings, $options );
$values = $this->sortEnumValues( $name, $values, $settings, $options );
// @todo Indicate deprecated values in some manner. Probably that needs
// MessageValue and/or MessageParam to have a generic ability to wrap
// values in HTML without that HTML coming out in the text format too.
return $values;
}
}
TypeDef/PresenceBooleanDef.php 0000666 00000004611 15133503751 0012311 0 ustar 00 <?php
namespace Wikimedia\ParamValidator\TypeDef;
use Wikimedia\Message\MessageValue;
use Wikimedia\ParamValidator\ParamValidator;
use Wikimedia\ParamValidator\TypeDef;
/**
* Type definition for checkbox-like boolean types
*
* This boolean is considered true if the parameter is present in the request,
* regardless of value. The only way for it to be false is for the parameter to
* be omitted entirely.
*
* The result from validate() is a PHP boolean.
*
* @since 1.34
* @unstable
*/
class PresenceBooleanDef extends TypeDef {
public function getValue( $name, array $settings, array $options ) {
return $this->callbacks->hasParam( $name, $options ) ? true : null;
}
public function validate( $name, $value, array $settings, array $options ) {
return (bool)$value;
}
public function normalizeSettings( array $settings ) {
// Cannot be multi-valued
$settings[ParamValidator::PARAM_ISMULTI] = false;
// Default the default to false so ParamValidator::getValue() returns false (T244440)
$settings += [ ParamValidator::PARAM_DEFAULT => false ];
return parent::normalizeSettings( $settings );
}
public function checkSettings( string $name, $settings, array $options, array $ret ): array {
$ret = parent::checkSettings( $name, $settings, $options, $ret );
if ( !empty( $settings[ParamValidator::PARAM_ISMULTI] ) &&
!isset( $ret['issues'][ParamValidator::PARAM_ISMULTI] )
) {
$ret['issues'][ParamValidator::PARAM_ISMULTI] =
'PARAM_ISMULTI cannot be used for presence-boolean-type parameters';
}
if ( ( $settings[ParamValidator::PARAM_DEFAULT] ?? false ) !== false &&
!isset( $ret['issues'][ParamValidator::PARAM_DEFAULT] )
) {
$ret['issues'][ParamValidator::PARAM_DEFAULT] =
'Default for presence-boolean-type parameters must be false or null';
}
return $ret;
}
public function getParamInfo( $name, array $settings, array $options ) {
$info = parent::getParamInfo( $name, $settings, $options );
// No need to report the default of "false"
$info['default'] = null;
return $info;
}
public function getHelpInfo( $name, array $settings, array $options ) {
$info = parent::getHelpInfo( $name, $settings, $options );
$info[ParamValidator::PARAM_TYPE] = MessageValue::new(
'paramvalidator-help-type-presenceboolean'
)->params( 1 );
// No need to report the default of "false"
$info[ParamValidator::PARAM_DEFAULT] = null;
return $info;
}
}
TypeDef/BooleanDef.php 0000666 00000003671 15133503751 0010631 0 ustar 00 <?php
namespace Wikimedia\ParamValidator\TypeDef;
use Wikimedia\Message\MessageValue;
use Wikimedia\Message\ParamType;
use Wikimedia\Message\ScalarParam;
use Wikimedia\ParamValidator\ParamValidator;
use Wikimedia\ParamValidator\TypeDef;
/**
* Type definition for boolean types
*
* This type accepts certain defined strings to mean 'true' or 'false'.
* The result from validate() is a PHP boolean.
*
* Failure codes:
* - 'badbool': The value is not a recognized boolean. No data.
*
* @since 1.34
* @unstable
*/
class BooleanDef extends TypeDef {
public static $TRUEVALS = [ 'true', 't', 'yes', 'y', 'on', '1' ];
public static $FALSEVALS = [ 'false', 'f', 'no', 'n', 'off', '0' ];
public function validate( $name, $value, array $settings, array $options ) {
$value = strtolower( $value );
if ( in_array( $value, self::$TRUEVALS, true ) ) {
return true;
}
if ( $value === '' || in_array( $value, self::$FALSEVALS, true ) ) {
return false;
}
$this->failure(
$this->failureMessage( 'badbool' )
->textListParams( array_map( [ $this, 'quoteVal' ], self::$TRUEVALS ) )
->numParams( count( self::$TRUEVALS ) )
->textListParams( array_merge(
array_map( [ $this, 'quoteVal' ], self::$FALSEVALS ),
[ MessageValue::new( 'paramvalidator-emptystring' ) ]
) )
->numParams( count( self::$FALSEVALS ) + 1 ),
$name, $value, $settings, $options
);
}
private function quoteVal( $v ) {
return new ScalarParam( ParamType::TEXT, "\"$v\"" );
}
public function stringifyValue( $name, $value, array $settings, array $options ) {
return $value ? self::$TRUEVALS[0] : self::$FALSEVALS[0];
}
public function getHelpInfo( $name, array $settings, array $options ) {
$info = parent::getHelpInfo( $name, $settings, $options );
$info[ParamValidator::PARAM_TYPE] = MessageValue::new( 'paramvalidator-help-type-boolean' )
->params( empty( $settings[ParamValidator::PARAM_ISMULTI] ) ? 1 : 2 );
return $info;
}
}
TypeDef/LimitDef.php 0000666 00000004202 15133503751 0010317 0 ustar 00 <?php
namespace Wikimedia\ParamValidator\TypeDef;
use Wikimedia\Message\MessageValue;
use Wikimedia\ParamValidator\ParamValidator;
/**
* Type definition for "limit" types
*
* A limit type is an integer type that also accepts the magic value "max".
* IntegerDef::PARAM_MIN defaults to 0 for this type.
*
* @see IntegerDef
* @since 1.34
* @unstable
*/
class LimitDef extends IntegerDef {
/**
* @inheritDoc
*
* Additional `$options` accepted:
* - 'parse-limit': (bool) Default true, set false to return 'max' rather
* than determining the effective value.
*/
public function validate( $name, $value, array $settings, array $options ) {
if ( $value === 'max' ) {
if ( $options['parse-limit'] ?? true ) {
$value = $this->callbacks->useHighLimits( $options )
? $settings[self::PARAM_MAX2] ?? $settings[self::PARAM_MAX] ?? PHP_INT_MAX
: $settings[self::PARAM_MAX] ?? PHP_INT_MAX;
}
return $value;
}
return parent::validate( $name, $value, $settings, $options );
}
public function normalizeSettings( array $settings ) {
$settings += [
self::PARAM_MIN => 0,
];
// Cannot be multi-valued
$settings[ParamValidator::PARAM_ISMULTI] = false;
return parent::normalizeSettings( $settings );
}
public function checkSettings( string $name, $settings, array $options, array $ret ): array {
$ret = parent::checkSettings( $name, $settings, $options, $ret );
if ( !empty( $settings[ParamValidator::PARAM_ISMULTI] ) &&
!isset( $ret['issues'][ParamValidator::PARAM_ISMULTI] )
) {
$ret['issues'][ParamValidator::PARAM_ISMULTI] =
'PARAM_ISMULTI cannot be used for limit-type parameters';
}
if ( ( $settings[self::PARAM_MIN] ?? 0 ) < 0 ) {
$ret['issues'][] = 'PARAM_MIN must be greater than or equal to 0';
}
if ( !isset( $settings[self::PARAM_MAX] ) ) {
$ret['issues'][] = 'PARAM_MAX must be set';
}
return $ret;
}
public function getHelpInfo( $name, array $settings, array $options ) {
$info = parent::getHelpInfo( $name, $settings, $options );
$info[ParamValidator::PARAM_TYPE] = MessageValue::new( 'paramvalidator-help-type-limit' )
->params( 1 );
return $info;
}
}
TypeDef/FloatDef.php 0000666 00000003564 15133503751 0010320 0 ustar 00 <?php
namespace Wikimedia\ParamValidator\TypeDef;
use Wikimedia\Message\MessageValue;
use Wikimedia\ParamValidator\ParamValidator;
/**
* Type definition for a floating-point type
*
* A valid representation consists of:
* - an optional sign (`+` or `-`)
* - a decimal number, using `.` as the decimal separator and no grouping
* - an optional E-notation suffix: the letter 'e' or 'E', an optional
* sign, and an integer
*
* Thus, for example, "12", "-.4", "6.022e23", or "+1.7e-10".
*
* The result from validate() is a PHP float.
*
* Failure codes:
* - 'badfloat': The value was invalid. No data.
* - 'badfloat-notfinite': The value was in a valid format, but conversion resulted in
* infinity or NAN.
*
* @since 1.34
* @unstable
*/
class FloatDef extends NumericDef {
protected $valueType = 'double';
public function validate( $name, $value, array $settings, array $options ) {
// Use a regex so as to avoid any potential oddness PHP's default conversion might allow.
if ( !preg_match( '/^[+-]?(?:\d*\.)?\d+(?:[eE][+-]?\d+)?$/D', $value ) ) {
$this->failure( 'badfloat', $name, $value, $settings, $options );
}
$ret = (float)$value;
if ( !is_finite( $ret ) ) {
$this->failure( 'badfloat-notfinite', $name, $value, $settings, $options );
}
return $this->checkRange( $ret, $name, $value, $settings, $options );
}
public function stringifyValue( $name, $value, array $settings, array $options ) {
// Ensure sufficient precision for round-tripping
$digits = PHP_FLOAT_DIG;
return sprintf( "%.{$digits}g", $value );
}
public function getHelpInfo( $name, array $settings, array $options ) {
$info = parent::getHelpInfo( $name, $settings, $options );
$info[ParamValidator::PARAM_TYPE] = MessageValue::new( 'paramvalidator-help-type-float' )
->params( empty( $settings[ParamValidator::PARAM_ISMULTI] ) ? 1 : 2 );
return $info;
}
}
TypeDef/NumericDef.php 0000666 00000015205 15133503751 0010650 0 ustar 00 <?php
namespace Wikimedia\ParamValidator\TypeDef;
use Wikimedia\Message\MessageValue;
use Wikimedia\Message\ParamType;
use Wikimedia\Message\ScalarParam;
use Wikimedia\ParamValidator\ParamValidator;
use Wikimedia\ParamValidator\TypeDef;
use Wikimedia\ParamValidator\ValidationException;
/**
* Type definition base class for numeric types
*
* * Failure codes:
* - 'outofrange': The value was outside of the allowed range. Data:
* - 'min': Minimum allowed, or null if there is no limit.
* - 'curmax': Current maximum allowed, or null if there is no limit.
* - 'max': Normal maximum allowed, or null if there is no limit.
* - 'highmax': High limits maximum allowed, or null if there is no limit.
*
* @stable to extend
* @since 1.35
* @unstable
*/
abstract class NumericDef extends TypeDef {
/**
* (bool) Whether to enforce the specified range.
*
* If set and truthy, the 'outofrange' failure is non-fatal.
*/
public const PARAM_IGNORE_RANGE = 'param-ignore-range';
/**
* (int|float) Minimum allowed value.
*/
public const PARAM_MIN = 'param-min';
/**
* (int|float) Maximum allowed value (normal limits)
*/
public const PARAM_MAX = 'param-max';
/**
* (int|float) Maximum allowed value (high limits)
*/
public const PARAM_MAX2 = 'param-max2';
/** @var string PHP type (as from `gettype()`) of values this NumericDef handles */
protected $valueType = 'integer';
/**
* Check the range of a value
* @param int|float $value Value to check.
* @param string $name Parameter name being validated.
* @param mixed $origValue Original value being validated.
* @param array $settings Parameter settings array.
* @param array $options Options array.
* @return int|float Validated value, may differ from $value if
* PARAM_IGNORE_RANGE was set.
* @throws ValidationException if the value out of range, and PARAM_IGNORE_RANGE wasn't set.
*/
protected function checkRange( $value, $name, $origValue, array $settings, array $options ) {
$min = $settings[self::PARAM_MIN] ?? null;
$max1 = $settings[self::PARAM_MAX] ?? null;
$max2 = $settings[self::PARAM_MAX2] ?? $max1;
$err = false;
if ( $min !== null && $value < $min ) {
$err = true;
$value = $min;
} elseif ( $max1 !== null && $value > $max1 ) {
if ( $max2 > $max1 && $this->callbacks->useHighLimits( $options ) ) {
if ( $value > $max2 ) {
$err = true;
$value = $max2;
}
} else {
$err = true;
$value = $max1;
}
}
if ( $err ) {
$what = '';
if ( $min !== null ) {
$what .= 'min';
}
if ( $max1 !== null ) {
$what .= 'max';
}
$max = $max2 !== null && $max2 > $max1 && $this->callbacks->useHighLimits( $options )
? $max2 : $max1;
$this->failure(
$this->failureMessage( 'outofrange', [
'min' => $min,
'curmax' => $max,
'max' => $max1,
'highmax' => $max2,
], $what )->numParams( $min ?? '', $max ?? '' ),
$name, $origValue, $settings, $options,
empty( $settings[self::PARAM_IGNORE_RANGE] )
);
}
return $value;
}
/**
* @inheritDoc
* @stable to override
*/
public function normalizeSettings( array $settings ) {
if ( !isset( $settings[self::PARAM_MAX] ) ) {
unset( $settings[self::PARAM_MAX2] );
}
if ( isset( $settings[self::PARAM_MAX2] ) && isset( $settings[self::PARAM_MAX] ) &&
$settings[self::PARAM_MAX2] < $settings[self::PARAM_MAX]
) {
$settings[self::PARAM_MAX2] = $settings[self::PARAM_MAX];
}
return parent::normalizeSettings( $settings );
}
/**
* @inheritDoc
* @stable to override
*/
public function checkSettings( string $name, $settings, array $options, array $ret ): array {
$ret = parent::checkSettings( $name, $settings, $options, $ret );
$ret['allowedKeys'] = array_merge( $ret['allowedKeys'], [
self::PARAM_IGNORE_RANGE, self::PARAM_MIN, self::PARAM_MAX, self::PARAM_MAX2,
] );
if ( !is_bool( $settings[self::PARAM_IGNORE_RANGE] ?? false ) ) {
$ret['issues'][self::PARAM_IGNORE_RANGE] = 'PARAM_IGNORE_RANGE must be boolean, got '
. gettype( $settings[self::PARAM_IGNORE_RANGE] );
}
$min = $settings[self::PARAM_MIN] ?? null;
$max = $settings[self::PARAM_MAX] ?? null;
$max2 = $settings[self::PARAM_MAX2] ?? null;
if ( $min !== null && gettype( $min ) !== $this->valueType ) {
$ret['issues'][self::PARAM_MIN] = "PARAM_MIN must be $this->valueType, got " . gettype( $min );
}
if ( $max !== null && gettype( $max ) !== $this->valueType ) {
$ret['issues'][self::PARAM_MAX] = "PARAM_MAX must be $this->valueType, got " . gettype( $max );
}
if ( $max2 !== null && gettype( $max2 ) !== $this->valueType ) {
$ret['issues'][self::PARAM_MAX2] = "PARAM_MAX2 must be $this->valueType, got "
. gettype( $max2 );
}
if ( $min !== null && $max !== null && $min > $max ) {
$ret['issues'][] = "PARAM_MIN must be less than or equal to PARAM_MAX, but $min > $max";
}
if ( $max2 !== null ) {
if ( $max === null ) {
$ret['issues'][] = 'PARAM_MAX2 cannot be used without PARAM_MAX';
} elseif ( $max2 < $max ) {
$ret['issues'][] = "PARAM_MAX2 must be greater than or equal to PARAM_MAX, but $max2 < $max";
}
}
return $ret;
}
/**
* @inheritDoc
* @stable to override
*/
public function getParamInfo( $name, array $settings, array $options ) {
$info = parent::getParamInfo( $name, $settings, $options );
$info['min'] = $settings[self::PARAM_MIN] ?? null;
$info['max'] = $settings[self::PARAM_MAX] ?? null;
$info['highmax'] = $settings[self::PARAM_MAX2] ?? $info['max'];
if ( $info['max'] === null || $info['highmax'] <= $info['max'] ) {
unset( $info['highmax'] );
}
return $info;
}
/**
* @inheritDoc
* @stable to override
*/
public function getHelpInfo( $name, array $settings, array $options ) {
$info = parent::getHelpInfo( $name, $settings, $options );
$min = '−∞';
$max = '∞';
$msg = '';
if ( isset( $settings[self::PARAM_MIN] ) ) {
$msg .= 'min';
$min = new ScalarParam( ParamType::NUM, $settings[self::PARAM_MIN] );
}
if ( isset( $settings[self::PARAM_MAX] ) ) {
$msg .= 'max';
$max = $settings[self::PARAM_MAX];
if ( isset( $settings[self::PARAM_MAX2] ) && $settings[self::PARAM_MAX2] > $max &&
$this->callbacks->useHighLimits( $options )
) {
$max = $settings[self::PARAM_MAX2];
}
$max = new ScalarParam( ParamType::NUM, $max );
}
if ( $msg !== '' ) {
$isMulti = !empty( $settings[ParamValidator::PARAM_ISMULTI] );
// Messages: paramvalidator-help-type-number-min, paramvalidator-help-type-number-max,
// paramvalidator-help-type-number-minmax
$info[self::PARAM_MIN] = MessageValue::new( "paramvalidator-help-type-number-$msg" )
->params( $isMulti ? 2 : 1, $min, $max );
}
return $info;
}
}
TypeDef/UploadDef.php 0000666 00000011677 15133503751 0010503 0 ustar 00 <?php
namespace Wikimedia\ParamValidator\TypeDef;
use InvalidArgumentException;
use Psr\Http\Message\UploadedFileInterface;
use UnexpectedValueException;
use Wikimedia\Message\MessageValue;
use Wikimedia\ParamValidator\ParamValidator;
use Wikimedia\ParamValidator\TypeDef;
use Wikimedia\ParamValidator\Util\UploadedFile;
/**
* Type definition for upload types
*
* The result from validate() is an object implementing UploadedFileInterface.
*
* Failure codes:
* - 'badupload': The upload is not valid. Data:
* - 'code': A value indicating why the upload was not valid:
* - 'inisize': The upload exceeded the maximum in php.ini.
* - 'formsize': The upload exceeded the maximum in the form post.
* - 'partial': The file was only partially uploaded.
* - 'nofile': There was no file.
* - 'notmpdir': PHP has no temporary directory to store the upload.
* - 'cantwrite': PHP could not store the upload.
* - 'phpext': A PHP extension rejected the upload.
* - 'notupload': The field was present in the submission but was not encoded as an upload.
* - 'size': The configured size (in bytes), if 'code' is 'inisize'.
*
* @since 1.34
* @unstable
*/
class UploadDef extends TypeDef {
public function getValue( $name, array $settings, array $options ) {
$ret = $this->callbacks->getUploadedFile( $name, $options );
if ( $ret && $ret->getError() === UPLOAD_ERR_NO_FILE &&
!$this->callbacks->hasParam( $name, $options )
) {
// This seems to be that the client explicitly specified "no file" for the field
// instead of just omitting the field completely. DWTM.
$ret = null;
} elseif ( !$ret && $this->callbacks->hasParam( $name, $options ) ) {
// The client didn't format their upload properly so it came in as an ordinary
// field. Convert it to an error.
$ret = new UploadedFile( [
'name' => '',
'type' => '',
'tmp_name' => '',
'error' => -42, // PHP's UPLOAD_ERR_* are all positive numbers.
'size' => 0,
] );
}
return $ret;
}
/**
* Fetch the value of PHP's upload_max_filesize ini setting
*
* This method exists so it can be mocked by unit tests that can't
* affect ini_get() directly.
*
* @codeCoverageIgnore
* @return string|false
*/
protected function getIniSize() {
return ini_get( 'upload_max_filesize' );
}
public function validate( $name, $value, array $settings, array $options ) {
static $codemap = [
-42 => 'notupload', // Local from getValue()
UPLOAD_ERR_FORM_SIZE => 'formsize',
UPLOAD_ERR_PARTIAL => 'partial',
UPLOAD_ERR_NO_FILE => 'nofile',
UPLOAD_ERR_NO_TMP_DIR => 'notmpdir',
UPLOAD_ERR_CANT_WRITE => 'cantwrite',
UPLOAD_ERR_EXTENSION => 'phpext',
];
if ( !$value instanceof UploadedFileInterface ) {
// Err?
$type = is_object( $value ) ? get_class( $value ) : gettype( $value );
throw new InvalidArgumentException( "\$value must be UploadedFileInterface, got $type" );
}
$err = $value->getError();
if ( $err === UPLOAD_ERR_OK ) {
return $value;
} elseif ( $err === UPLOAD_ERR_INI_SIZE ) {
static $prefixes = [
'g' => 1024 ** 3,
'm' => 1024 ** 2,
'k' => 1024 ** 1,
];
$size = $this->getIniSize();
$last = strtolower( substr( $size, -1 ) );
$size = intval( $size, 10 ) * ( $prefixes[$last] ?? 1 );
$this->failure(
$this->failureMessage( 'badupload', [
'code' => 'inisize',
'size' => $size,
], 'inisize' )->sizeParams( $size ),
$name, '', $settings, $options
);
} elseif ( isset( $codemap[$err] ) ) {
$this->failure(
$this->failureMessage( 'badupload', [ 'code' => $codemap[$err] ], $codemap[$err] ),
$name, '', $settings, $options
);
} else {
$constant = '';
foreach ( get_defined_constants() as $c => $v ) {
// @phan-suppress-next-line PhanTypeComparisonFromArray
if ( $v === $err && str_starts_with( $c, 'UPLOAD_ERR_' ) ) {
$constant = " ($c?)";
}
}
throw new UnexpectedValueException( "Unrecognized PHP upload error value $err$constant" );
}
}
public function checkSettings( string $name, $settings, array $options, array $ret ): array {
$ret = parent::checkSettings( $name, $settings, $options, $ret );
if ( isset( $settings[ParamValidator::PARAM_DEFAULT] ) ) {
$ret['issues'][ParamValidator::PARAM_DEFAULT] =
'Cannot specify a default for upload-type parameters';
}
if ( !empty( $settings[ParamValidator::PARAM_ISMULTI] ) &&
!isset( $ret['issues'][ParamValidator::PARAM_ISMULTI] )
) {
$ret['issues'][ParamValidator::PARAM_ISMULTI] =
'PARAM_ISMULTI cannot be used for upload-type parameters';
}
return $ret;
}
public function stringifyValue( $name, $value, array $settings, array $options ) {
// Not going to happen.
return null;
}
public function getHelpInfo( $name, array $settings, array $options ) {
$info = parent::getHelpInfo( $name, $settings, $options );
$info[ParamValidator::PARAM_TYPE] = MessageValue::new( 'paramvalidator-help-type-upload' );
return $info;
}
}
TypeDef/TimestampDef.php 0000666 00000011311 15133503751 0011203 0 ustar 00 <?php
namespace Wikimedia\ParamValidator\TypeDef;
use InvalidArgumentException;
use Wikimedia\Message\MessageValue;
use Wikimedia\ParamValidator\Callbacks;
use Wikimedia\ParamValidator\ParamValidator;
use Wikimedia\ParamValidator\TypeDef;
use Wikimedia\ParamValidator\ValidationException;
use Wikimedia\Timestamp\ConvertibleTimestamp;
use Wikimedia\Timestamp\TimestampException;
/**
* Type definition for timestamp types
*
* This uses the wikimedia/timestamp library for parsing and formatting the
* timestamps.
*
* The result from validate() is a ConvertibleTimestamp by default, but this
* may be changed by both a constructor option and a PARAM constant.
*
* Failure codes:
* - 'badtimestamp': The timestamp is not valid. No data, but the
* TimestampException is available via Exception::getPrevious().
* - 'unclearnowtimestamp': Non-fatal. The value is the empty string or "0".
* Use 'now' instead if you really want the current timestamp. No data.
*
* @since 1.34
* @unstable
*/
class TimestampDef extends TypeDef {
/**
* (string|int) Timestamp format to return from validate()
*
* Values include:
* - 'ConvertibleTimestamp': A ConvertibleTimestamp object.
* - 'DateTime': A PHP DateTime object
* - One of ConvertibleTimestamp's TS_* constants.
*
* This does not affect the format returned by stringifyValue().
*/
public const PARAM_TIMESTAMP_FORMAT = 'param-timestamp-format';
/** @var string|int */
protected $defaultFormat;
/** @var int */
protected $stringifyFormat;
/**
* @param Callbacks $callbacks
* @param array $options Options:
* - defaultFormat: (string|int) Default for PARAM_TIMESTAMP_FORMAT.
* Default if not specified is 'ConvertibleTimestamp'.
* - stringifyFormat: (int) Format to use for stringifyValue().
* Default is TS_ISO_8601.
*/
public function __construct( Callbacks $callbacks, array $options = [] ) {
parent::__construct( $callbacks );
$this->defaultFormat = $options['defaultFormat'] ?? 'ConvertibleTimestamp';
$this->stringifyFormat = $options['stringifyFormat'] ?? TS_ISO_8601;
// Check values by trying to convert 0
if ( $this->defaultFormat !== 'ConvertibleTimestamp' && $this->defaultFormat !== 'DateTime' &&
ConvertibleTimestamp::convert( $this->defaultFormat, 0 ) === false
) {
throw new InvalidArgumentException( 'Invalid value for $options[\'defaultFormat\']' );
}
if ( ConvertibleTimestamp::convert( $this->stringifyFormat, 0 ) === false ) {
throw new InvalidArgumentException( 'Invalid value for $options[\'stringifyFormat\']' );
}
}
public function validate( $name, $value, array $settings, array $options ) {
// Confusing synonyms for the current time accepted by ConvertibleTimestamp
if ( !$value ) {
$this->failure( 'unclearnowtimestamp', $name, $value, $settings, $options, false );
$value = 'now';
}
$format = $settings[self::PARAM_TIMESTAMP_FORMAT] ?? $this->defaultFormat;
try {
$timestampObj = new ConvertibleTimestamp( $value === 'now' ? false : $value );
$timestamp = ( $format !== 'ConvertibleTimestamp' && $format !== 'DateTime' )
? $timestampObj->getTimestamp( $format )
: null;
} catch ( TimestampException $ex ) {
// $this->failure() doesn't handle passing a previous exception
throw new ValidationException(
$this->failureMessage( 'badtimestamp' )->plaintextParams( $name, $value ),
$name, $value, $settings, $ex
);
}
switch ( $format ) {
case 'ConvertibleTimestamp':
return $timestampObj;
case 'DateTime':
// Eew, no getter.
return $timestampObj->timestamp;
default:
return $timestamp;
}
}
public function checkSettings( string $name, $settings, array $options, array $ret ): array {
$ret = parent::checkSettings( $name, $settings, $options, $ret );
$ret['allowedKeys'] = array_merge( $ret['allowedKeys'], [
self::PARAM_TIMESTAMP_FORMAT,
] );
$f = $settings[self::PARAM_TIMESTAMP_FORMAT] ?? $this->defaultFormat;
if ( $f !== 'ConvertibleTimestamp' && $f !== 'DateTime' &&
ConvertibleTimestamp::convert( $f, 0 ) === false
) {
$ret['issues'][self::PARAM_TIMESTAMP_FORMAT] = 'Value for PARAM_TIMESTAMP_FORMAT is not valid';
}
return $ret;
}
public function stringifyValue( $name, $value, array $settings, array $options ) {
if ( !$value instanceof ConvertibleTimestamp ) {
$value = new ConvertibleTimestamp( $value );
}
return $value->getTimestamp( $this->stringifyFormat );
}
public function getHelpInfo( $name, array $settings, array $options ) {
$info = parent::getHelpInfo( $name, $settings, $options );
$info[ParamValidator::PARAM_TYPE] = MessageValue::new( 'paramvalidator-help-type-timestamp' )
->params( empty( $settings[ParamValidator::PARAM_ISMULTI] ) ? 1 : 2 );
return $info;
}
}
TypeDef/StringDef.php 0000666 00000012170 15133503751 0010512 0 ustar 00 <?php
namespace Wikimedia\ParamValidator\TypeDef;
use Wikimedia\Message\MessageValue;
use Wikimedia\ParamValidator\Callbacks;
use Wikimedia\ParamValidator\ParamValidator;
use Wikimedia\ParamValidator\TypeDef;
/**
* Type definition for string types
*
* The result from validate() is a PHP string.
*
* Failure codes:
* - 'missingparam': The parameter is the empty string (and that's not allowed). No data.
*
* Additional codes may be generated when using certain PARAM constants. See
* the constants' documentation for details.
*
* @since 1.34
* @unstable
*/
class StringDef extends TypeDef {
/**
* (integer) Maximum length of a string in bytes.
*
* Failure codes:
* - 'maxbytes': The string is too long. Data:
* - 'maxbytes': The maximum number of bytes allowed, or null if no limit
* - 'maxchars': The maximum number of characters allowed, or null if no limit
*/
public const PARAM_MAX_BYTES = 'param-max-bytes';
/**
* (integer) Maximum length of a string in characters (Unicode codepoints).
*
* The string is assumed to be encoded as UTF-8.
*
* Failure codes:
* - 'maxchars': The string is too long. Data:
* - 'maxbytes': The maximum number of bytes allowed, or null if no limit
* - 'maxchars': The maximum number of characters allowed, or null if no limit
*/
public const PARAM_MAX_CHARS = 'param-max-chars';
protected $allowEmptyWhenRequired = false;
/**
* @param Callbacks $callbacks
* @param array $options Options:
* - allowEmptyWhenRequired: (bool) Whether to reject the empty string when PARAM_REQUIRED.
* Defaults to false.
*/
public function __construct( Callbacks $callbacks, array $options = [] ) {
parent::__construct( $callbacks );
$this->allowEmptyWhenRequired = !empty( $options['allowEmptyWhenRequired'] );
}
public function validate( $name, $value, array $settings, array $options ) {
if ( !$this->allowEmptyWhenRequired && $value === '' &&
!empty( $settings[ParamValidator::PARAM_REQUIRED] )
) {
$this->failure( 'missingparam', $name, $value, $settings, $options );
}
$len = strlen( $value );
if ( isset( $settings[self::PARAM_MAX_BYTES] ) && $len > $settings[self::PARAM_MAX_BYTES] ) {
$this->failure(
$this->failureMessage( 'maxbytes', [
'maxbytes' => $settings[self::PARAM_MAX_BYTES] ?? null,
'maxchars' => $settings[self::PARAM_MAX_CHARS] ?? null,
] )->numParams( $settings[self::PARAM_MAX_BYTES], $len ),
$name, $value, $settings, $options
);
}
$len = mb_strlen( $value, 'UTF-8' );
if ( isset( $settings[self::PARAM_MAX_CHARS] ) && $len > $settings[self::PARAM_MAX_CHARS] ) {
$this->failure(
$this->failureMessage( 'maxchars', [
'maxbytes' => $settings[self::PARAM_MAX_BYTES] ?? null,
'maxchars' => $settings[self::PARAM_MAX_CHARS] ?? null,
] )->numParams( $settings[self::PARAM_MAX_CHARS], $len ),
$name, $value, $settings, $options
);
}
return $value;
}
public function checkSettings( string $name, $settings, array $options, array $ret ): array {
$ret = parent::checkSettings( $name, $settings, $options, $ret );
$ret['allowedKeys'] = array_merge( $ret['allowedKeys'], [
self::PARAM_MAX_BYTES, self::PARAM_MAX_CHARS,
] );
$maxb = $settings[self::PARAM_MAX_BYTES] ?? PHP_INT_MAX;
if ( !is_int( $maxb ) ) {
$ret['issues'][self::PARAM_MAX_BYTES] = 'PARAM_MAX_BYTES must be an integer, got '
. gettype( $maxb );
} elseif ( $maxb < 0 ) {
$ret['issues'][self::PARAM_MAX_BYTES] = 'PARAM_MAX_BYTES must be greater than or equal to 0';
}
$maxc = $settings[self::PARAM_MAX_CHARS] ?? PHP_INT_MAX;
if ( !is_int( $maxc ) ) {
$ret['issues'][self::PARAM_MAX_CHARS] = 'PARAM_MAX_CHARS must be an integer, got '
. gettype( $maxc );
} elseif ( $maxc < 0 ) {
$ret['issues'][self::PARAM_MAX_CHARS] = 'PARAM_MAX_CHARS must be greater than or equal to 0';
}
if ( !$this->allowEmptyWhenRequired && !empty( $settings[ParamValidator::PARAM_REQUIRED] ) ) {
if ( $maxb === 0 ) {
$ret['issues'][] = 'PARAM_REQUIRED is set, allowEmptyWhenRequired is not set, and '
. 'PARAM_MAX_BYTES is 0. That\'s impossible to satisfy.';
}
if ( $maxc === 0 ) {
$ret['issues'][] = 'PARAM_REQUIRED is set, allowEmptyWhenRequired is not set, and '
. 'PARAM_MAX_CHARS is 0. That\'s impossible to satisfy.';
}
}
return $ret;
}
public function getParamInfo( $name, array $settings, array $options ) {
$info = parent::getParamInfo( $name, $settings, $options );
$info['maxbytes'] = $settings[self::PARAM_MAX_BYTES] ?? null;
$info['maxchars'] = $settings[self::PARAM_MAX_CHARS] ?? null;
return $info;
}
public function getHelpInfo( $name, array $settings, array $options ) {
$info = parent::getHelpInfo( $name, $settings, $options );
if ( isset( $settings[self::PARAM_MAX_BYTES] ) ) {
$info[self::PARAM_MAX_BYTES] = MessageValue::new( 'paramvalidator-help-type-string-maxbytes' )
->numParams( $settings[self::PARAM_MAX_BYTES] );
}
if ( isset( $settings[self::PARAM_MAX_CHARS] ) ) {
$info[self::PARAM_MAX_CHARS] = MessageValue::new( 'paramvalidator-help-type-string-maxchars' )
->numParams( $settings[self::PARAM_MAX_CHARS] );
}
return $info;
}
}
TypeDef/ExpiryDef.php 0000666 00000012731 15133503751 0010527 0 ustar 00 <?php
namespace Wikimedia\ParamValidator\TypeDef;
use InvalidArgumentException;
use Wikimedia\Message\DataMessageValue;
use Wikimedia\Message\MessageValue;
use Wikimedia\ParamValidator\ParamValidator;
use Wikimedia\ParamValidator\TypeDef;
use Wikimedia\Timestamp\ConvertibleTimestamp;
/**
* Type definition for expiry timestamps.
*
* @since 1.35
*/
class ExpiryDef extends TypeDef {
/** @var array Possible values that mean "doesn't expire". */
public const INFINITY_VALS = [ 'infinite', 'indefinite', 'infinity', 'never' ];
/**
* (bool) If truthy, the value given for the PARAM_MAX setting is used if the provided expiry
* exceeds it, and the 'badexpiry-duration' message is shown as a warning.
*
* If false, 'badexpiry-duration' is shown and is fatal.
*/
public const PARAM_USE_MAX = 'param-use-max';
/**
* (int|float) Maximum non-infinity duration.
*/
public const PARAM_MAX = 'param-max';
public function validate( $name, $value, array $settings, array $options ) {
try {
$expiry = self::normalizeExpiry( $value, TS_ISO_8601 );
} catch ( InvalidArgumentException $e ) {
$this->failure( 'badexpiry', $name, $value, $settings, $options );
}
if ( $expiry !== 'infinity' && $expiry < ConvertibleTimestamp::now( TS_ISO_8601 ) ) {
$this->failure( 'badexpiry-past', $name, $value, $settings, $options );
}
$max = $settings[self::PARAM_MAX] ?? null;
if ( self::expiryExceedsMax( $expiry, $max ) ) {
$dontUseMax = empty( $settings[self::PARAM_USE_MAX] );
// Show warning that expiry exceeds the max, and that the max is being used instead.
$msg = DataMessageValue::new(
$dontUseMax
? 'paramvalidator-badexpiry-duration'
: 'paramvalidator-badexpiry-duration-max',
[ $max ]
);
$this->failure( $msg, $name, $value, $settings, $options, $dontUseMax );
return self::normalizeExpiry( $max, TS_ISO_8601 );
}
return $expiry;
}
public function getHelpInfo( $name, array $settings, array $options ) {
$info = parent::getHelpInfo( $name, $settings, $options );
$info[ParamValidator::PARAM_TYPE] = MessageValue::new( 'paramvalidator-help-type-expiry' )
->params( empty( $settings[ParamValidator::PARAM_ISMULTI] ) ? 1 : 2 )
->textListParams(
// Should be quoted or monospace for presentation purposes,
// but textListParams() doesn't do this.
array_map( static function ( $val ) {
return "\"$val\"";
}, self::INFINITY_VALS )
);
return $info;
}
/**
* Normalize a user-inputted expiry in ConvertibleTimestamp.
* @param string|null $expiry
* @param int|null $style null or in a format acceptable to ConvertibleTimestamp (TS_* constants)
*
* @return ConvertibleTimestamp|string|null Timestamp as ConvertibleTimestamp if $style is null, a string
* timestamp in $style is not null, 'infinity' if $expiry is one of the self::INFINITY_VALS,
* or null if $expiry is null.
*
* @throws InvalidArgumentException if $expiry is invalid
*/
public static function normalizeExpiry( ?string $expiry = null, ?int $style = null ) {
if ( $expiry === null ) {
return null;
}
if ( in_array( $expiry, self::INFINITY_VALS, true ) ) {
return 'infinity';
}
// ConvertibleTimestamp::time() used so we can fake the current time in ExpiryDefTest.
$unix = strtotime( $expiry, ConvertibleTimestamp::time() );
if ( $unix === false ) {
// Invalid expiry.
throw new InvalidArgumentException( "Invalid expiry value: {$expiry}" );
}
// Don't pass 0, since ConvertibleTimestamp interprets that to mean the current timestamp.
// '00' does the right thing. Without this check, calling normalizeExpiry()
// with 1970-01-01T00:00:00Z incorrectly returns the current time.
$expiryConvertibleTimestamp = new ConvertibleTimestamp( $unix === 0 ? '00' : $unix );
if ( $style !== null ) {
return $expiryConvertibleTimestamp->getTimestamp( $style );
}
return $expiryConvertibleTimestamp;
}
/**
* Returns a normalized expiry or the max expiry if the given expiry exceeds it.
* @param string|null $expiry
* @param string|null $maxExpiryDuration
* @param int|null $style null or in a format acceptable to ConvertibleTimestamp (TS_* constants)
* @return ConvertibleTimestamp|string|null Timestamp as ConvertibleTimestamp if $style is null, a string
* timestamp in $style is not null, 'infinity' if $expiry is one of the self::INFINITY_VALS,
* or null if $expiry is null.
*/
public static function normalizeUsingMaxExpiry( ?string $expiry, ?string $maxExpiryDuration, ?int $style ) {
if ( self::expiryExceedsMax( $expiry, $maxExpiryDuration ) ) {
return self::normalizeExpiry( $maxExpiryDuration, $style );
}
return self::normalizeExpiry( $expiry, $style );
}
public function checkSettings( string $name, $settings, array $options, array $ret ): array {
$ret = parent::checkSettings( $name, $settings, $options, $ret );
$ret['allowedKeys'][] = self::PARAM_USE_MAX;
$ret['allowedKeys'][] = self::PARAM_MAX;
return $ret;
}
/**
* Given an expiry, test if the normalized value exceeds the given maximum.
*
* @param string|null $expiry
* @param string|null $max Relative maximum duration acceptable by strtotime() (i.e. '6 months')
* @return bool
*/
private static function expiryExceedsMax( ?string $expiry, ?string $max = null ): bool {
$expiry = self::normalizeExpiry( $expiry );
$max = self::normalizeExpiry( $max );
if ( !$max || !$expiry || $expiry === 'infinity' ) {
// Either there is no max or given expiry was invalid.
return false;
}
return $expiry > $max;
}
}
TypeDef/PasswordDef.php 0000666 00000001624 15133503751 0011050 0 ustar 00 <?php
namespace Wikimedia\ParamValidator\TypeDef;
use Wikimedia\ParamValidator\ParamValidator;
/**
* Type definition for "password" types
*
* This is a string type that forces PARAM_SENSITIVE = true.
*
* @see StringDef
* @since 1.34
* @unstable
*/
class PasswordDef extends StringDef {
public function normalizeSettings( array $settings ) {
$settings[ParamValidator::PARAM_SENSITIVE] = true;
return parent::normalizeSettings( $settings );
}
public function checkSettings( string $name, $settings, array $options, array $ret ): array {
$ret = parent::checkSettings( $name, $settings, $options, $ret );
if ( ( $settings[ParamValidator::PARAM_SENSITIVE] ?? true ) !== true &&
!isset( $ret['issues'][ParamValidator::PARAM_SENSITIVE] )
) {
$ret['issues'][ParamValidator::PARAM_SENSITIVE] =
'Cannot set PARAM_SENSITIVE to false for password-type parameters';
}
return $ret;
}
}
TypeDef/IntegerDef.php 0000666 00000003311 15133503751 0010636 0 ustar 00 <?php
namespace Wikimedia\ParamValidator\TypeDef;
use Wikimedia\Message\MessageValue;
use Wikimedia\ParamValidator\ParamValidator;
/**
* Type definition for integer types
*
* A valid representation consists of an optional sign (`+` or `-`) followed by
* one or more decimal digits.
*
* The result from validate() is a PHP integer.
*
* * Failure codes:
* - 'badinteger': The value was invalid or could not be represented as a PHP
* integer. No data.
*
* @since 1.34
* @unstable
*/
class IntegerDef extends NumericDef {
public function validate( $name, $value, array $settings, array $options ) {
if ( is_array( $value ) || !preg_match( '/^[+-]?\d+$/D', $value ) ) {
$this->failure( 'badinteger', $name, $value, $settings, $options );
}
$ret = intval( $value, 10 );
// intval() returns min/max on overflow, so check that
if ( $ret === PHP_INT_MAX || $ret === PHP_INT_MIN ) {
$tmp = ( $ret < 0 ? '-' : '' ) . ltrim( $value, '-0' );
if ( $tmp !== (string)$ret ) {
$this->failure( 'badinteger', $name, $value, $settings, $options );
}
}
return $this->checkRange( $ret, $name, $value, $settings, $options );
}
public function getHelpInfo( $name, array $settings, array $options ) {
$info = parent::getHelpInfo( $name, $settings, $options );
$info[ParamValidator::PARAM_TYPE] = MessageValue::new( 'paramvalidator-help-type-integer' )
->params( empty( $settings[ParamValidator::PARAM_ISMULTI] ) ? 1 : 2 );
return $info;
}
public function stringifyValue( $name, $value, array $settings, array $options ) {
if ( !is_array( $value ) ) {
return parent::stringifyValue( $name, $value, $settings, $options );
}
return ParamValidator::implodeMultiValue( $value );
}
}
TypeDef.php 0000666 00000021471 15133503751 0006631 0 ustar 00 <?php
namespace Wikimedia\ParamValidator;
use Wikimedia\Message\DataMessageValue;
use Wikimedia\Message\MessageValue;
/**
* Base definition for ParamValidator types.
*
* Most methods in this class accept an "options array". This is just the `$options`
* passed to ParamValidator::getValue(), ParamValidator::validateValue(), and the like
* and is intended for communication of non-global state to the Callbacks.
*
* @stable to extend
* @since 1.34
* @unstable
*/
abstract class TypeDef {
/** @var Callbacks */
protected $callbacks;
/**
* @stable to call
*
* @param Callbacks $callbacks
*/
public function __construct( Callbacks $callbacks ) {
$this->callbacks = $callbacks;
}
/**
* Record a failure message
*
* Depending on `$fatal`, this will either throw a ValidationException or
* call $this->callbacks->recordCondition().
*
* Note that parameters for `$name` and `$value` are always added as `$1`
* and `$2`.
*
* @param DataMessageValue|string $failure Failure code or message.
* @param string $name Parameter name being validated.
* @param mixed $value Value being validated.
* @param array $settings Parameter settings array.
* @param array $options Options array.
* @param bool $fatal Whether the failure is fatal
*/
protected function failure(
$failure, $name, $value, array $settings, array $options, $fatal = true
) {
if ( !is_string( $value ) ) {
$value = (string)$this->stringifyValue( $name, $value, $settings, $options );
}
if ( is_string( $failure ) ) {
$mv = $this->failureMessage( $failure )
->plaintextParams( $name, $value );
} else {
$mv = DataMessageValue::new( $failure->getKey(), [], $failure->getCode(), $failure->getData() )
->plaintextParams( $name, $value )
->params( ...$failure->getParams() );
}
if ( $fatal ) {
throw new ValidationException( $mv, $name, $value, $settings );
}
$this->callbacks->recordCondition( $mv, $name, $value, $settings, $options );
}
/**
* Create a DataMessageValue representing a failure
*
* The message key will be "paramvalidator-$code" or "paramvalidator-$code-$suffix".
*
* Use DataMessageValue's param mutators to add additional MessageParams.
* Note that `failure()` will prepend parameters for `$name` and `$value`.
*
* @param string $code Failure code.
* @param array|null $data Failure data.
* @param string|null $suffix Suffix to append when producing the message key
* @return DataMessageValue
*/
protected function failureMessage( $code, array $data = null, $suffix = null ): DataMessageValue {
return DataMessageValue::new(
"paramvalidator-$code" . ( $suffix !== null ? "-$suffix" : '' ),
[], $code, $data
);
}
/**
* Get the value from the request
* @stable to override
*
* @note Only override this if you need to use something other than
* $this->callbacks->getValue() to fetch the value. Reformatting from a
* string should typically be done by self::validate().
* @note Handling of ParamValidator::PARAM_DEFAULT should be left to ParamValidator,
* as should PARAM_REQUIRED and the like.
*
* @param string $name Parameter name being fetched.
* @param array $settings Parameter settings array.
* @param array $options Options array.
* @return null|mixed Return null if the value wasn't present, otherwise a
* value to be passed to self::validate().
*/
public function getValue( $name, array $settings, array $options ) {
return $this->callbacks->getValue( $name, null, $options );
}
/**
* Validate the value
*
* When ParamValidator is processing a multi-valued parameter, this will be
* called once for each of the supplied values. Which may mean zero calls.
*
* When getValue() returned null, this will not be called.
*
* @param string $name Parameter name being validated.
* @param mixed $value Value to validate, from getValue().
* @param array $settings Parameter settings array.
* @param array $options Options array. Note the following values that may be set
* by ParamValidator:
* - is-default: (bool) If present and true, the value was taken from PARAM_DEFAULT rather
* that being supplied by the client.
* - values-list: (string[]) If defined, values of a multi-valued parameter are being processed
* (and this array holds the full set of values).
* @return mixed Validated value
* @throws ValidationException if the value is invalid
*/
abstract public function validate( $name, $value, array $settings, array $options );
/**
* Normalize a settings array
* @stable to override
* @param array $settings
* @return array
*/
public function normalizeSettings( array $settings ) {
return $settings;
}
/**
* Validate a parameter settings array
*
* This is intended for validation of parameter settings during unit or
* integration testing, and should implement strict checks.
*
* The rest of the code should generally be more permissive.
*
* @see ParamValidator::checkSettings()
* @stable to override
*
* @param string $name Parameter name
* @param array|mixed $settings Default value or an array of settings
* using PARAM_* constants.
* @param array $options Options array, passed through to the TypeDef and Callbacks.
* @param array $ret
* - 'issues': (string[]) Errors detected in $settings, as English text. If the settings
* are valid, this will be the empty array. Keys on input are ParamValidator constants,
* allowing the typedef to easily override core validation; this need not be preserved
* when returned.
* - 'allowedKeys': (string[]) ParamValidator keys that are allowed in `$settings`.
* - 'messages': (MessageValue[]) Messages to be checked for existence.
* @return array $ret, with any relevant changes.
*/
public function checkSettings( string $name, $settings, array $options, array $ret ): array {
return $ret;
}
/**
* Get the values for enum-like parameters
*
* This is primarily intended for documentation and implementation of
* PARAM_ALL; it is the responsibility of the TypeDef to ensure that validate()
* accepts the values returned here.
* @stable to override
*
* @param string $name Parameter name being validated.
* @param array $settings Parameter settings array.
* @param array $options Options array.
* @return array|null All possible enumerated values, or null if this is
* not an enumeration.
*/
public function getEnumValues( $name, array $settings, array $options ) {
return null;
}
/**
* Convert a value to a string representation.
*
* This is intended as the inverse of getValue() and validate(): this
* should accept anything returned by those methods or expected to be used
* as PARAM_DEFAULT, and if the string from this method is passed in as client
* input or PARAM_DEFAULT it should give equivalent output from validate().
*
* @param string $name Parameter name being converted.
* @param mixed $value Parameter value being converted. Do not pass null.
* @param array $settings Parameter settings array.
* @param array $options Options array.
* @return string|null Return null if there is no representation of $value
* reasonably satisfying the description given.
*/
public function stringifyValue( $name, $value, array $settings, array $options ) {
return (string)$value;
}
/**
* Describe parameter settings in a machine-readable format.
*
* Keys should be short strings using lowercase ASCII letters. Values
* should generally be values that could be encoded in JSON or the like.
*
* This is intended to handle PARAM constants specific to this class. It
* generally shouldn't handle constants defined on ParamValidator itself.
* @stable to override
*
* @param string $name Parameter name.
* @param array $settings Parameter settings array.
* @param array $options Options array.
* @return array
*/
public function getParamInfo( $name, array $settings, array $options ) {
return [];
}
/**
* Describe parameter settings in human-readable format
*
* Keys in the returned array should generally correspond to PARAM
* constants.
*
* If relevant, a MessageValue describing the type itself should be
* returned with key ParamValidator::PARAM_TYPE.
*
* The default messages for other ParamValidator-defined PARAM constants
* may be suppressed by returning null as the value for those constants, or
* replaced by returning a replacement MessageValue. Normally, however,
* the default messages should not be changed.
*
* MessageValues describing any other constraints applied via PARAM
* constants specific to this class should also be returned.
* @stable to override
*
* @param string $name Parameter name being described.
* @param array $settings Parameter settings array.
* @param array $options Options array.
* @return (MessageValue|null)[]
*/
public function getHelpInfo( $name, array $settings, array $options ) {
return [];
}
}
TypeDef/TagsDefTest.php 0000666 00000010250 15133511337 0010776 0 ustar 00 <?php
namespace MediaWiki\ParamValidator\TypeDef;
use ChangeTags;
use MediaWiki\ChangeTags\ChangeTagsStore;
use MediaWikiIntegrationTestCase;
use Wikimedia\Message\DataMessageValue;
use Wikimedia\ParamValidator\ParamValidator;
use Wikimedia\ParamValidator\SimpleCallbacks;
use Wikimedia\ParamValidator\ValidationException;
/**
* @group Database
* @covers MediaWiki\ParamValidator\TypeDef\TagsDef
*/
class TagsDefTest extends MediaWikiIntegrationTestCase {
protected static $testClass = TagsDef::class;
protected function setUp(): void {
parent::setUp();
ChangeTags::defineTag( 'tag1' );
ChangeTags::defineTag( 'tag2' );
$this->tablesUsed[] = 'change_tag_def';
// Since the type def shouldn't care about the specific user,
// remove the right from relevant groups to ensure that it's not
// checking.
$this->setGroupPermissions( [
'*' => [ 'applychangetags' => false ],
'user' => [ 'applychangetags' => false ],
] );
}
/**
* @dataProvider provideValidate
* @param mixed $value Value for getCallbacks()
* @param mixed|ValidationException $expect Expected result from TypeDef::validate().
* If a ValidationException, it is expected that a ValidationException
* with matching failure code and data will be thrown. Otherwise, the return value must be equal.
* @param array $settings Settings array.
* @param array $options Options array
* @param array[] $expectConds Expected conditions reported. Each array is
* `[ $ex->getFailureCode() ] + $ex->getFailureData()`.
*/
public function testValidate(
$value, $expect, array $settings = [], array $options = [], array $expectConds = []
) {
$callbacks = new SimpleCallbacks( [ 'test' => $value ] );
$typeDef = new TagsDef(
$callbacks,
$this->getServiceContainer()->getChangeTagsStore()
);
$settings = $typeDef->normalizeSettings( $settings );
if ( $expect instanceof ValidationException ) {
try {
$v = $typeDef->getValue( 'test', $settings, $options );
$typeDef->validate( 'test', $v, $settings, $options );
$this->fail( 'Expected exception not thrown' );
} catch ( ValidationException $ex ) {
$this->assertSame(
$expect->getFailureMessage()->getCode(),
$ex->getFailureMessage()->getCode()
);
$this->assertSame(
$expect->getFailureMessage()->getData(),
$ex->getFailureMessage()->getData()
);
}
} else {
$v = $typeDef->getValue( 'test', $settings, $options );
$this->assertEquals( $expect, $typeDef->validate( 'test', $v, $settings, $options ) );
}
$conditions = [];
foreach ( $callbacks->getRecordedConditions() as $c ) {
$conditions[] = [ 'code' => $c['message']->getCode(), 'data' => $c['message']->getData() ];
}
$this->assertSame( $expectConds, $conditions );
}
public static function provideValidate() {
$settings = [
ParamValidator::PARAM_TYPE => 'tags',
ParamValidator::PARAM_ISMULTI => true,
];
$valuesList = [ 'values-list' => [ 'tag1', 'doesnotexist', 'doesnotexist2' ] ];
return [
'Basic' => [ 'tag1', [ 'tag1' ] ],
'Bad tag' => [
'doesnotexist',
new ValidationException(
DataMessageValue::new( 'paramvalidator-badtags', [], 'badtags', [
'disallowedtags' => [ 'doesnotexist' ],
] ),
'test', 'doesnotexist', []
),
],
'Multi' => [ 'tag1', 'tag1', $settings, [ 'values-list' => [ 'tag1', 'tag2' ] ] ],
'Multi with bad tag (but not the tag)' => [
'tag1', 'tag1', $settings, $valuesList
],
'Multi with bad tag' => [
'doesnotexist',
new ValidationException(
DataMessageValue::new( 'paramvalidator-badtags', [], 'badtags', [
'disallowedtags' => [ 'doesnotexist', 'doesnotexist2' ],
] ),
'test', 'doesnotexist', $settings
),
$settings, $valuesList
],
];
}
public function testGetEnumValues() {
$explicitlyDefinedTags = [ 'foo', 'bar', 'baz' ];
$changeTagsStore = $this->createNoOpMock(
ChangeTagsStore::class,
[ 'listExplicitlyDefinedTags' ]
);
$changeTagsStore->method( 'listExplicitlyDefinedTags' )
->willReturn( $explicitlyDefinedTags );
$typeDef = new TagsDef( new SimpleCallbacks( [] ), $changeTagsStore );
$this->assertSame(
$explicitlyDefinedTags,
$typeDef->getEnumValues( 'test', [], [] )
);
}
}
TypeDef/TitleDefTest.php 0000666 00000007505 15133511337 0011172 0 ustar 00 <?php
namespace MediaWiki\ParamValidator\TypeDef;
use MediaWiki\MainConfigNames;
use MediaWiki\MediaWikiServices;
use MediaWiki\Title\Title;
use MediaWiki\Title\TitleValue;
use Wikimedia\ParamValidator\ParamValidator;
use Wikimedia\ParamValidator\SimpleCallbacks;
/**
* @covers \MediaWiki\ParamValidator\TypeDef\TitleDef
* @group Database
*/
class TitleDefTest extends TypeDefIntegrationTestCase {
protected function getInstance( SimpleCallbacks $callbacks, array $options ) {
$this->overrideConfigValue( MainConfigNames::LanguageCode, 'en' );
return new TitleDef(
$callbacks,
MediaWikiServices::getInstance()->getTitleFactory()
);
}
/**
* @inheritDoc
* @dataProvider provideValidate
*/
public function testValidate(
$value, $expect, array $settings = [], array $options = [], array $expectConds = []
) {
if ( $this->dataName() === 'must exist (success)' ) {
$status = $this->editPage( Title::makeTitle( NS_MAIN, 'Exists' ), 'exists' );
$this->assertTrue( $status->isOK() );
}
parent::testValidate( $value, $expect, $settings, $options, $expectConds );
}
public function provideValidate() {
return [
'plain' => [
'value' => 'Foo',
'expect' => 'Foo',
'settings' => [],
],
'normalization' => [
'value' => 'foo_bar',
'expect' => 'Foo bar',
'settings' => [],
],
'bad title' => [
'value' => '<script>',
'expect' => $this->getValidationException( 'badtitle', '<script>' ),
'settings' => [],
],
'as object' => [
'value' => 'Foo',
'expect' => new TitleValue( NS_MAIN, 'Foo' ),
'settings' => [ TitleDef::PARAM_RETURN_OBJECT => true ],
],
'as object, with namespace' => [
'value' => 'User:Foo',
'expect' => new TitleValue( NS_USER, 'Foo' ),
'settings' => [ TitleDef::PARAM_RETURN_OBJECT => true ],
],
'object normalization' => [
'value' => 'foo_bar',
'expect' => new TitleValue( NS_MAIN, 'Foo bar' ),
'settings' => [ TitleDef::PARAM_RETURN_OBJECT => true ],
],
'must exist (success)' => [
'value' => 'Exists',
'expect' => 'Exists',
'settings' => [ TitleDef::PARAM_MUST_EXIST => true ],
],
'must exist (failure)' => [
'value' => 'does not exist',
'expect' => $this->getValidationException( 'missingtitle', 'does not exist',
[ TitleDef::PARAM_MUST_EXIST => true ] ),
'settings' => [ TitleDef::PARAM_MUST_EXIST => true ],
],
];
}
public function provideStringifyValue() {
return [
// Underscore-to-space conversion not happening here but later in validate().
'String' => [ 'User:John_Doe', 'User:John_Doe' ],
'TitleValue' => [ new TitleValue( NS_USER, 'John_Doe' ), 'User:John Doe' ],
'Title' => [ Title::makeTitle( NS_USER, 'John_Doe' ), 'User:John Doe' ],
];
}
public function provideCheckSettings() {
// checkSettings() is itself used in tests. Testing it is a waste of time,
// just provide the minimum required.
return [
'Basic test' => [ [], self::STDRET, array_merge_recursive( self::STDRET, [
'allowedKeys' => [ TitleDef::PARAM_MUST_EXIST, TitleDef::PARAM_RETURN_OBJECT ],
] ) ],
];
}
public function provideGetInfo() {
return [
'no mustExist' => [
'settings' => [],
'expectParamInfo' => [ 'mustExist' => false ],
'expectHelpInfo' => [
ParamValidator::PARAM_TYPE =>
'<message key="paramvalidator-help-type-title"></message>',
TitleDef::PARAM_MUST_EXIST =>
'<message key="paramvalidator-help-type-title-no-must-exist"></message>'
],
],
'mustExist' => [
'settings' => [ TitleDef::PARAM_MUST_EXIST => true ],
'expectParamInfo' => [ 'mustExist' => true ],
'expectHelpInfo' => [
ParamValidator::PARAM_TYPE =>
'<message key="paramvalidator-help-type-title"></message>',
TitleDef::PARAM_MUST_EXIST =>
'<message key="paramvalidator-help-type-title-must-exist"></message>'
],
],
];
}
}
TypeDef/TypeDefIntegrationTestCase.php 0000666 00000000631 15133511337 0014023 0 ustar 00 <?php
namespace MediaWiki\ParamValidator\TypeDef;
use MediaWikiIntegrationTestCase;
use Wikimedia\ParamValidator\TypeDef\TypeDefTestCaseTrait;
abstract class TypeDefIntegrationTestCase extends MediaWikiIntegrationTestCase {
use TypeDefTestCaseTrait;
/** Standard "$ret" array for provideCheckSettings */
protected const STDRET =
[ 'issues' => [ 'X' ], 'allowedKeys' => [ 'Y' ], 'messages' => [] ];
}
TypeDef/UserDefTest.php 0000666 00000035523 15133511761 0011031 0 ustar 00 <?php
namespace MediaWiki\ParamValidator\TypeDef;
use MediaWiki\HookContainer\HookContainer;
use MediaWiki\Tests\Unit\DummyServicesTrait;
use MediaWiki\User\UserIdentity;
use MediaWiki\User\UserIdentityLookup;
use MediaWiki\User\UserIdentityValue;
use Wikimedia\Message\DataMessageValue;
use Wikimedia\ParamValidator\ParamValidator;
use Wikimedia\ParamValidator\SimpleCallbacks;
use Wikimedia\ParamValidator\ValidationException;
/**
* @covers \MediaWiki\ParamValidator\TypeDef\UserDef
*/
class UserDefTest extends TypeDefUnitTestCase {
use DummyServicesTrait;
protected function getInstance( SimpleCallbacks $callbacks, array $options ) {
// The UserIdentityLookup that we have knows about 5 users, with ids
// 1 through 5 and names starting with the first 5 letters of the alphabet:
$namesToIds = [
'Adam Smith' => 1,
'Becca' => 2,
'Charlie' => 3,
'Danny' => 4,
'Emma' => 5,
];
$userIdentityLookup = $this->createMock( UserIdentityLookup::class );
$userIdentityLookup->method( 'getUserIdentityByName' )->willReturnCallback(
static function ( $name, $flags ) use ( $namesToIds ) {
if ( isset( $namesToIds[$name] ) ) {
return new UserIdentityValue( $namesToIds[$name], $name );
}
return null;
}
);
$userIdentityLookup->method( 'getUserIdentityByUserId' )->willReturnCallback(
static function ( $id, $flags ) use ( $namesToIds ) {
$idsToNames = array_flip( $namesToIds );
if ( isset( $idsToNames[$id] ) ) {
return new UserIdentityValue( $id, $idsToNames[$id] );
}
return null;
}
);
// DummyServicesTrait will call $this->createHookContainer() if we didn't pass
// one, but that method is only available from MediaWikiTestCaseTrait - just
// create a simple mock that doesn't do anything, because we
// don't care about hooks here
$hookContainer = $this->createMock( HookContainer::class );
$hookContainer->method( 'run' )->willReturn( true );
// We can throw mock exceptions because the UserDef code doesn't care about
// the messages in the exceptions, just if they are thrown
$titleParser = $this->getDummyTitleParser( [
'validInterwikis' => [ 'interwiki' ],
'throwMockExceptions' => true,
'hookContainer' => $hookContainer, // for the NamespaceInfo
] );
$userNameUtils = $this->getDummyUserNameUtils( [
'titleParser' => $titleParser, // don't create a new one
'hookContainer' => $hookContainer,
] );
return new UserDef(
$callbacks,
$userIdentityLookup,
$titleParser,
$userNameUtils
);
}
public function provideValidate() {
// General tests of string inputs
$data = [
'Basic' => [ 'name', 'Adam Smith', 'Adam Smith' ],
'Normalized' => [ 'name', 'adam_Smith', 'Adam Smith' ],
'External' => [ 'interwiki', 'm>some_user', 'm>some_user' ],
'IPv4' => [ 'ip', '192.168.0.1', '192.168.0.1' ],
'IPv4, normalized' => [ 'ip', '192.168.000.001', '192.168.0.1' ],
'IPv6' => [ 'ip', '2001:DB8:0:0:0:0:0:0', '2001:DB8:0:0:0:0:0:0' ],
'IPv6, normalized' => [ 'ip', '2001:0db8::', '2001:DB8:0:0:0:0:0:0' ],
'IPv6, with leading ::' => [ 'ip', '::1', '0:0:0:0:0:0:0:1' ],
'IPv4 range' => [ 'cidr', '192.168.000.000/16', '192.168.0.0/16' ],
'IPv6 range' => [ 'cidr', '2001:0DB8::/64', '2001:DB8:0:0:0:0:0:0/64' ],
'Usemod IP' => [ 'ip', '192.168.0.xxx', '192.168.0.xxx' ],
'Bogus IP' => [ '', '192.168.0.256', null ],
'Bogus Usemod IP' => [ '', '192.268.0.xxx', null ],
'Usemod IP as range' => [ '', '192.168.0.xxx/16', null ],
'Bad username' => [ '', '[[Foo]]', null ],
'No namespaces' => [ '', 'Talk:Foo', null ],
'No namespaces (2)' => [ '', 'Help:Foo', null ],
'No namespaces (except User is ok)' => [ 'name', 'User:Adam_Smith', 'Adam Smith' ],
'No namespaces (except User is ok) (IPv6)' => [ 'ip', 'User:::1', '0:0:0:0:0:0:0:1' ],
'No interwiki prefixes' => [ '', 'interwiki:Foo', null ],
'No fragment in IP' => [ '', '192.168.0.256#', null ],
];
foreach ( $data as $key => [ $type, $input, $expect ] ) {
$ex = new ValidationException(
DataMessageValue::new( 'paramvalidator-baduser', [], 'baduser' ),
'test', $input, []
);
if ( $type === '' ) {
yield $key => [ $input, $ex ];
continue;
}
yield $key => [ $input, $expect ];
yield "$key, only '$type' allowed" => [
$input,
$expect,
[ UserDef::PARAM_ALLOWED_USER_TYPES => [ $type ] ],
];
$types = array_diff( [ 'name', 'ip', 'cidr', 'interwiki' ], [ $type ] );
yield "$key, without '$type' allowed" => [
$input,
$ex,
[ UserDef::PARAM_ALLOWED_USER_TYPES => $types ],
];
if ( $type === 'ip'
|| $type === 'interwiki'
|| $type === 'cidr'
) {
// For all of these the UserIdentity returned will be a
// UserIdentityValue object since the name and id are both
// known (id is 0 for all)
$obj = UserIdentityValue::newAnonymous( $expect );
} else {
// Creating from name, we are only testing for "Adam Smith"
// so the id will be 1
$obj = new UserIdentityValue( 1, $expect );
}
yield "$key, returning object" => [ $input, $obj, [ UserDef::PARAM_RETURN_OBJECT => true ] ];
}
// Test input by user ID
// Since this user isn't in our mock UserIdentityLookup, the name is "Unknown user"
// and the id is switched to just 0. We cover the case of existing ids in
// testProcessUser()
$input = '#1234';
$ex = new ValidationException(
DataMessageValue::new( 'paramvalidator-baduser', [], 'baduser' ),
'test', $input, []
);
yield 'User ID' => [ $input, $ex, [ UserDef::PARAM_RETURN_OBJECT => true ] ];
yield 'User ID, with \'id\' allowed, returning object' => [
$input,
new UserIdentityValue( 0, "Unknown user" ),
[ UserDef::PARAM_ALLOWED_USER_TYPES => [ 'id' ], UserDef::PARAM_RETURN_OBJECT => true ],
];
// Tests for T232672 (consistent treatment of whitespace and BIDI characters)
$data = [
'name' => [ 'Emma', [ 1 ], 'Emma' ],
'interwiki' => [ 'm>some_user', [ 1, 2, 6 ], null ],
'ip (v4)' => [ '192.168.0.1', [ 1, 3, 4 ], '192.168.0.1' ],
'ip (v6)' => [ '2001:DB8:0:0:0:0:0:0', [ 2, 5, 6 ], '2001:DB8:0:0:0:0:0:0' ],
'ip (v6, colons)' => [ '::1', [ 1, 2 ], '0:0:0:0:0:0:0:1' ],
'cidr (v4)' => [ '192.168.0.0/16', [ 1, 3, 4, 11, 12, 13 ], '192.168.0.0/16' ],
'cidr (v6)' => [ '2001:db8::/64', [ 2, 5, 6, 20, 21, 22 ], '2001:DB8:0:0:0:0:0:0/64' ],
];
foreach ( $data as $key => [ $name, $positions, $expect ] ) {
$input = " $name ";
yield "T232672: leading/trailing whitespace for $key" => [ $input, $expect ?? $input ];
$input = "_{$name}_";
yield "T232672: leading/trailing underscores for $key" => [ $input, $expect ?? $input ];
$positions = array_merge( [ 0, strlen( $name ) ], $positions );
foreach ( $positions as $i ) {
$input = substr_replace( $name, "\u{200E}", $i, 0 );
yield "T232672: U+200E at position $i for $key" => [ $input, $expect ?? $input ];
}
}
}
public function provideNormalizeSettings() {
return [
'Basic test' => [
[ 'param-foo' => 'bar' ],
[
'param-foo' => 'bar',
UserDef::PARAM_ALLOWED_USER_TYPES => [ 'name', 'ip', 'cidr', 'interwiki' ],
],
],
'Types not overridden' => [
[
'param-foo' => 'bar',
UserDef::PARAM_ALLOWED_USER_TYPES => [ 'name', 'id' ],
],
[
'param-foo' => 'bar',
UserDef::PARAM_ALLOWED_USER_TYPES => [ 'name', 'id' ],
],
],
];
}
public function provideCheckSettings() {
$keys = [ 'Y', UserDef::PARAM_ALLOWED_USER_TYPES, UserDef::PARAM_RETURN_OBJECT ];
$ismultiIssue = 'Multi-valued user-type parameters with PARAM_RETURN_OBJECT or allowing IDs '
. 'should set low values (<= 10) for PARAM_ISMULTI_LIMIT1 and PARAM_ISMULTI_LIMIT2.'
. ' (Note that "<= 10" is arbitrary. If something hits this, we can investigate a real limit '
. 'once we have a real use case to look at.)';
return [
'Basic test' => [
[],
self::STDRET,
[
'issues' => [ 'X' ],
'allowedKeys' => $keys,
'messages' => [],
],
],
'Test with everything' => [
[
UserDef::PARAM_ALLOWED_USER_TYPES => [ 'name' ],
UserDef::PARAM_RETURN_OBJECT => true,
],
self::STDRET,
[
'issues' => [ 'X' ],
'allowedKeys' => $keys,
'messages' => [],
],
],
'Bad types' => [
[
UserDef::PARAM_ALLOWED_USER_TYPES => 'name',
UserDef::PARAM_RETURN_OBJECT => 1,
],
self::STDRET,
[
'issues' => [
'X',
UserDef::PARAM_RETURN_OBJECT => 'PARAM_RETURN_OBJECT must be boolean, got integer',
UserDef::PARAM_ALLOWED_USER_TYPES => 'PARAM_ALLOWED_USER_TYPES must be an array, got string',
],
'allowedKeys' => $keys,
'messages' => [],
],
],
'PARAM_ALLOWED_USER_TYPES cannot be empty' => [
[
UserDef::PARAM_ALLOWED_USER_TYPES => [],
],
self::STDRET,
[
'issues' => [
'X',
UserDef::PARAM_ALLOWED_USER_TYPES => 'PARAM_ALLOWED_USER_TYPES cannot be empty',
],
'allowedKeys' => $keys,
'messages' => [],
],
],
'PARAM_ALLOWED_USER_TYPES invalid values' => [
[
UserDef::PARAM_ALLOWED_USER_TYPES => [ 'name', 'id', 'ssn', 'Q-number' ],
],
self::STDRET,
[
'issues' => [
'X',
UserDef::PARAM_ALLOWED_USER_TYPES
=> 'PARAM_ALLOWED_USER_TYPES contains invalid values: ssn, Q-number',
],
'allowedKeys' => $keys,
'messages' => [],
],
],
'ISMULTI generally ok' => [
[
ParamValidator::PARAM_ISMULTI => true,
],
self::STDRET,
[
'issues' => [ 'X' ],
'allowedKeys' => $keys,
'messages' => [],
],
],
'ISMULTI with ID not ok (1)' => [
[
ParamValidator::PARAM_ISMULTI => true,
UserDef::PARAM_ALLOWED_USER_TYPES => [ 'id' ],
],
self::STDRET,
[
'issues' => [ 'X', $ismultiIssue ],
'allowedKeys' => $keys,
'messages' => [],
],
],
'ISMULTI with ID not ok (2)' => [
[
ParamValidator::PARAM_ISMULTI => true,
ParamValidator::PARAM_ISMULTI_LIMIT1 => 10,
ParamValidator::PARAM_ISMULTI_LIMIT2 => 11,
UserDef::PARAM_ALLOWED_USER_TYPES => [ 'id' ],
],
self::STDRET,
[
'issues' => [ 'X', $ismultiIssue ],
'allowedKeys' => $keys,
'messages' => [],
],
],
'ISMULTI with ID ok with low limits' => [
[
ParamValidator::PARAM_ISMULTI => true,
ParamValidator::PARAM_ISMULTI_LIMIT1 => 10,
ParamValidator::PARAM_ISMULTI_LIMIT2 => 10,
UserDef::PARAM_ALLOWED_USER_TYPES => [ 'id' ],
],
self::STDRET,
[
'issues' => [ 'X' ],
'allowedKeys' => $keys,
'messages' => [],
],
],
'ISMULTI with RETURN_OBJECT also not ok' => [
[
ParamValidator::PARAM_ISMULTI => true,
UserDef::PARAM_RETURN_OBJECT => true,
],
self::STDRET,
[
'issues' => [ 'X', $ismultiIssue ],
'allowedKeys' => $keys,
'messages' => [],
],
],
'ISMULTI with RETURN_OBJECT also ok with low limits' => [
[
ParamValidator::PARAM_ISMULTI => true,
ParamValidator::PARAM_ISMULTI_LIMIT1 => 10,
ParamValidator::PARAM_ISMULTI_LIMIT2 => 10,
UserDef::PARAM_RETURN_OBJECT => true,
],
self::STDRET,
[
'issues' => [ 'X' ],
'allowedKeys' => $keys,
'messages' => [],
],
],
];
}
public function provideGetInfo() {
return [
'Basic test' => [
[],
[
'subtypes' => [ 'name', 'ip', 'cidr', 'interwiki' ],
],
[
ParamValidator::PARAM_TYPE => '<message key="paramvalidator-help-type-user"><text>1</text><list listType="text"><text><message key="paramvalidator-help-type-user-subtype-name"></message></text><text><message key="paramvalidator-help-type-user-subtype-ip"></message></text><text><message key="paramvalidator-help-type-user-subtype-cidr"></message></text><text><message key="paramvalidator-help-type-user-subtype-interwiki"></message></text></list><num>4</num></message>',
],
],
'Specific types' => [
[
ParamValidator::PARAM_ISMULTI => true,
UserDef::PARAM_ALLOWED_USER_TYPES => [ 'name', 'id' ],
UserDef::PARAM_RETURN_OBJECT => true,
],
[
'subtypes' => [ 'name', 'id' ],
],
[
ParamValidator::PARAM_TYPE => '<message key="paramvalidator-help-type-user"><text>2</text><list listType="text"><text><message key="paramvalidator-help-type-user-subtype-name"></message></text><text><message key="paramvalidator-help-type-user-subtype-id"></message></text></list><num>2</num></message>',
],
],
];
}
private function assertUserIdentity( $actual, $expectId, $expectName ) {
// Can't use UserIdentity::equals() since that only checks the name
$this->assertInstanceOf( UserIdentity::class, $actual );
$this->assertSame( $expectId, $actual->getId() );
$this->assertSame( $expectName, $actual->getName() );
}
/**
* @dataProvider provideMissingId
*/
public function testProcessUser_missingId( $missingId ) {
// User created by id, does not exist, falls back to "Unknown user"
// See our mock UserIdentityLookup for which ids and names exist
$userDef = $this->getInstance( new SimpleCallbacks( [] ), [] );
$res = $userDef->validate(
'', // $name, unused here
"#$missingId",
[
UserDef::PARAM_ALLOWED_USER_TYPES => [ 'id' ],
UserDef::PARAM_RETURN_OBJECT => true,
], // $settings
[] // $options, unused here
);
// Even though we created with $missingId, the resulting UserIdentity has
// an id of 0 because the user does not exist
$this->assertUserIdentity( $res, 0, "Unknown user" );
}
public static function provideMissingId() {
yield "0 no longer matches request ip" => [ 0 ];
yield "Id with no user" => [ 6 ];
}
public function testProcessUser_validId() {
// User created by id, does exist
// See our mock UserIdentityLookup for which ids and names exist
$userDef = $this->getInstance( new SimpleCallbacks( [] ), [] );
$res = $userDef->validate(
'', // $name, unused here
"#5",
[
UserDef::PARAM_ALLOWED_USER_TYPES => [ 'id' ],
UserDef::PARAM_RETURN_OBJECT => true,
], // $settings
[] // $options, unused here
);
$this->assertUserIdentity( $res, 5, 'Emma' );
}
public function testProcessUser_missingName() {
// Created by name, does not exist
// Already in the canonical form
// See our mock UserIdentityLookup for which ids and names exist
$userName = 'UserDefTest-processUser-missing';
$userDef = $this->getInstance( new SimpleCallbacks( [] ), [] );
$res = $userDef->validate(
'', // $name, unused here
$userName,
[
UserDef::PARAM_ALLOWED_USER_TYPES => [ 'name' ],
UserDef::PARAM_RETURN_OBJECT => true,
], // $settings
[] // $options, unused here
);
$this->assertUserIdentity( $res, 0, $userName );
}
public function testProcessUser_0() {
$userName = '0';
$userDef = $this->getInstance( new SimpleCallbacks( [] ), [] );
$res = $userDef->validate(
'', // $name, unused here
$userName,
[
UserDef::PARAM_ALLOWED_USER_TYPES => [ 'name' ],
UserDef::PARAM_RETURN_OBJECT => true,
], // $settings
[] // $options, unused here
);
$this->assertUserIdentity( $res, 0, $userName );
}
}
TypeDef/NamespaceDefTest.php 0000666 00000020661 15133511761 0012004 0 ustar 00 <?php
namespace MediaWiki\ParamValidator\TypeDef;
use ApiResult;
use MediaWiki\HookContainer\HookContainer;
use MediaWiki\Tests\Unit\DummyServicesTrait;
use Wikimedia\Message\DataMessageValue;
use Wikimedia\ParamValidator\ParamValidator;
use Wikimedia\ParamValidator\SimpleCallbacks;
use Wikimedia\ParamValidator\TypeDef\EnumDef;
use Wikimedia\ParamValidator\ValidationException;
/**
* @covers \MediaWiki\ParamValidator\TypeDef\NamespaceDef
*/
class NamespaceDefTest extends TypeDefUnitTestCase {
use DummyServicesTrait;
private function getNamespaceInfo() {
// DummyServicesTrait::getDummyNamespaceInfo() would call
// $this->createHookContainer() if we didn't pass one, but that
// method is only available from MediaWikiTestCaseTrait - just
// create a simple mock that doesn't do anything, because we
// don't care about hooks here
$hookContainer = $this->createMock( HookContainer::class );
$hookContainer->method( 'run' )->willReturn( true );
return $this->getDummyNamespaceInfo( [ 'hookContainer' => $hookContainer ] );
}
protected function getInstance( SimpleCallbacks $callbacks, array $options ) {
return new NamespaceDef(
$callbacks,
$this->getNamespaceInfo()
);
}
private function getNamespaces( $extra = [] ) {
$namespaces = array_merge(
$this->getNamespaceInfo()->getValidNamespaces(),
$extra
);
sort( $namespaces );
return $namespaces;
}
public function provideValidate() {
$settings = [
ParamValidator::PARAM_TYPE => 'namespace',
];
$extraSettings = [
ParamValidator::PARAM_TYPE => 'namespace',
NamespaceDef::PARAM_EXTRA_NAMESPACES => [ -5 ],
];
return [
'Basic' => [ '0', 0, $settings ],
'Bad namespace' => [
'x',
new ValidationException(
DataMessageValue::new( 'paramvalidator-badvalue', [], 'badvalue', [] ), 'test', 'x', $settings
),
$settings
],
'Unknown namespace' => [
'x',
new ValidationException(
DataMessageValue::new( 'paramvalidator-badvalue', [], 'badvalue', [] ), 'test', '-1', []
),
],
'Extra namespaces' => [ '-5', -5, $extraSettings ],
];
}
public function provideGetEnumValues() {
return [
'Basic test' => [
[ ParamValidator::PARAM_TYPE => 'namespace' ],
$this->getNamespaces(),
],
'Extra namespaces' => [
[
ParamValidator::PARAM_TYPE => 'namespace',
NamespaceDef::PARAM_EXTRA_NAMESPACES => [ NS_SPECIAL, NS_MEDIA ]
],
$this->getNamespaces( [ NS_SPECIAL, NS_MEDIA ] ),
],
];
}
public function provideNormalizeSettings() {
return [
'Basic test' => [ [], [] ],
'Add PARAM_ALL' => [
[ ParamValidator::PARAM_ISMULTI => true ],
[ ParamValidator::PARAM_ISMULTI => true, ParamValidator::PARAM_ALL => true ],
],
'Force PARAM_ALL' => [
[ ParamValidator::PARAM_ISMULTI => true, ParamValidator::PARAM_ALL => false ],
[ ParamValidator::PARAM_ISMULTI => true, ParamValidator::PARAM_ALL => true ],
],
'Force PARAM_ALL (2)' => [
[ ParamValidator::PARAM_ISMULTI => true, ParamValidator::PARAM_ALL => 'all' ],
[ ParamValidator::PARAM_ISMULTI => true, ParamValidator::PARAM_ALL => true ],
],
];
}
public function provideCheckSettings() {
$keys = [ 'Y', EnumDef::PARAM_DEPRECATED_VALUES, NamespaceDef::PARAM_EXTRA_NAMESPACES ];
return [
'Basic test' => [
[],
self::STDRET,
[
'issues' => [ 'X' ],
'allowedKeys' => $keys,
'messages' => [],
],
],
'Test with everything' => [
[
ParamValidator::PARAM_ISMULTI => true,
ParamValidator::PARAM_ALL => true,
NamespaceDef::PARAM_EXTRA_NAMESPACES => [ -1, -2 ],
],
self::STDRET,
[
'issues' => [ 'X' ],
'allowedKeys' => $keys,
'messages' => [],
],
],
'PARAM_ALL cannot be false' => [
[
ParamValidator::PARAM_ISMULTI => true,
ParamValidator::PARAM_ALL => false,
],
self::STDRET,
[
'issues' => [
'X',
ParamValidator::PARAM_ALL
=> 'PARAM_ALL cannot be false or a string for namespace-type parameters',
],
'allowedKeys' => $keys,
'messages' => [],
],
],
'PARAM_ALL cannot be a string' => [
[
ParamValidator::PARAM_ISMULTI => true,
ParamValidator::PARAM_ALL => 'all',
],
self::STDRET,
[
'issues' => [
'X',
ParamValidator::PARAM_ALL
=> 'PARAM_ALL cannot be false or a string for namespace-type parameters',
],
'allowedKeys' => $keys,
'messages' => [],
],
],
'PARAM_ALL ignored without PARAM_ISMULTI' => [
[
ParamValidator::PARAM_ALL => 'all',
],
self::STDRET,
[
'issues' => [ 'X' ],
'allowedKeys' => $keys,
'messages' => [],
],
],
'PARAM_ALL cannot be a string, but another PARAM_ALL issue was already logged' => [
[
ParamValidator::PARAM_ISMULTI => true,
ParamValidator::PARAM_ALL => 'all',
],
[
'issues' => [ ParamValidator::PARAM_ALL => 'XXX' ],
'allowedKeys' => [ 'Y' ],
'messages' => [],
],
[
'issues' => [ ParamValidator::PARAM_ALL => 'XXX' ],
'allowedKeys' => $keys,
'messages' => [],
],
],
'Bad type for PARAM_EXTRA_NAMESPACES' => [
[
NamespaceDef::PARAM_EXTRA_NAMESPACES => -1,
],
self::STDRET,
[
'issues' => [
'X',
NamespaceDef::PARAM_EXTRA_NAMESPACES
=> 'PARAM_EXTRA_NAMESPACES must be an integer[], got integer'
],
'allowedKeys' => $keys,
'messages' => [],
],
],
'Empty array for PARAM_EXTRA_NAMESPACES ok' => [
[
NamespaceDef::PARAM_EXTRA_NAMESPACES => [],
],
self::STDRET,
[
'issues' => [ 'X' ],
'allowedKeys' => $keys,
'messages' => [],
],
],
'Bad value types for PARAM_EXTRA_NAMESPACES' => [
[
NamespaceDef::PARAM_EXTRA_NAMESPACES => [ '-1' ],
],
self::STDRET,
[
'issues' => [
'X',
NamespaceDef::PARAM_EXTRA_NAMESPACES
=> 'PARAM_EXTRA_NAMESPACES must be an integer[], got string[]'
],
'allowedKeys' => $keys,
'messages' => [],
],
],
'Bad value types for PARAM_EXTRA_NAMESPACES (2)' => [
[
NamespaceDef::PARAM_EXTRA_NAMESPACES => [ 0, '-1', '-2' ],
],
self::STDRET,
[
'issues' => [
'X',
NamespaceDef::PARAM_EXTRA_NAMESPACES
=> 'PARAM_EXTRA_NAMESPACES must be an integer[], got (integer|string)[]'
],
'allowedKeys' => $keys,
'messages' => [],
],
],
];
}
public function provideStringifyValue() {
return [
'Basic test' => [ 123, '123' ],
'Array' => [ [ 1, 2, 3 ], '1|2|3' ],
];
}
public function provideGetInfo() {
yield 'Basic test' => [
[],
[ 'type' => 'namespace' ],
[
ParamValidator::PARAM_TYPE => '<message key="paramvalidator-help-type-enum"><text>1</text><list listType="comma"><text>0</text><text>1</text><text>2</text><text>3</text><text>4</text><text>5</text><text>6</text><text>7</text><text>8</text><text>9</text><text>10</text><text>11</text><text>12</text><text>13</text><text>14</text><text>15</text></list><num>16</num></message>',
ParamValidator::PARAM_ISMULTI => null,
],
];
yield 'Extra namespaces' => [
[
ParamValidator::PARAM_DEFAULT => 0,
NamespaceDef::PARAM_EXTRA_NAMESPACES => [ NS_SPECIAL, NS_MEDIA ]
],
[ 'type' => 'namespace', 'extranamespaces' => [ NS_SPECIAL, NS_MEDIA ] ],
[
ParamValidator::PARAM_TYPE => '<message key="paramvalidator-help-type-enum"><text>1</text><list listType="comma"><text>-1</text><text>-2</text><text>0</text><text>1</text><text>2</text><text>3</text><text>4</text><text>5</text><text>6</text><text>7</text><text>8</text><text>9</text><text>10</text><text>11</text><text>12</text><text>13</text><text>14</text><text>15</text></list><num>18</num></message>',
ParamValidator::PARAM_ISMULTI => null,
],
];
yield 'Extra namespaces, for Action API' => [
[ NamespaceDef::PARAM_EXTRA_NAMESPACES => [ NS_SPECIAL, NS_MEDIA ] ],
[
'type' => 'namespace',
'extranamespaces' => [
NS_SPECIAL, NS_MEDIA,
ApiResult::META_INDEXED_TAG_NAME => 'ns',
],
],
[
ParamValidator::PARAM_TYPE => '<message key="paramvalidator-help-type-enum"><text>1</text><list listType="comma"><text>-1</text><text>-2</text><text>0</text><text>1</text><text>2</text><text>3</text><text>4</text><text>5</text><text>6</text><text>7</text><text>8</text><text>9</text><text>10</text><text>11</text><text>12</text><text>13</text><text>14</text><text>15</text></list><num>18</num></message>',
ParamValidator::PARAM_ISMULTI => null,
],
[ 'module' => (object)[] ],
];
}
}
TypeDef/TypeDefUnitTestCase.php 0000666 00000000604 15133511761 0012460 0 ustar 00 <?php
namespace MediaWiki\ParamValidator\TypeDef;
use MediaWikiUnitTestCase;
use Wikimedia\ParamValidator\TypeDef\TypeDefTestCaseTrait;
abstract class TypeDefUnitTestCase extends MediaWikiUnitTestCase {
use TypeDefTestCaseTrait;
/** Standard "$ret" array for provideCheckSettings */
protected const STDRET =
[ 'issues' => [ 'X' ], 'allowedKeys' => [ 'Y' ], 'messages' => [] ];
}
TypeDefTest.php 0000666 00000011755 15133512335 0007473 0 ustar 00 <?php
namespace Wikimedia\ParamValidator;
use Wikimedia\Message\DataMessageValue;
use Wikimedia\TestingAccessWrapper;
/**
* @covers Wikimedia\ParamValidator\TypeDef
*/
class TypeDefTest extends \PHPUnit\Framework\TestCase {
public function testMisc() {
$typeDef = $this->getMockBuilder( TypeDef::class )
->setConstructorArgs( [ new SimpleCallbacks( [] ) ] )
->getMockForAbstractClass();
$this->assertSame( [ 'foobar' ], $typeDef->normalizeSettings( [ 'foobar' ] ) );
$ret = [ 'issues' => [], 'allowedKeys' => [], 'messages' => [] ];
$this->assertSame( $ret, $typeDef->checkSettings( 'foobar', [], [], $ret ) );
$this->assertNull( $typeDef->getEnumValues( 'foobar', [], [] ) );
$this->assertSame( '123', $typeDef->stringifyValue( 'foobar', 123, [], [] ) );
}
public function testGetValue() {
$options = [ (object)[] ];
$callbacks = $this->getMockBuilder( Callbacks::class )->getMockForAbstractClass();
$callbacks->expects( $this->once() )->method( 'getValue' )
->with(
$this->identicalTo( 'foobar' ),
$this->identicalTo( null ),
$this->identicalTo( $options )
)
->willReturn( 'zyx' );
$typeDef = $this->getMockBuilder( TypeDef::class )
->setConstructorArgs( [ $callbacks ] )
->getMockForAbstractClass();
$this->assertSame(
'zyx',
$typeDef->getValue( 'foobar', [ ParamValidator::PARAM_DEFAULT => 'foo' ], $options )
);
}
public function testGetParamInfo() {
$typeDef = $this->getMockBuilder( TypeDef::class )
->setConstructorArgs( [ new SimpleCallbacks( [] ) ] )
->getMockForAbstractClass();
$this->assertSame( [], $typeDef->getParamInfo( 'foobar', [], [] ) );
}
public function testGetHelpInfo() {
$typeDef = $this->getMockBuilder( TypeDef::class )
->setConstructorArgs( [ new SimpleCallbacks( [] ) ] )
->getMockForAbstractClass();
$this->assertSame( [], $typeDef->getHelpInfo( 'foobar', [], [] ) );
}
/** @dataProvider provideFailureMessage */
public function testFailureMessage( $expect, $code, array $data = null, $suffix = null ) {
$typeDef = $this->getMockBuilder( TypeDef::class )
->setConstructorArgs( [ new SimpleCallbacks( [] ) ] )
->getMockForAbstractClass();
$ret = TestingAccessWrapper::newFromObject( $typeDef )->failureMessage( $code, $data, $suffix );
$this->assertInstanceOf( DataMessageValue::class, $ret );
$this->assertSame( $expect, $ret->dump() );
}
public static function provideFailureMessage() {
return [
'Basic' => [
'<datamessage key="paramvalidator-foobar" code="foobar"></datamessage>',
'foobar',
],
'With data' => [
'<datamessage key="paramvalidator-foobar" code="foobar"><data>{"x":123}</data></datamessage>',
'foobar', [ 'x' => 123 ]
],
'With suffix' => [
'<datamessage key="paramvalidator-foobar-baz" code="foobar"><data>[]</data></datamessage>',
'foobar', [], 'baz'
],
];
}
/** @dataProvider provideFailure */
public function testFailure_fatal(
$expect, $failure, $name, $value, array $settings, array $options
) {
$callbacks = new SimpleCallbacks( [] );
$typeDef = $this->getMockBuilder( TypeDef::class )
->setConstructorArgs( [ $callbacks ] )
->getMockForAbstractClass();
try {
TestingAccessWrapper::newFromObject( $typeDef )
->failure( $failure, $name, $value, $settings, $options );
$this->fail( 'Expected exception not thrown' );
} catch ( ValidationException $ex ) {
$this->assertSame( $expect, $ex->getFailureMessage()->dump() );
$this->assertSame( $name, $ex->getParamName() );
$this->assertSame( (string)$value, $ex->getParamValue() );
$this->assertSame( $settings, $ex->getSettings() );
}
$this->assertSame( [], $callbacks->getRecordedConditions() );
}
/** @dataProvider provideFailure */
public function testFailure_nonfatal(
$expect, $failure, $name, $value, array $settings, array $options
) {
$callbacks = new SimpleCallbacks( [] );
$typeDef = $this->getMockBuilder( TypeDef::class )
->setConstructorArgs( [ $callbacks ] )
->getMockForAbstractClass();
TestingAccessWrapper::newFromObject( $typeDef )
->failure( $failure, $name, $value, $settings, $options, false );
$conds = $callbacks->getRecordedConditions();
$this->assertCount( 1, $conds );
$conds[0]['message'] = $conds[0]['message']->dump();
$this->assertSame( [
'message' => $expect,
'name' => $name,
'value' => (string)$value,
'settings' => $settings,
], $conds[0] );
}
public static function provideFailure() {
return [
'Basic' => [
'<datamessage key="paramvalidator-foobar" code="foobar"><params><plaintext>test</plaintext><plaintext>1234</plaintext></params></datamessage>',
'foobar', 'test', 1234, [], []
],
'DataMessageValue' => [
'<datamessage key="XXX-msg" code="foobar"><params><plaintext>test</plaintext><plaintext>XXX</plaintext><text>a</text><text>b</text><plaintext>pt</plaintext></params><data>{"data":"!!!"}</data></datamessage>',
DataMessageValue::new( 'XXX-msg', [ 'a', 'b' ], 'foobar', [ 'data' => '!!!' ] )
->plaintextParams( 'pt' ),
'test', 'XXX', [], []
],
];
}
}
TypeDef/EnumDefTest.php 0000666 00000015231 15133512335 0011007 0 ustar 00 <?php
namespace Wikimedia\ParamValidator\TypeDef;
use Wikimedia\Message\DataMessageValue;
use Wikimedia\Message\MessageValue;
use Wikimedia\ParamValidator\ParamValidator;
use Wikimedia\ParamValidator\SimpleCallbacks;
use Wikimedia\ParamValidator\ValidationException;
/**
* @covers Wikimedia\ParamValidator\TypeDef\EnumDef
*/
class EnumDefTest extends TypeDefTestCase {
protected function getInstance( SimpleCallbacks $callbacks, array $options ) {
return new EnumDef( $callbacks, $options );
}
public function provideValidate() {
$settings = [
ParamValidator::PARAM_TYPE => [ 'a', 'b', 'c', 'd', 'e' ],
EnumDef::PARAM_DEPRECATED_VALUES => [
'b' => MessageValue::new( 'not-to-be', [ '??' ] ),
'c' => true,
'e' => DataMessageValue::new( 'xyz', [ '??' ], 'bogus', [ 'x' => 'y' ] ),
],
];
return [
'Basic' => [ 'a', 'a', $settings ],
'Deprecated' => [ 'c', 'c', $settings, [], [
[ 'code' => 'deprecated-value', 'data' => null ],
] ],
'Deprecated with message' => [
'b', 'b', $settings, [], [
[ 'code' => 'deprecated-value', 'data' => null ]
] ],
'Deprecated with data message' => [
'e', 'e', $settings, [], [
[ 'code' => 'deprecated-value', 'data' => [ 'x' => 'y' ] ]
] ],
'Deprecated, from default' => [
'c', 'c', $settings, [ 'is-default' => true ], []
],
'Bad value, non-multi' => [
'x',
new ValidationException(
DataMessageValue::new( 'paramvalidator-badvalue-enumnotmulti', [], 'badvalue', [] ),
'test', 'x', $settings
),
$settings,
],
'Bad value, non-multi but looks like it' => [
'x|y',
new ValidationException(
DataMessageValue::new( 'paramvalidator-badvalue-enumnotmulti', [], 'badvalue', [] ),
'test', 'x|y', $settings
),
$settings,
],
'Bad value, multi' => [
'x|y',
new ValidationException(
DataMessageValue::new( 'paramvalidator-badvalue-enummulti', [], 'badvalue', [] ),
'test', 'x|y', $settings + [ ParamValidator::PARAM_ISMULTI => true ]
),
$settings + [ ParamValidator::PARAM_ISMULTI => true ],
[ 'values-list' => [ 'x|y' ] ],
],
];
}
public function provideCheckSettings() {
return [
'Basic test' => [
[
ParamValidator::PARAM_TYPE => [ 'a', 'b', 'c', 'd', 'e' ],
],
self::STDRET,
[
'issues' => [ 'X' ],
'allowedKeys' => [ 'Y', EnumDef::PARAM_DEPRECATED_VALUES ],
'messages' => [],
],
],
'Bad type for PARAM_DEPRECATED_VALUES' => [
[
ParamValidator::PARAM_TYPE => [ 'a', 'b', 'c', 'd', 'e' ],
EnumDef::PARAM_DEPRECATED_VALUES => false,
],
self::STDRET,
[
'issues' => [
'X',
EnumDef::PARAM_DEPRECATED_VALUES => 'PARAM_DEPRECATED_VALUES must be an array, got boolean',
],
'allowedKeys' => [ 'Y', EnumDef::PARAM_DEPRECATED_VALUES ],
'messages' => [],
],
],
'PARAM_DEPRECATED_VALUES value errors' => [
[
ParamValidator::PARAM_TYPE => [ 'a', 'b', 'c', 'd', 'e', 'f', 'g', 0, '1' ],
EnumDef::PARAM_DEPRECATED_VALUES => [
'b' => null,
'c' => false,
'd' => true,
'e' => MessageValue::new( 'e' ),
'f' => 'f',
'g' => $this,
0 => true,
1 => true,
'x' => null,
],
],
self::STDRET,
[
'issues' => [
'X',
'Values in PARAM_DEPRECATED_VALUES must be null, true, or MessageValue, but value for "c" is false',
'Values in PARAM_DEPRECATED_VALUES must be null, true, or MessageValue, but value for "f" is string',
'Values in PARAM_DEPRECATED_VALUES must be null, true, or MessageValue, but value for "g" is ' . static::class,
// phpcs:enable
'PARAM_DEPRECATED_VALUES contains "x", which is not one of the enumerated values',
],
'allowedKeys' => [ 'Y', EnumDef::PARAM_DEPRECATED_VALUES ],
'messages' => [
MessageValue::new( 'e' ),
],
],
],
];
}
public function provideGetEnumValues() {
return [
'Basic test' => [
[ ParamValidator::PARAM_TYPE => [ 'a', 'b', 'c', 'd' ] ],
[ 'a', 'b', 'c', 'd' ],
],
];
}
public function provideStringifyValue() {
return [
'Basic test' => [ 123, '123' ],
'Array' => [ [ 1, 2, 3 ], '1|2|3' ],
'Array with pipes' => [ [ 1, 2, '3|4', 5 ], "\x1f1\x1f2\x1f3|4\x1f5" ],
];
}
public function provideGetInfo() {
return [
'Non-multi' => [
[
ParamValidator::PARAM_TYPE => [ 'a', 'b', 'c', 'd' ],
],
[
'type' => [ 'a', 'b', 'c', 'd' ],
],
[
ParamValidator::PARAM_TYPE => '<message key="paramvalidator-help-type-enum"><text>1</text><list listType="comma"><text>a</text><text>b</text><text>c</text><text>d</text></list><num>4</num></message>',
ParamValidator::PARAM_ISMULTI => null,
],
],
'Multi' => [
[
ParamValidator::PARAM_TYPE => [ 'a', 'b', 'c', 'd' ],
ParamValidator::PARAM_ISMULTI => true,
],
[
'type' => [ 'a', 'b', 'c', 'd' ],
],
[
ParamValidator::PARAM_TYPE => '<message key="paramvalidator-help-type-enum"><text>2</text><list listType="comma"><text>a</text><text>b</text><text>c</text><text>d</text></list><num>4</num></message>',
ParamValidator::PARAM_ISMULTI => null,
],
],
'Deprecated values' => [
[
ParamValidator::PARAM_TYPE => [ 'a', 'b', 'c', 'd' ],
EnumDef::PARAM_DEPRECATED_VALUES => [ 'b' => 'B', 'c' => false, 'x' => true ],
],
[
'type' => [ 'a', 'd', 'b', 'c' ],
'deprecatedvalues' => [ 'b', 'c' ],
],
[
ParamValidator::PARAM_TYPE => '<message key="paramvalidator-help-type-enum"><text>1</text><list listType="comma"><text>a</text><text>d</text><text>b</text><text>c</text></list><num>4</num></message>',
ParamValidator::PARAM_ISMULTI => null,
],
],
'Deprecated values are all not allowed values' => [
[
ParamValidator::PARAM_TYPE => [ 'a', 'b', 'c', 'd' ],
EnumDef::PARAM_DEPRECATED_VALUES => [ 'x' => true ],
],
[
'type' => [ 'a', 'b', 'c', 'd' ],
],
[
ParamValidator::PARAM_TYPE => '<message key="paramvalidator-help-type-enum"><text>1</text><list listType="comma"><text>a</text><text>b</text><text>c</text><text>d</text></list><num>4</num></message>',
ParamValidator::PARAM_ISMULTI => null,
],
],
'Empty-string is a value' => [
[
ParamValidator::PARAM_TYPE => [ '', 'a', 'b', 'c', 'd' ],
],
[
'type' => [ '', 'a', 'b', 'c', 'd' ],
],
[
ParamValidator::PARAM_TYPE => '<message key="paramvalidator-help-type-enum"><text>1</text><text><message key="paramvalidator-help-type-enum-can-be-empty"><list listType="comma"><text>a</text><text>b</text><text>c</text><text>d</text></list><num>4</num></message></text><num>5</num></message>',
ParamValidator::PARAM_ISMULTI => null,
],
],
];
}
}
TypeDef/UploadDefTest.php 0000666 00000016302 15133512335 0011327 0 ustar 00 <?php
namespace Wikimedia\ParamValidator\TypeDef;
use Wikimedia\Message\DataMessageValue;
use Wikimedia\ParamValidator\ParamValidator;
use Wikimedia\ParamValidator\SimpleCallbacks;
use Wikimedia\ParamValidator\Util\UploadedFile;
use Wikimedia\ParamValidator\ValidationException;
/**
* @covers Wikimedia\ParamValidator\TypeDef\UploadDef
*/
class UploadDefTest extends TypeDefTestCase {
protected function getCallbacks( $value, array $options ) {
if ( $value instanceof UploadedFile ) {
return new SimpleCallbacks( [], [ 'test' => $value ] );
} else {
return new SimpleCallbacks( [ 'test' => $value ] );
}
}
protected function getInstance( SimpleCallbacks $callbacks, array $options ) {
$ret = $this->getMockBuilder( UploadDef::class )
->setConstructorArgs( [ $callbacks ] )
->onlyMethods( [ 'getIniSize' ] )
->getMock();
$ret->method( 'getIniSize' )->willReturn( $options['inisize'] ?? 2 * 1024 * 1024 );
return $ret;
}
private function makeUpload( $err = UPLOAD_ERR_OK ) {
return new UploadedFile( [
'name' => 'example.txt',
'type' => 'text/plain',
'size' => 0,
'tmp_name' => '...',
'error' => $err,
] );
}
public function testGetNoFile() {
$typeDef = $this->getInstance(
$this->getCallbacks( $this->makeUpload( UPLOAD_ERR_NO_FILE ), [] ),
[]
);
$this->assertNull( $typeDef->getValue( 'test', [], [] ) );
$this->assertNull( $typeDef->getValue( 'nothing', [], [] ) );
}
public function provideValidate() {
$okFile = $this->makeUpload();
$iniFile = $this->makeUpload( UPLOAD_ERR_INI_SIZE );
$exIni = new ValidationException(
DataMessageValue::new( 'paramvalidator-badupload-inisize', [], 'badupload', [
'code' => 'inisize',
'size' => 2 * 1024 * 1024 * 1024,
] ),
'test', '', []
);
return [
'Valid upload' => [ $okFile, $okFile ],
'Not an upload' => [
'bar',
new ValidationException(
DataMessageValue::new( 'paramvalidator-badupload-notupload', [], 'badupload', [
'code' => 'notupload'
] ),
'test', 'bar', []
),
],
'Too big (bytes)' => [ $iniFile, $exIni, [], [ 'inisize' => 2 * 1024 * 1024 * 1024 ] ],
'Too big (k)' => [ $iniFile, $exIni, [], [ 'inisize' => ( 2 * 1024 * 1024 ) . 'k' ] ],
'Too big (K)' => [ $iniFile, $exIni, [], [ 'inisize' => ( 2 * 1024 * 1024 ) . 'K' ] ],
'Too big (m)' => [ $iniFile, $exIni, [], [ 'inisize' => ( 2 * 1024 ) . 'm' ] ],
'Too big (M)' => [ $iniFile, $exIni, [], [ 'inisize' => ( 2 * 1024 ) . 'M' ] ],
'Too big (g)' => [ $iniFile, $exIni, [], [ 'inisize' => '2g' ] ],
'Too big (G)' => [ $iniFile, $exIni, [], [ 'inisize' => '2G' ] ],
'Form size' => [
$this->makeUpload( UPLOAD_ERR_FORM_SIZE ),
new ValidationException(
DataMessageValue::new( 'paramvalidator-badupload-formsize', [], 'badupload', [
'code' => 'formsize',
] ),
'test', '', []
),
],
'Partial' => [
$this->makeUpload( UPLOAD_ERR_PARTIAL ),
new ValidationException(
DataMessageValue::new( 'paramvalidator-badupload-partial', [], 'badupload', [
'code' => 'partial',
] ),
'test', '', []
),
],
'No tmp' => [
$this->makeUpload( UPLOAD_ERR_NO_TMP_DIR ),
new ValidationException(
DataMessageValue::new( 'paramvalidator-badupload-notmpdir', [], 'badupload', [
'code' => 'notmpdir',
] ),
'test', '', []
),
],
'Can\'t write' => [
$this->makeUpload( UPLOAD_ERR_CANT_WRITE ),
new ValidationException(
DataMessageValue::new( 'paramvalidator-badupload-cantwrite', [], 'badupload', [
'code' => 'cantwrite',
] ),
'test', '', []
),
],
'Ext abort' => [
$this->makeUpload( UPLOAD_ERR_EXTENSION ),
new ValidationException(
DataMessageValue::new( 'paramvalidator-badupload-phpext', [], 'badupload', [
'code' => 'phpext',
] ),
'test', '', []
),
],
];
}
public function testValidate_badType() {
$callbacks = $this->getCallbacks( 'foo', [] );
$typeDef = $this->getInstance( $callbacks, [] );
$this->expectException( \InvalidArgumentException::class );
$this->expectExceptionMessage( '$value must be UploadedFileInterface, got string' );
$typeDef->validate( 'test', 'foo', [], [] );
}
public function testValidate_badType2() {
$callbacks = $this->getCallbacks( 'foo', [] );
$typeDef = $this->getInstance( $callbacks, [] );
$this->expectException( \InvalidArgumentException::class );
$this->expectExceptionMessage( '$value must be UploadedFileInterface, got NULL' );
$typeDef->validate( 'test', null, [], [] );
}
public function testValidate_unknownError() {
// -43 should be safe from ever being a valid UPLOAD_ERR_ constant
$callbacks = $this->getCallbacks( $this->makeUpload( -43 ), [] );
$typeDef = $this->getInstance( $callbacks, [] );
$value = $typeDef->getValue( 'test', [], [] );
$this->expectException( \UnexpectedValueException::class );
$this->expectExceptionMessage( 'Unrecognized PHP upload error value -43' );
$typeDef->validate( 'test', $value, [], [] );
}
public function testValidate_unknownError2() {
define( 'UPLOAD_ERR_UPLOADDEFTEST', -44 );
$callbacks = $this->getCallbacks( $this->makeUpload( UPLOAD_ERR_UPLOADDEFTEST ), [] );
$typeDef = $this->getInstance( $callbacks, [] );
$value = $typeDef->getValue( 'test', [], [] );
$this->expectException( \UnexpectedValueException::class );
$this->expectExceptionMessage(
'Unrecognized PHP upload error value -44 (UPLOAD_ERR_UPLOADDEFTEST?)'
);
$typeDef->validate( 'test', $value, [], [] );
}
public function provideCheckSettings() {
return [
'Basic test' => [
[],
self::STDRET,
self::STDRET,
],
'PARAM_ISMULTI not allowed' => [
[
ParamValidator::PARAM_ISMULTI => true,
],
self::STDRET,
[
'issues' => [
'X',
ParamValidator::PARAM_ISMULTI
=> 'PARAM_ISMULTI cannot be used for upload-type parameters',
],
'allowedKeys' => [ 'Y' ],
'messages' => [],
],
],
'PARAM_ISMULTI not allowed, but another ISMULTI issue was already logged' => [
[
ParamValidator::PARAM_ISMULTI => true,
],
[
'issues' => [
ParamValidator::PARAM_ISMULTI => 'XXX',
],
'allowedKeys' => [ 'Y' ],
'messages' => [],
],
[
'issues' => [
ParamValidator::PARAM_ISMULTI => 'XXX',
],
'allowedKeys' => [ 'Y' ],
'messages' => [],
],
],
'PARAM_DEFAULT can be null' => [
[ ParamValidator::PARAM_DEFAULT => null ],
self::STDRET,
self::STDRET,
],
'PARAM_DEFAULT is otherwise not allowed' => [
[
ParamValidator::PARAM_DEFAULT => true,
],
[
'issues' => [
'X',
ParamValidator::PARAM_DEFAULT => 'XXX',
],
'allowedKeys' => [ 'Y' ],
'messages' => [],
],
[
'issues' => [
'X',
ParamValidator::PARAM_DEFAULT => 'Cannot specify a default for upload-type parameters',
],
'allowedKeys' => [ 'Y' ],
'messages' => [],
],
],
];
}
public function provideStringifyValue() {
return [
'Yeah, right' => [ $this->makeUpload(), null ],
];
}
public function provideGetInfo() {
return [
'Basic test' => [
[],
[],
[
ParamValidator::PARAM_TYPE => '<message key="paramvalidator-help-type-upload"></message>',
],
],
];
}
}
TypeDef/TypeDefTestCase.php 0000666 00000000713 15133512335 0011617 0 ustar 00 <?php
namespace Wikimedia\ParamValidator\TypeDef;
/**
* Test case infrastructure for TypeDef subclasses
*
* Generally you'll only need to implement self::getInstance() and
* data providers methods.
*/
abstract class TypeDefTestCase extends \PHPUnit\Framework\TestCase {
use TypeDefTestCaseTrait;
/** Standard "$ret" array for provideCheckSettings */
protected const STDRET =
[ 'issues' => [ 'X' ], 'allowedKeys' => [ 'Y' ], 'messages' => [] ];
}
TypeDef/TimestampDefTest.php 0000666 00000015210 15133512335 0012043 0 ustar 00 <?php
namespace Wikimedia\ParamValidator\TypeDef;
use Wikimedia\Message\DataMessageValue;
use Wikimedia\ParamValidator\ParamValidator;
use Wikimedia\ParamValidator\SimpleCallbacks;
use Wikimedia\ParamValidator\ValidationException;
use Wikimedia\Timestamp\ConvertibleTimestamp;
/**
* @covers Wikimedia\ParamValidator\TypeDef\TimestampDef
*/
class TimestampDefTest extends TypeDefTestCase {
protected function getInstance( SimpleCallbacks $callbacks, array $options ) {
return new TimestampDef( $callbacks, $options );
}
/** @dataProvider provideConstructorOptions */
public function testConstructorOptions( array $options, $ok ): void {
if ( $ok ) {
$this->assertTrue( true ); // dummy
} else {
$this->expectException( \InvalidArgumentException::class );
}
$this->getInstance( new SimpleCallbacks( [] ), $options );
}
public static function provideConstructorOptions(): array {
return [
'Basic test' => [ [], true ],
'Default format ConvertibleTimestamp' => [ [ 'defaultFormat' => 'ConvertibleTimestamp' ], true ],
'Default format DateTime' => [ [ 'defaultFormat' => 'DateTime' ], true ],
'Default format TS_ISO_8601' => [ [ 'defaultFormat' => TS_ISO_8601 ], true ],
'Default format invalid (string)' => [ [ 'defaultFormat' => 'foobar' ], false ],
'Default format invalid (int)' => [ [ 'defaultFormat' => 1000 ], false ],
'Stringify format ConvertibleTimestamp' => [
[ 'stringifyFormat' => 'ConvertibleTimestamp' ], false
],
'Stringify format DateTime' => [ [ 'stringifyFormat' => 'DateTime' ], false ],
'Stringify format TS_ISO_8601' => [ [ 'stringifyFormat' => TS_ISO_8601 ], true ],
'Stringify format invalid (string)' => [ [ 'stringifyFormat' => 'foobar' ], false ],
'Stringify format invalid (int)' => [ [ 'stringifyFormat' => 1000 ], false ],
];
}
/** @dataProvider provideValidate */
public function testValidate(
$value, $expect, array $settings = [], array $options = [], array $expectConds = []
) {
ConvertibleTimestamp::setFakeTime( 1559764242 );
parent::testValidate( $value, $expect, $settings, $options, $expectConds );
}
public function provideValidate() {
$specific = new ConvertibleTimestamp( 1517630706 );
$specificMs = new ConvertibleTimestamp( 1517630706.999 );
$now = new ConvertibleTimestamp( 1559764242 );
$formatDT = [ TimestampDef::PARAM_TIMESTAMP_FORMAT => 'DateTime' ];
$formatMW = [ TimestampDef::PARAM_TIMESTAMP_FORMAT => TS_MW ];
return [
// We don't try to validate all formats supported by ConvertibleTimestamp, just
// some of the interesting ones.
'ISO format' => [ '2018-02-03T04:05:06Z', $specific ],
'ISO format with TZ' => [ '2018-02-03T00:05:06-04:00', $specific ],
'ISO format without punctuation' => [ '20180203T040506', $specific ],
'ISO format with ms' => [ '2018-02-03T04:05:06.999000Z', $specificMs ],
'ISO format with ms without punctuation' => [ '20180203T040506.999', $specificMs ],
'MW format' => [ '20180203040506', $specific ],
'Generic format' => [ '2018-02-03 04:05:06', $specific ],
'Generic format + GMT' => [ '2018-02-03 04:05:06 GMT', $specific ],
'Generic format + TZ +0100' => [ '2018-02-03 05:05:06+0100', $specific ],
'Generic format + TZ -01' => [ '2018-02-03 03:05:06-01', $specific ],
'Seconds-since-epoch format' => [ '1517630706', $specific ],
'Seconds-since-epoch format with ms' => [ '1517630706.9990', $specificMs ],
'Now' => [ 'now', $now ],
// Warnings
'Empty' => [ '', $now, [], [], [ [ 'code' => 'unclearnowtimestamp', 'data' => null ] ] ],
'Zero' => [ '0', $now, [], [], [ [ 'code' => 'unclearnowtimestamp', 'data' => null ] ] ],
// Error handling
'Bad value' => [
'bogus',
new ValidationException(
DataMessageValue::new( 'paramvalidator-badtimestamp', [], 'badtimestamp' ),
'test', 'bogus', []
),
],
// T272637
'Incomplete MW format' => [
'2014815210101',
new ValidationException(
DataMessageValue::new( 'paramvalidator-badtimestamp', [], 'badtimestamp' ),
'test', '2014815210101', []
),
$formatMW
],
// Formatting
'=> DateTime' => [ 'now', $now->timestamp, $formatDT ],
'=> TS_MW' => [ 'now', '20190605195042', $formatMW ],
'=> TS_MW as default' => [ 'now', '20190605195042', [], [ 'defaultFormat' => TS_MW ] ],
'=> TS_MW overriding default'
=> [ 'now', '20190605195042', $formatMW, [ 'defaultFormat' => TS_ISO_8601 ] ],
];
}
public function provideCheckSettings() {
$keys = [ 'Y', TimestampDef::PARAM_TIMESTAMP_FORMAT ];
return [
'Basic test' => [
[],
self::STDRET,
[
'issues' => [ 'X' ],
'allowedKeys' => $keys,
'messages' => [],
],
],
'Test with format ConvertibleTimestamp' => [
[ TimestampDef::PARAM_TIMESTAMP_FORMAT => 'ConvertibleTimestamp' ],
self::STDRET,
[
'issues' => [ 'X' ],
'allowedKeys' => $keys,
'messages' => [],
],
],
'Test with format DateTime' => [
[ TimestampDef::PARAM_TIMESTAMP_FORMAT => 'DateTime' ],
self::STDRET,
[
'issues' => [ 'X' ],
'allowedKeys' => $keys,
'messages' => [],
],
],
'Test with format TS_ISO_8601' => [
[ TimestampDef::PARAM_TIMESTAMP_FORMAT => TS_ISO_8601 ],
self::STDRET,
[
'issues' => [ 'X' ],
'allowedKeys' => $keys,
'messages' => [],
],
],
'Test with invalid format (string)' => [
[ TimestampDef::PARAM_TIMESTAMP_FORMAT => 'foobar' ],
self::STDRET,
[
'issues' => [
'X',
TimestampDef::PARAM_TIMESTAMP_FORMAT => 'Value for PARAM_TIMESTAMP_FORMAT is not valid',
],
'allowedKeys' => $keys,
'messages' => [],
],
],
'Test with invalid format (int)' => [
[ TimestampDef::PARAM_TIMESTAMP_FORMAT => 1000 ],
self::STDRET,
[
'issues' => [
'X',
TimestampDef::PARAM_TIMESTAMP_FORMAT => 'Value for PARAM_TIMESTAMP_FORMAT is not valid',
],
'allowedKeys' => $keys,
'messages' => [],
],
],
];
}
public function provideStringifyValue() {
$specific = new ConvertibleTimestamp( '20180203040506' );
return [
[ '20180203040506', '2018-02-03T04:05:06Z' ],
[ $specific, '2018-02-03T04:05:06Z' ],
[ $specific->timestamp, '2018-02-03T04:05:06Z' ],
[ $specific, '20180203040506', [], [ 'stringifyFormat' => TS_MW ] ],
];
}
public function provideGetInfo() {
return [
'Basic test' => [
[],
[],
[
ParamValidator::PARAM_TYPE => '<message key="paramvalidator-help-type-timestamp"><text>1</text></message>',
],
],
'Multi-valued' => [
[ ParamValidator::PARAM_ISMULTI => true ],
[],
[
ParamValidator::PARAM_TYPE => '<message key="paramvalidator-help-type-timestamp"><text>2</text></message>',
],
],
];
}
}
TypeDef/FloatDefTest.php 0000666 00000011436 15133512335 0011153 0 ustar 00 <?php
namespace Wikimedia\ParamValidator\TypeDef;
use Wikimedia\Message\DataMessageValue;
use Wikimedia\ParamValidator\ParamValidator;
use Wikimedia\ParamValidator\SimpleCallbacks;
use Wikimedia\ParamValidator\ValidationException;
/**
* @covers Wikimedia\ParamValidator\TypeDef\FloatDef
*/
class FloatDefTest extends TypeDefTestCase {
protected function getInstance( SimpleCallbacks $callbacks, array $options ) {
return new FloatDef( $callbacks, $options );
}
public function provideValidate() {
return [
[ '123', 123.0 ],
[ '123.4', 123.4 ],
[ '0.4', 0.4 ],
[ '.4', 0.4 ],
[ '+123', 123.0 ],
[ '+123.4', 123.4 ],
[ '+0.4', 0.4 ],
[ '+.4', 0.4 ],
[ '-123', -123.0 ],
[ '-123.4', -123.4 ],
[ '-.4', -0.4 ],
[ '-.4', -0.4 ],
[ '123e5', 12300000.0 ],
[ '123E5', 12300000.0 ],
[ '123.4e+5', 12340000.0 ],
[ '123E5', 12300000.0 ],
[ '-123.4e-5', -0.001234 ],
[ '.4E-5', 0.000004 ],
[ '0', 0 ],
[ '000000', 0 ],
[ '0000.0000', 0 ],
[ '000001.0002000000', 1.0002 ],
[ '1e0', 1 ],
[ '1e-0000', 1 ],
[ '1e+00010', 1e10 ],
'Weird, but ok' => [ '-0', 0 ],
'Underflow is ok' => [ '1e-9999', 0 ],
'Empty decimal part' => [ '1.', new ValidationException(
DataMessageValue::new( 'paramvalidator-badfloat', [], 'badfloat' ),
'test', '1.', []
) ],
'Bad sign' => [ ' 1', new ValidationException(
DataMessageValue::new( 'paramvalidator-badfloat', [], 'badfloat' ),
'test', ' 1', []
) ],
'Comma as decimal separator or thousands grouping?' => [ '1,234', new ValidationException(
DataMessageValue::new( 'paramvalidator-badfloat', [], 'badfloat' ),
'test', '1,234', []
) ],
'U+2212 minus' => [ '−1', new ValidationException(
DataMessageValue::new( 'paramvalidator-badfloat', [], 'badfloat' ),
'test', '−1', []
) ],
'Overflow' => [ '1e9999', new ValidationException(
DataMessageValue::new( 'paramvalidator-badfloat-notfinite', [], 'badfloat-notfinite' ),
'test', '1e9999', []
) ],
'Overflow, -INF' => [ '-1e9999', new ValidationException(
DataMessageValue::new( 'paramvalidator-badfloat-notfinite', [], 'badfloat-notfinite' ),
'test', '-1e9999', []
) ],
'Bogus value' => [ 'foo', new ValidationException(
DataMessageValue::new( 'paramvalidator-notfinite', [], 'badfloat' ),
'test', 'foo', []
) ],
'Bogus value 2' => [ '123f4', new ValidationException(
DataMessageValue::new( 'paramvalidator-notfinite', [], 'badfloat' ),
'test', '123f4', []
) ],
'Newline' => [ "123\n", new ValidationException(
DataMessageValue::new( 'paramvalidator-notfinite', [], 'badfloat' ),
'test', "123\n", []
) ],
];
}
public function provideCheckSettings() {
$keys = [
'Y', FloatDef::PARAM_IGNORE_RANGE,
FloatDef::PARAM_MIN, FloatDef::PARAM_MAX, FloatDef::PARAM_MAX2
];
return [
'Basic test' => [
[],
self::STDRET,
[
'issues' => [ 'X' ],
'allowedKeys' => $keys,
'messages' => [],
],
],
'Test with everything' => [
[
FloatDef::PARAM_IGNORE_RANGE => true,
FloatDef::PARAM_MIN => -100.0,
FloatDef::PARAM_MAX => -90.0,
FloatDef::PARAM_MAX2 => -80.0,
],
self::STDRET,
[
'issues' => [ 'X' ],
'allowedKeys' => $keys,
'messages' => [],
],
],
'Bad types' => [
[
FloatDef::PARAM_IGNORE_RANGE => 1,
FloatDef::PARAM_MIN => 1,
FloatDef::PARAM_MAX => '2',
FloatDef::PARAM_MAX2 => '3',
],
self::STDRET,
[
'issues' => [
'X',
FloatDef::PARAM_IGNORE_RANGE => 'PARAM_IGNORE_RANGE must be boolean, got integer',
FloatDef::PARAM_MIN => 'PARAM_MIN must be double, got integer',
FloatDef::PARAM_MAX => 'PARAM_MAX must be double, got string',
FloatDef::PARAM_MAX2 => 'PARAM_MAX2 must be double, got string',
],
'allowedKeys' => $keys,
'messages' => [],
],
],
];
}
public function provideStringifyValue() {
$digits = defined( 'PHP_FLOAT_DIG' ) ? PHP_FLOAT_DIG : 15;
return [
[ 1.2, '1.2' ],
[ 10 / 3, '3.' . str_repeat( '3', $digits - 1 ) ],
[ 1e100, '1.0e+100' ],
[ 6.022e-23, '6.022e-23' ],
];
}
public function provideGetInfo() {
return [
'Basic test' => [
[],
[ 'min' => null, 'max' => null ],
[
ParamValidator::PARAM_TYPE => '<message key="paramvalidator-help-type-float"><text>1</text></message>',
],
],
'Various settings' => [
[
FloatDef::PARAM_MIN => 1,
FloatDef::PARAM_MAX => 100,
ParamValidator::PARAM_ISMULTI => true
],
[ 'min' => 1, 'max' => 100 ],
[
FloatDef::PARAM_MIN => '<message key="paramvalidator-help-type-number-minmax"><text>2</text><num>1</num><num>100</num></message>',
ParamValidator::PARAM_TYPE => '<message key="paramvalidator-help-type-float"><text>2</text></message>',
],
],
];
}
}
TypeDef/LimitDefTest.php 0000666 00000010300 15133512335 0011151 0 ustar 00 <?php
namespace Wikimedia\ParamValidator\TypeDef;
use Wikimedia\ParamValidator\ParamValidator;
use Wikimedia\ParamValidator\SimpleCallbacks;
/**
* @covers Wikimedia\ParamValidator\TypeDef\LimitDef
*/
class LimitDefTest extends TypeDefTestCase {
protected function getInstance( SimpleCallbacks $callbacks, array $options ) {
return new LimitDef( $callbacks, $options );
}
public function provideValidate() {
$useHigh = [ 'useHighLimits' => true ];
$max = [ LimitDef::PARAM_MAX => 2 ];
$max2 = [ LimitDef::PARAM_MAX => 2, LimitDef::PARAM_MAX2 => 4 ];
yield 'Max' => [ 'max', 2, $max ];
yield 'Max, use high' => [ 'max', 2, $max, $useHigh ];
yield 'Max2' => [ 'max', 2, $max2 ];
yield 'Max2, use high' => [ 'max', 4, $max2, $useHigh ];
// Test an arbitrary number for coverage. Actual number handling is tested via
// the base class IntegerDef's tests.
yield 'A number' => [ '123', 123 ];
}
public function provideNormalizeSettings() {
$def = [ LimitDef::PARAM_MIN => 0, ParamValidator::PARAM_ISMULTI => false ];
return [
[
[],
$def,
],
[
[ ParamValidator::PARAM_ISMULTI => true ],
[ ParamValidator::PARAM_ISMULTI => false ] + $def,
],
[
[ LimitDef::PARAM_MAX => 2 ],
[ LimitDef::PARAM_MAX => 2 ] + $def,
],
[
[ LimitDef::PARAM_MIN => 1, LimitDef::PARAM_MAX => 2, LimitDef::PARAM_MAX2 => 4 ],
[ LimitDef::PARAM_MIN => 1, LimitDef::PARAM_MAX => 2, LimitDef::PARAM_MAX2 => 4 ] + $def,
],
[
[ LimitDef::PARAM_MIN => 1, LimitDef::PARAM_MAX => 4, LimitDef::PARAM_MAX2 => 2 ],
[ LimitDef::PARAM_MIN => 1, LimitDef::PARAM_MAX => 4, LimitDef::PARAM_MAX2 => 4 ] + $def,
],
[
[ LimitDef::PARAM_MAX2 => 2 ],
$def,
],
];
}
public function provideCheckSettings() {
$keys = [
'Y', IntegerDef::PARAM_IGNORE_RANGE,
IntegerDef::PARAM_MIN, IntegerDef::PARAM_MAX, IntegerDef::PARAM_MAX2
];
return [
'Basic test' => [
[
LimitDef::PARAM_MAX => 10,
],
self::STDRET,
[
'issues' => [ 'X' ],
'allowedKeys' => $keys,
'messages' => [],
],
],
'Test with everything' => [
[
LimitDef::PARAM_IGNORE_RANGE => true,
LimitDef::PARAM_MIN => 0,
LimitDef::PARAM_MAX => 10,
LimitDef::PARAM_MAX2 => 100,
],
self::STDRET,
[
'issues' => [ 'X' ],
'allowedKeys' => $keys,
'messages' => [],
],
],
'PARAM_ISMULTI not allowed' => [
[
ParamValidator::PARAM_ISMULTI => true,
LimitDef::PARAM_MAX => 10,
],
self::STDRET,
[
'issues' => [
'X',
ParamValidator::PARAM_ISMULTI => 'PARAM_ISMULTI cannot be used for limit-type parameters',
],
'allowedKeys' => $keys,
'messages' => [],
],
],
'PARAM_ISMULTI not allowed, but another ISMULTI issue was already logged' => [
[
ParamValidator::PARAM_ISMULTI => true,
LimitDef::PARAM_MAX => 10,
],
[
'issues' => [
ParamValidator::PARAM_ISMULTI => 'XXX',
],
'allowedKeys' => [ 'Y' ],
'messages' => [],
],
[
'issues' => [
ParamValidator::PARAM_ISMULTI => 'XXX',
],
'allowedKeys' => $keys,
'messages' => [],
],
],
'PARAM_MIN == 0' => [
[
LimitDef::PARAM_MIN => 0,
LimitDef::PARAM_MAX => 2,
],
self::STDRET,
[
'issues' => [ 'X' ],
'allowedKeys' => $keys,
'messages' => [],
],
],
'PARAM_MIN < 0' => [
[
LimitDef::PARAM_MIN => -1,
LimitDef::PARAM_MAX => 2,
],
self::STDRET,
[
'issues' => [
'X',
'PARAM_MIN must be greater than or equal to 0',
],
'allowedKeys' => $keys,
'messages' => [],
],
],
'PARAM_MAX is required' => [
[],
self::STDRET,
[
'issues' => [
'X',
'PARAM_MAX must be set',
],
'allowedKeys' => $keys,
'messages' => [],
],
],
];
}
public function provideGetInfo() {
return [
'Basic' => [
[],
[ 'min' => 0, 'max' => null ],
[
LimitDef::PARAM_MIN => '<message key="paramvalidator-help-type-number-min"><text>1</text><num>0</num><text>∞</text></message>',
ParamValidator::PARAM_TYPE => '<message key="paramvalidator-help-type-limit"><text>1</text></message>',
],
],
];
}
}
TypeDef/IntegerDefTest.php 0000666 00000023447 15133512335 0011510 0 ustar 00 <?php
namespace Wikimedia\ParamValidator\TypeDef;
use Wikimedia\Message\DataMessageValue;
use Wikimedia\ParamValidator\ParamValidator;
use Wikimedia\ParamValidator\SimpleCallbacks;
use Wikimedia\ParamValidator\ValidationException;
/**
* @covers Wikimedia\ParamValidator\TypeDef\IntegerDef
* @covers Wikimedia\ParamValidator\TypeDef\NumericDef
*/
class IntegerDefTest extends TypeDefTestCase {
protected function getInstance( SimpleCallbacks $callbacks, array $options ) {
return new IntegerDef( $callbacks, $options );
}
/**
* @param string $v Representing a positive integer
* @return string Representing $v + 1
*/
private static function plusOne( $v ) {
for ( $i = strlen( $v ) - 1; $i >= 0; $i-- ) {
if ( $v[$i] === '9' ) {
$v[$i] = '0';
} else {
$v[$i] = $v[$i] + 1;
return $v;
}
}
return '1' . $v;
}
public function provideValidate() {
$badinteger = new ValidationException(
DataMessageValue::new( 'XXX', [], 'badinteger' ),
'test', '...', []
);
$outofrange = new ValidationException(
DataMessageValue::new( 'XXX', [], 'outofrange', [
'min' => 0, 'curmax' => 2, 'max' => 2, 'highmax' => 2
] ),
'test', '...', []
);
$outofrange2 = new ValidationException(
DataMessageValue::new( 'XXX', [], 'outofrange', [
'min' => 0, 'curmax' => 2, 'max' => 2, 'highmax' => 4
] ),
'test', '...', []
);
$outofrange2h = new ValidationException(
DataMessageValue::new( 'XXX', [], 'outofrange', [
'min' => 0, 'curmax' => 4, 'max' => 2, 'highmax' => 4
] ),
'test', '...', []
);
$asWarn = static function ( ValidationException $ex ) {
return [
'code' => $ex->getFailureMessage()->getCode(),
'data' => $ex->getFailureMessage()->getData(),
];
};
$minmax = [
IntegerDef::PARAM_MIN => 0,
IntegerDef::PARAM_MAX => 2,
];
$minmax2 = [
IntegerDef::PARAM_MIN => 0,
IntegerDef::PARAM_MAX => 2,
IntegerDef::PARAM_MAX2 => 4,
];
$ignore = [
IntegerDef::PARAM_IGNORE_RANGE => true,
];
$usehigh = [ 'useHighLimits' => true ];
return [
[ '123', 123 ],
[ '-123', -123 ],
[ '000123', 123 ],
[ '000', 0 ],
[ '-0', 0 ],
[ (string)PHP_INT_MAX, PHP_INT_MAX ],
[ '0000' . PHP_INT_MAX, PHP_INT_MAX ],
[ (string)PHP_INT_MIN, PHP_INT_MIN ],
[ '-0000' . substr( PHP_INT_MIN, 1 ), PHP_INT_MIN ],
'Overflow' => [ self::plusOne( (string)PHP_INT_MAX ), $badinteger ],
'Negative overflow' => [ '-' . self::plusOne( substr( PHP_INT_MIN, 1 ) ), $badinteger ],
'Float' => [ '1.5', $badinteger ],
'Float (e notation)' => [ '1e1', $badinteger ],
'Bad sign (space)' => [ ' 1', $badinteger ],
'Bad sign (newline)' => [ "\n1", $badinteger ],
'Bogus value' => [ 'max', $badinteger ],
'Bogus value (2)' => [ '1foo', $badinteger ],
'Hex value' => [ '0x123', $badinteger ],
'Newline' => [ "1\n", $badinteger ],
'Array' => [ [ '1.5' ], $badinteger ],
'Ok with range' => [ '1', 1, $minmax ],
'Below minimum' => [ '-1', $outofrange, $minmax ],
'Below minimum, ignored' => [ '-1', 0, $minmax + $ignore, [], [ $asWarn( $outofrange ) ] ],
'Above maximum' => [ '3', $outofrange, $minmax ],
'Above maximum, ignored' => [ '3', 2, $minmax + $ignore, [], [ $asWarn( $outofrange ) ] ],
'Not above max2 but can\'t use it' => [ '3', $outofrange2, $minmax2, [] ],
'Not above max2 but can\'t use it, ignored'
=> [ '3', 2, $minmax2 + $ignore, [], [ $asWarn( $outofrange2 ) ] ],
'Not above max2' => [ '3', 3, $minmax2, $usehigh ],
'Above max2' => [ '5', $outofrange2h, $minmax2, $usehigh ],
'Above max2, ignored'
=> [ '5', 4, $minmax2 + $ignore, $usehigh, [ $asWarn( $outofrange2h ) ] ],
];
}
public function provideNormalizeSettings() {
return [
[ [], [] ],
[
[ IntegerDef::PARAM_MAX => 2 ],
[ IntegerDef::PARAM_MAX => 2 ],
],
[
[ IntegerDef::PARAM_MIN => 1, IntegerDef::PARAM_MAX => 2, IntegerDef::PARAM_MAX2 => 4 ],
[ IntegerDef::PARAM_MIN => 1, IntegerDef::PARAM_MAX => 2, IntegerDef::PARAM_MAX2 => 4 ],
],
[
[ IntegerDef::PARAM_MIN => 1, IntegerDef::PARAM_MAX => 4, IntegerDef::PARAM_MAX2 => 2 ],
[ IntegerDef::PARAM_MIN => 1, IntegerDef::PARAM_MAX => 4, IntegerDef::PARAM_MAX2 => 4 ],
],
[
[ IntegerDef::PARAM_MAX2 => 2 ],
[],
],
];
}
public function provideCheckSettings() {
$keys = [
'Y', IntegerDef::PARAM_IGNORE_RANGE,
IntegerDef::PARAM_MIN, IntegerDef::PARAM_MAX, IntegerDef::PARAM_MAX2
];
return [
'Basic test' => [
[],
self::STDRET,
[
'issues' => [ 'X' ],
'allowedKeys' => $keys,
'messages' => [],
],
],
'Test with everything' => [
[
IntegerDef::PARAM_IGNORE_RANGE => true,
IntegerDef::PARAM_MIN => -100,
IntegerDef::PARAM_MAX => -90,
IntegerDef::PARAM_MAX2 => -80,
],
self::STDRET,
[
'issues' => [ 'X' ],
'allowedKeys' => $keys,
'messages' => [],
],
],
'Bad types' => [
[
IntegerDef::PARAM_IGNORE_RANGE => 1,
IntegerDef::PARAM_MIN => 1.0,
IntegerDef::PARAM_MAX => '2',
IntegerDef::PARAM_MAX2 => '3',
],
self::STDRET,
[
'issues' => [
'X',
IntegerDef::PARAM_IGNORE_RANGE => 'PARAM_IGNORE_RANGE must be boolean, got integer',
IntegerDef::PARAM_MIN => 'PARAM_MIN must be integer, got double',
IntegerDef::PARAM_MAX => 'PARAM_MAX must be integer, got string',
IntegerDef::PARAM_MAX2 => 'PARAM_MAX2 must be integer, got string',
],
'allowedKeys' => $keys,
'messages' => [],
],
],
'Min == max' => [
[
IntegerDef::PARAM_MIN => 1,
IntegerDef::PARAM_MAX => 1,
],
self::STDRET,
[
'issues' => [ 'X' ],
'allowedKeys' => $keys,
'messages' => [],
],
],
'Min > max' => [
[
IntegerDef::PARAM_MIN => 2,
IntegerDef::PARAM_MAX => 1,
],
self::STDRET,
[
'issues' => [
'X',
'PARAM_MIN must be less than or equal to PARAM_MAX, but 2 > 1',
],
'allowedKeys' => $keys,
'messages' => [],
],
],
'Max2 without max' => [
[
IntegerDef::PARAM_MAX2 => 1,
],
self::STDRET,
[
'issues' => [
'X',
'PARAM_MAX2 cannot be used without PARAM_MAX',
],
'allowedKeys' => $keys,
'messages' => [],
],
],
'Max2 == max' => [
[
IntegerDef::PARAM_MAX => 1,
IntegerDef::PARAM_MAX2 => 1,
],
self::STDRET,
[
'issues' => [ 'X' ],
'allowedKeys' => $keys,
'messages' => [],
],
],
'Max2 < max' => [
[
IntegerDef::PARAM_MAX => -10,
IntegerDef::PARAM_MAX2 => -11,
],
self::STDRET,
[
'issues' => [
'X',
'PARAM_MAX2 must be greater than or equal to PARAM_MAX, but -11 < -10',
],
'allowedKeys' => $keys,
'messages' => [],
],
],
];
}
public function provideGetInfo() {
return [
'Basic' => [
[],
[ 'min' => null, 'max' => null ],
[
ParamValidator::PARAM_TYPE => '<message key="paramvalidator-help-type-integer"><text>1</text></message>',
],
],
'Min' => [
[ IntegerDef::PARAM_MIN => 0, ParamValidator::PARAM_ISMULTI => true ],
[ 'min' => 0, 'max' => null ],
[
IntegerDef::PARAM_MIN => '<message key="paramvalidator-help-type-number-min"><text>2</text><num>0</num><text>∞</text></message>',
ParamValidator::PARAM_TYPE => '<message key="paramvalidator-help-type-integer"><text>2</text></message>',
],
],
'Max' => [
[ IntegerDef::PARAM_MAX => 2 ],
[ 'min' => null, 'max' => 2 ],
[
IntegerDef::PARAM_MIN => '<message key="paramvalidator-help-type-number-max"><text>1</text><text>−∞</text><num>2</num></message>',
ParamValidator::PARAM_TYPE => '<message key="paramvalidator-help-type-integer"><text>1</text></message>',
],
],
'Max2' => [
[ IntegerDef::PARAM_MAX => 2, IntegerDef::PARAM_MAX2 => 4 ],
[ 'min' => null, 'max' => 2, 'highmax' => 4 ],
[
IntegerDef::PARAM_MIN => '<message key="paramvalidator-help-type-number-max"><text>1</text><text>−∞</text><num>2</num></message>',
ParamValidator::PARAM_TYPE => '<message key="paramvalidator-help-type-integer"><text>1</text></message>',
],
],
'Max2, highlimits' => [
[ IntegerDef::PARAM_MAX => 2, IntegerDef::PARAM_MAX2 => 4 ],
[ 'min' => null, 'max' => 2, 'highmax' => 4 ],
[
IntegerDef::PARAM_MIN => '<message key="paramvalidator-help-type-number-max"><text>1</text><text>−∞</text><num>4</num></message>',
ParamValidator::PARAM_TYPE => '<message key="paramvalidator-help-type-integer"><text>1</text></message>',
],
[ 'useHighLimits' => true ],
],
'Minmax' => [
[ IntegerDef::PARAM_MIN => 0, IntegerDef::PARAM_MAX => 2 ],
[ 'min' => 0, 'max' => 2 ],
[
IntegerDef::PARAM_MIN => '<message key="paramvalidator-help-type-number-minmax"><text>1</text><num>0</num><num>2</num></message>',
ParamValidator::PARAM_TYPE => '<message key="paramvalidator-help-type-integer"><text>1</text></message>',
],
],
'Minmax2' => [
[ IntegerDef::PARAM_MIN => 0, IntegerDef::PARAM_MAX => 2, IntegerDef::PARAM_MAX2 => 4 ],
[ 'min' => 0, 'max' => 2, 'highmax' => 4 ],
[
IntegerDef::PARAM_MIN => '<message key="paramvalidator-help-type-number-minmax"><text>1</text><num>0</num><num>2</num></message>',
ParamValidator::PARAM_TYPE => '<message key="paramvalidator-help-type-integer"><text>1</text></message>',
],
],
'Minmax2, highlimits' => [
[
IntegerDef::PARAM_MIN => 0, IntegerDef::PARAM_MAX => 2, IntegerDef::PARAM_MAX2 => 4,
ParamValidator::PARAM_ISMULTI => true
],
[ 'min' => 0, 'max' => 2, 'highmax' => 4 ],
[
IntegerDef::PARAM_MIN => '<message key="paramvalidator-help-type-number-minmax"><text>2</text><num>0</num><num>4</num></message>',
ParamValidator::PARAM_TYPE => '<message key="paramvalidator-help-type-integer"><text>2</text></message>',
],
[ 'useHighLimits' => true ],
],
];
}
}
TypeDef/ExpiryDefTest.php 0000666 00000012067 15133512335 0011367 0 ustar 00 <?php
namespace Wikimedia\ParamValidator\TypeDef;
use InvalidArgumentException;
use Wikimedia\Message\DataMessageValue;
use Wikimedia\ParamValidator\SimpleCallbacks;
use Wikimedia\ParamValidator\ValidationException;
use Wikimedia\Timestamp\ConvertibleTimestamp;
/**
* @covers \Wikimedia\ParamValidator\TypeDef\ExpiryDef
*/
class ExpiryDefTest extends TypeDefTestCase {
protected function getInstance( SimpleCallbacks $callbacks, array $options ) {
return new ExpiryDef( $callbacks, $options );
}
/**
* Get an entry for the provideValidate() provider, where a given value
* is asserted to cause a ValidationException with the given message.
* @param string $value
* @param string $msg
* @param array $settings
* @return array
*/
private function getValidationAssertion( string $value, string $msg, array $settings = [] ) {
return [
$value,
new ValidationException(
DataMessageValue::new( $msg ),
'expiry',
$value,
[]
),
$settings
];
}
/**
* @dataProvider provideValidate
*/
public function testValidate(
$value, $expect, array $settings = [], array $options = [], array $expectConds = []
) {
ConvertibleTimestamp::setFakeTime( 1559764242 );
parent::testValidate( $value, $expect, $settings, $options, $expectConds );
}
public function provideValidate() {
$settings = [
ExpiryDef::PARAM_MAX => '6 months',
ExpiryDef::PARAM_USE_MAX => true,
];
return [
'Valid infinity' => [ 'indefinite', 'infinity' ],
'Invalid expiry' => $this->getValidationAssertion( 'foobar', 'badexpiry' ),
'Expiry in past' => $this->getValidationAssertion( '20150123T12:34:56Z', 'badexpiry-past' ),
'Expiry in past with unix 0' => $this->getValidationAssertion(
'1970-01-01T00:00:00Z',
'badexpiry-past'
),
'Expiry in past with negative unix time' => $this->getValidationAssertion(
'1969-12-31T23:59:59Z',
'badexpiry-past',
$settings
),
'Valid expiry' => [
'99990123123456',
'9999-01-23T12:34:56Z'
],
'Valid relative expiry' => [
'1 month',
'2019-07-05T19:50:42Z'
],
'Expiry less than max' => [ '20190701123456', '2019-07-01T12:34:56Z', $settings ],
'Relative expiry less than max' => [ '1 day', '2019-06-06T19:50:42Z', $settings ],
'Infinity less than max' => [ 'indefinite', 'infinity', $settings ],
'Expiry exceeds max' => [
'9999-01-23T12:34:56Z',
'2019-12-05T19:50:42Z',
$settings,
[],
[
[
'code' => 'paramvalidator-badexpiry-duration-max',
'data' => null,
]
],
],
'Relative expiry exceeds max' => [
'10 years',
'2019-12-05T19:50:42Z',
$settings,
[],
[
[
'code' => 'paramvalidator-badexpiry-duration-max',
'data' => null,
]
],
],
'Expiry exceeds max, fatal' => $this->getValidationAssertion(
'9999-01-23T12:34:56Z',
'paramvalidator-badexpiry-duration',
[
ExpiryDef::PARAM_MAX => '6 months',
]
),
];
}
public function testNormalizeExpiry() {
$this->assertNull( ExpiryDef::normalizeExpiry( null ) );
$this->assertSame(
'infinity',
ExpiryDef::normalizeExpiry( 'indefinite' )
);
$this->assertSame(
'2050-01-01T00:00:00Z',
ExpiryDef::normalizeExpiry( '205001010000', TS_ISO_8601 )
);
$this->assertSame(
'1970-01-01T00:00:00Z',
ExpiryDef::normalizeExpiry( '1970-01-01T00:00:00Z', TS_ISO_8601 )
);
$this->expectException( InvalidArgumentException::class );
$this->expectExceptionMessage( 'Invalid expiry value: 0' );
ExpiryDef::normalizeExpiry( 0, TS_ISO_8601 );
}
public function provideGetInfo() {
return [
'Basic' => [
[],
[],
[
'param-type' => '<message key="paramvalidator-help-type-expiry"><text>1</text><list listType="text"><text>"infinite"</text><text>"indefinite"</text><text>"infinity"</text><text>"never"</text></list></message>'
]
]
];
}
public function provideCheckSettings() {
$keys = [ 'Y', ExpiryDef::PARAM_USE_MAX, ExpiryDef::PARAM_MAX ];
return [
'Basic test' => [
[],
self::STDRET,
[
'issues' => [ 'X' ],
'allowedKeys' => $keys,
'messages' => [],
],
]
];
}
/**
* @covers \Wikimedia\ParamValidator\TypeDef\ExpiryDef::normalizeUsingMaxExpiry
*/
public function testNormalizeUsingMaxExpiry() {
// Fake current time to be 2020-05-27T00:00:00Z
$fakeTime = ConvertibleTimestamp::setFakeTime( '20200527000000' );
$this->assertSame(
'2020-11-27T00:00:00Z',
ExpiryDef::normalizeUsingMaxExpiry( '10 months', '6 months', TS_ISO_8601 )
);
$this->assertSame(
'2020-10-27T00:00:00Z',
ExpiryDef::normalizeUsingMaxExpiry( '2020-10-27T00:00:00Z', '6 months', TS_ISO_8601 )
);
$this->assertSame(
'infinity',
ExpiryDef::normalizeUsingMaxExpiry( 'infinity', '6 months', TS_ISO_8601 )
);
$this->assertNull( ExpiryDef::normalizeUsingMaxExpiry( null, '6 months', TS_ISO_8601 ) );
$this->expectException( InvalidArgumentException::class );
$this->expectExceptionMessage( 'Invalid expiry value: invalid expiry' );
ExpiryDef::normalizeUsingMaxExpiry( 'invalid expiry', '6 months', TS_ISO_8601 );
}
}
TypeDef/BooleanDefTest.php 0000666 00000003412 15133512335 0011460 0 ustar 00 <?php
namespace Wikimedia\ParamValidator\TypeDef;
use Wikimedia\Message\DataMessageValue;
use Wikimedia\ParamValidator\ParamValidator;
use Wikimedia\ParamValidator\SimpleCallbacks;
use Wikimedia\ParamValidator\ValidationException;
/**
* @covers \Wikimedia\ParamValidator\TypeDef\BooleanDef
*/
class BooleanDefTest extends TypeDefTestCase {
protected function getInstance( SimpleCallbacks $callbacks, array $options ) {
return new BooleanDef( $callbacks, $options );
}
public function provideValidate() {
foreach ( [
[ BooleanDef::$TRUEVALS, true ],
[ BooleanDef::$FALSEVALS, false ],
[ [ '' ], false ],
] as [ $vals, $expect ] ) {
foreach ( $vals as $v ) {
yield "Value '$v'" => [ $v, $expect ];
$v2 = ucfirst( $v );
if ( $v2 !== $v ) {
yield "Value '$v2'" => [ $v2, $expect ];
}
$v3 = strtoupper( $v );
if ( $v3 !== $v2 ) {
yield "Value '$v3'" => [ $v3, $expect ];
}
}
}
yield "Value '2'" => [ 2, new ValidationException(
DataMessageValue::new( 'paramvalidator-badbool', [], 'badbool' ),
'test', '2', []
) ];
yield "Value 'foobar'" => [ 'foobar', new ValidationException(
DataMessageValue::new( 'paramvalidator-badbool', [], 'badbool' ),
'test', 'foobar', []
) ];
}
public function provideStringifyValue() {
return [
[ true, 'true' ],
[ false, 'false' ],
];
}
public function provideGetInfo() {
return [
'Basic test' => [
[],
[],
[
ParamValidator::PARAM_TYPE => '<message key="paramvalidator-help-type-boolean"><text>1</text></message>',
],
],
'Multi-valued' => [
[ ParamValidator::PARAM_ISMULTI => true ],
[],
[
ParamValidator::PARAM_TYPE => '<message key="paramvalidator-help-type-boolean"><text>2</text></message>',
],
],
];
}
}
TypeDef/StringDefTest.php 0000666 00000012531 15133512335 0011351 0 ustar 00 <?php
namespace Wikimedia\ParamValidator\TypeDef;
use Wikimedia\Message\DataMessageValue;
use Wikimedia\ParamValidator\ParamValidator;
use Wikimedia\ParamValidator\SimpleCallbacks;
use Wikimedia\ParamValidator\ValidationException;
/**
* @covers Wikimedia\ParamValidator\TypeDef\StringDef
*/
class StringDefTest extends TypeDefTestCase {
protected function getInstance( SimpleCallbacks $callbacks, array $options ) {
return new StringDef( $callbacks, $options );
}
public function provideValidate() {
$req = [
ParamValidator::PARAM_REQUIRED => true,
];
$maxBytes = [
StringDef::PARAM_MAX_BYTES => 4,
];
$maxChars = [
StringDef::PARAM_MAX_CHARS => 2,
];
return [
'Basic' => [ '123', '123' ],
'Empty' => [ '', '' ],
'Empty, required' => [
'',
new ValidationException(
DataMessageValue::new( 'paramvalidator-missingparam', [], 'missingparam' ),
'test', '', []
),
$req,
],
'Empty, required, allowed' => [ '', '', $req, [ 'allowEmptyWhenRequired' => true ] ],
'Max bytes, ok' => [ 'abcd', 'abcd', $maxBytes ],
'Max bytes, exceeded' => [
'abcde',
new ValidationException(
DataMessageValue::new( 'paramvalidator-maxbytes', [], 'maxbytes', [
'maxbytes' => 4, 'maxchars' => null,
] ),
'test', '', []
),
$maxBytes,
],
'Max bytes, ok (2)' => [ '😄', '😄', $maxBytes ],
'Max bytes, exceeded (2)' => [
'😭?',
new ValidationException(
DataMessageValue::new( 'paramvalidator-maxbytes', [], 'maxbytes', [
'maxbytes' => 4, 'maxchars' => null,
] ),
'test', '', []
),
$maxBytes,
],
'Max chars, ok' => [ 'ab', 'ab', $maxChars ],
'Max chars, exceeded' => [
'abc',
new ValidationException(
DataMessageValue::new( 'paramvalidator-maxchars', [], 'maxchars', [
'maxbytes' => null, 'maxchars' => 2,
] ),
'test', '', []
),
$maxChars,
],
'Max chars, ok (2)' => [ '😄😄', '😄😄', $maxChars ],
'Max chars, exceeded (2)' => [
'😭??',
new ValidationException(
DataMessageValue::new( 'paramvalidator-maxchars', [], 'maxchars', [
'maxbytes' => null, 'maxchars' => 2,
] ),
'test', '', []
),
$maxChars,
],
];
}
public function provideCheckSettings() {
$keys = [ 'Y', StringDef::PARAM_MAX_BYTES, StringDef::PARAM_MAX_CHARS ];
return [
'Basic test' => [
[],
self::STDRET,
[
'issues' => [ 'X' ],
'allowedKeys' => $keys,
'messages' => [],
],
],
'Test with everything' => [
[
StringDef::PARAM_MAX_BYTES => 255,
StringDef::PARAM_MAX_CHARS => 100,
],
self::STDRET,
[
'issues' => [ 'X' ],
'allowedKeys' => $keys,
'messages' => [],
],
],
'Bad types' => [
[
StringDef::PARAM_MAX_BYTES => '255',
StringDef::PARAM_MAX_CHARS => 100.0,
],
self::STDRET,
[
'issues' => [
'X',
StringDef::PARAM_MAX_BYTES => 'PARAM_MAX_BYTES must be an integer, got string',
StringDef::PARAM_MAX_CHARS => 'PARAM_MAX_CHARS must be an integer, got double',
],
'allowedKeys' => $keys,
'messages' => [],
],
],
'Out of range' => [
[
StringDef::PARAM_MAX_BYTES => -1,
StringDef::PARAM_MAX_CHARS => -1,
],
self::STDRET,
[
'issues' => [
'X',
StringDef::PARAM_MAX_BYTES => 'PARAM_MAX_BYTES must be greater than or equal to 0',
StringDef::PARAM_MAX_CHARS => 'PARAM_MAX_CHARS must be greater than or equal to 0',
],
'allowedKeys' => $keys,
'messages' => [],
],
],
'Zero not allowed when required and !allowEmptyWhenRequired' => [
[
ParamValidator::PARAM_REQUIRED => true,
StringDef::PARAM_MAX_BYTES => 0,
StringDef::PARAM_MAX_CHARS => 0,
],
self::STDRET,
[
'issues' => [
'X',
'PARAM_REQUIRED is set, allowEmptyWhenRequired is not set, and PARAM_MAX_BYTES is 0. That\'s impossible to satisfy.',
'PARAM_REQUIRED is set, allowEmptyWhenRequired is not set, and PARAM_MAX_CHARS is 0. That\'s impossible to satisfy.',
],
'allowedKeys' => $keys,
'messages' => [],
],
[ 'allowEmptyWhenRequired' => false ],
],
'Zero allowed when not required' => [
[
StringDef::PARAM_MAX_BYTES => 0,
StringDef::PARAM_MAX_CHARS => 0,
],
self::STDRET,
[
'issues' => [ 'X' ],
'allowedKeys' => $keys,
'messages' => [],
],
[ 'allowEmptyWhenRequired' => false ],
],
'Zero allowed when allowEmptyWhenRequired' => [
[
ParamValidator::PARAM_REQUIRED => true,
StringDef::PARAM_MAX_BYTES => 0,
StringDef::PARAM_MAX_CHARS => 0,
],
self::STDRET,
[
'issues' => [ 'X' ],
'allowedKeys' => $keys,
'messages' => [],
],
[ 'allowEmptyWhenRequired' => true ],
],
];
}
public function provideGetInfo() {
return [
'Basic test' => [
[],
[ 'maxbytes' => null, 'maxchars' => null ],
[],
],
'With settings' => [
[
StringDef::PARAM_MAX_BYTES => 4,
StringDef::PARAM_MAX_CHARS => 2,
ParamValidator::PARAM_ISMULTI => true,
],
[ 'maxbytes' => 4, 'maxchars' => 2 ],
[
StringDef::PARAM_MAX_BYTES => '<message key="paramvalidator-help-type-string-maxbytes"><num>4</num></message>',
StringDef::PARAM_MAX_CHARS => '<message key="paramvalidator-help-type-string-maxchars"><num>2</num></message>',
],
],
];
}
}
TypeDef/PresenceBooleanDefTest.php 0000666 00000006253 15133512335 0013153 0 ustar 00 <?php
namespace Wikimedia\ParamValidator\TypeDef;
use Wikimedia\ParamValidator\ParamValidator;
use Wikimedia\ParamValidator\SimpleCallbacks;
/**
* @covers Wikimedia\ParamValidator\TypeDef\PresenceBooleanDef
*/
class PresenceBooleanDefTest extends TypeDefTestCase {
protected function getInstance( SimpleCallbacks $callbacks, array $options ) {
return new PresenceBooleanDef( $callbacks, $options );
}
public function provideValidate() {
return [
[ null, false ],
[ '', true ],
[ '0', true ],
[ '1', true ],
[ 'anything really', true ],
];
}
public function provideNormalizeSettings() {
return [
[
[],
[ ParamValidator::PARAM_ISMULTI => false, ParamValidator::PARAM_DEFAULT => false ],
],
[
[ ParamValidator::PARAM_ISMULTI => true ],
[ ParamValidator::PARAM_ISMULTI => false, ParamValidator::PARAM_DEFAULT => false ],
],
[
[ ParamValidator::PARAM_DEFAULT => null ],
[ ParamValidator::PARAM_DEFAULT => null, ParamValidator::PARAM_ISMULTI => false ],
],
];
}
public function provideCheckSettings() {
return [
'Basic test' => [
[],
self::STDRET,
self::STDRET,
],
'PARAM_ISMULTI not allowed' => [
[
ParamValidator::PARAM_ISMULTI => true,
],
self::STDRET,
[
'issues' => [
'X',
ParamValidator::PARAM_ISMULTI
=> 'PARAM_ISMULTI cannot be used for presence-boolean-type parameters',
],
'allowedKeys' => [ 'Y' ],
'messages' => [],
],
],
'PARAM_ISMULTI not allowed, but another ISMULTI issue was already logged' => [
[
ParamValidator::PARAM_ISMULTI => true,
],
[
'issues' => [
ParamValidator::PARAM_ISMULTI => 'XXX',
],
'allowedKeys' => [ 'Y' ],
'messages' => [],
],
[
'issues' => [
ParamValidator::PARAM_ISMULTI => 'XXX',
],
'allowedKeys' => [ 'Y' ],
'messages' => [],
],
],
'PARAM_DEFAULT can be false' => [
[ ParamValidator::PARAM_DEFAULT => false ],
self::STDRET,
self::STDRET,
],
'PARAM_DEFAULT can be null' => [
[ ParamValidator::PARAM_DEFAULT => null ],
self::STDRET,
self::STDRET,
],
'PARAM_DEFAULT cannot be true' => [
[
ParamValidator::PARAM_DEFAULT => true,
],
self::STDRET,
[
'issues' => [
'X',
ParamValidator::PARAM_DEFAULT
=> 'Default for presence-boolean-type parameters must be false or null',
],
'allowedKeys' => [ 'Y' ],
'messages' => [],
],
],
'PARAM_DEFAULT invalid, but another DEFAULT issue was already logged' => [
[
ParamValidator::PARAM_DEFAULT => true,
],
[
'issues' => [
ParamValidator::PARAM_DEFAULT => 'XXX',
],
'allowedKeys' => [ 'Y' ],
'messages' => [],
],
[
'issues' => [
ParamValidator::PARAM_DEFAULT => 'XXX',
],
'allowedKeys' => [ 'Y' ],
'messages' => [],
],
],
];
}
public function provideGetInfo() {
return [
'Basic test' => [
[],
[ 'default' => null ],
[
ParamValidator::PARAM_TYPE => '<message key="paramvalidator-help-type-presenceboolean"><text>1</text></message>',
ParamValidator::PARAM_DEFAULT => null,
],
],
];
}
}
TypeDef/PasswordDefTest.php 0000666 00000003070 15133512335 0011703 0 ustar 00 <?php
namespace Wikimedia\ParamValidator\TypeDef;
use Wikimedia\ParamValidator\ParamValidator;
use Wikimedia\ParamValidator\SimpleCallbacks;
require_once __DIR__ . '/StringDefTest.php';
/**
* @covers Wikimedia\ParamValidator\TypeDef\PasswordDef
*/
class PasswordDefTest extends StringDefTest {
protected function getInstance( SimpleCallbacks $callbacks, array $options ) {
return new PasswordDef( $callbacks, $options );
}
public function provideNormalizeSettings() {
return [
[ [], [ ParamValidator::PARAM_SENSITIVE => true ] ],
[ [ ParamValidator::PARAM_SENSITIVE => false ], [ ParamValidator::PARAM_SENSITIVE => true ] ],
];
}
public function provideCheckSettings() {
$keys = [ 'Y', StringDef::PARAM_MAX_BYTES, StringDef::PARAM_MAX_CHARS ];
yield from parent::provideCheckSettings();
yield 'PARAM_SENSITIVE cannot be false' => [
[
ParamValidator::PARAM_SENSITIVE => false,
],
self::STDRET,
[
'issues' => [
'X',
ParamValidator::PARAM_SENSITIVE
=> 'Cannot set PARAM_SENSITIVE to false for password-type parameters',
],
'allowedKeys' => $keys,
'messages' => [],
],
];
yield 'PARAM_SENSITIVE cannot be false, but another PARAM_SENSITIVE issue was already logged' => [
[
ParamValidator::PARAM_SENSITIVE => false,
],
[
'issues' => [
ParamValidator::PARAM_SENSITIVE => 'XXX',
],
'allowedKeys' => [ 'Y' ],
'messages' => [],
],
[
'issues' => [
ParamValidator::PARAM_SENSITIVE => 'XXX',
],
'allowedKeys' => $keys,
'messages' => [],
],
];
}
}
TypeDef/TypeDefTestCaseTrait.php 0000666 00000016142 15133512335 0012626 0 ustar 00 <?php
namespace Wikimedia\ParamValidator\TypeDef;
use Wikimedia\Message\DataMessageValue;
use Wikimedia\Message\MessageValue;
use Wikimedia\ParamValidator\SimpleCallbacks;
use Wikimedia\ParamValidator\TypeDef;
use Wikimedia\ParamValidator\ValidationException;
use Wikimedia\Timestamp\ConvertibleTimestamp;
/**
* Test case infrastructure for TypeDef subclasses
*
* Generally you'll only need to implement self::getInstance() and
* data providers methods.
*/
trait TypeDefTestCaseTrait {
/**
* Reset any fake timestamps so that they don't mess with any other tests.
*
* @after
*/
protected function fakeTimestampTearDown() {
ConvertibleTimestamp::setFakeTime( null );
}
/**
* Create a SimpleCallbacks for testing
*
* The object created here should result in a call to the TypeDef's
* `getValue( 'test' )` returning an appropriate result for testing.
*
* @param mixed $value Value to return for 'test'
* @param array $options Options array.
* @return SimpleCallbacks
*/
protected function getCallbacks( $value, array $options ) {
return new SimpleCallbacks( $value === null ? [] : [ 'test' => $value ] );
}
/**
* Create an instance of the TypeDef subclass being tested
*
* @param SimpleCallbacks $callbacks From $this->getCallbacks()
* @param array $options Options array.
* @return TypeDef
*/
abstract protected function getInstance( SimpleCallbacks $callbacks, array $options );
/**
* @dataProvider provideValidate
* @param mixed $value Value for getCallbacks()
* @param mixed|ValidationException $expect Expected result from TypeDef::validate().
* If a ValidationException, it is expected that a ValidationException
* with matching failure code and data will be thrown. Otherwise, the return value must be equal.
* @param array $settings Settings array.
* @param array $options Options array
* @param array[] $expectConds Expected condition codes and data reported.
*/
public function testValidate(
$value, $expect, array $settings = [], array $options = [], array $expectConds = []
) {
$callbacks = $this->getCallbacks( $value, $options );
$typeDef = $this->getInstance( $callbacks, $options );
$settings = $typeDef->normalizeSettings( $settings );
if ( $expect instanceof ValidationException ) {
try {
$v = $typeDef->getValue( 'test', $settings, $options );
$typeDef->validate( 'test', $v, $settings, $options );
$this->fail( 'Expected exception not thrown' );
} catch ( ValidationException $ex ) {
$this->assertSame(
$expect->getFailureMessage()->getCode(),
$ex->getFailureMessage()->getCode()
);
$this->assertSame(
$expect->getFailureMessage()->getData(),
$ex->getFailureMessage()->getData()
);
}
} else {
$v = $typeDef->getValue( 'test', $settings, $options );
$this->assertEquals( $expect, $typeDef->validate( 'test', $v, $settings, $options ) );
}
$conditions = [];
foreach ( $callbacks->getRecordedConditions() as $c ) {
$conditions[] = [ 'code' => $c['message']->getCode(), 'data' => $c['message']->getData() ];
}
$this->assertSame( $expectConds, $conditions );
}
/**
* @return array|Iterable
*/
abstract public function provideValidate();
/**
* @param array $settings
* @param array $expect
* @param array $options
* @dataProvider provideNormalizeSettings
*/
public function testNormalizeSettings( array $settings, array $expect, array $options = [] ) {
$typeDef = $this->getInstance( new SimpleCallbacks( [] ), $options );
$this->assertSame( $expect, $typeDef->normalizeSettings( $settings ) );
}
/**
* @return array|Iterable
*/
public function provideNormalizeSettings() {
return [
'Basic test' => [ [ 'param-foo' => 'bar' ], [ 'param-foo' => 'bar' ] ],
];
}
/**
* @dataProvider provideCheckSettings
* @param array $settings
* @param array $ret Input $ret array
* @param array $expect
* @param array $options Options array
*/
public function testCheckSettings(
array $settings,
array $ret,
array $expect,
array $options = []
): void {
$typeDef = $this->getInstance( new SimpleCallbacks( [] ), $options );
$this->assertEquals( $expect, $typeDef->checkSettings( 'test', $settings, $options, $ret ) );
}
/**
* @return array|Iterable
*/
public function provideCheckSettings() {
return [
'Basic test' => [ [], self::STDRET, self::STDRET ],
];
}
/**
* @param array $settings
* @param array|null $expect
* @param array $options Options array
* @dataProvider provideGetEnumValues
*/
public function testGetEnumValues( array $settings, $expect, array $options = [] ) {
$typeDef = $this->getInstance( new SimpleCallbacks( [] ), $options );
$settings = $typeDef->normalizeSettings( $settings );
$this->assertSame( $expect, $typeDef->getEnumValues( 'test', $settings, $options ) );
}
/**
* @return array|Iterable
*/
public function provideGetEnumValues() {
return [
'Basic test' => [ [], null ],
];
}
/**
* @param mixed $value
* @param string $expect
* @param array $settings
* @param array $options
* @dataProvider provideStringifyValue
*
*/
public function testStringifyValue( $value, $expect, array $settings = [], array $options = [] ) {
$typeDef = $this->getInstance( new SimpleCallbacks( [] ), $options );
$settings = $typeDef->normalizeSettings( $settings );
$this->assertSame( $expect, $typeDef->stringifyValue( 'test', $value, $settings, $options ) );
}
/**
* @return array|Iterable
*/
public function provideStringifyValue() {
return [
'Basic test' => [ 123, '123' ],
];
}
/**
* @param array $settings
* @param array $expectParamInfo
* @param array $expectHelpInfo
* @param array $options
* @dataProvider provideGetInfo
*/
public function testGetInfo(
array $settings, array $expectParamInfo, array $expectHelpInfo, array $options = []
) {
$typeDef = $this->getInstance( new SimpleCallbacks( [] ), $options );
$settings = $typeDef->normalizeSettings( $settings );
$this->assertSame(
$expectParamInfo,
$typeDef->getParamInfo( 'test', $settings, $options )
);
$actual = [];
$constraint = \PHPUnit\Framework\Assert::logicalOr(
$this->isNull(),
$this->isInstanceOf( MessageValue::class )
);
foreach ( $typeDef->getHelpInfo( 'test', $settings, $options ) as $k => $v ) {
$this->assertThat( $v, $constraint );
$actual[$k] = $v ? $v->dump() : null;
}
$this->assertSame( $expectHelpInfo, $actual );
}
/**
* @return array|Iterable
*/
public function provideGetInfo() {
return [
'Basic test' => [ [], [], [] ],
];
}
/**
* Create a ValidationException that's identical to what the typedef object would throw.
* @param string $code Error code, same as what was passed to TypeDef::failure().
* @param mixed $value Parameter value (ie. second argument to TypeDef::validate()).
* @param array $settings Settings object (ie. third argument to TypeDef::validate()).
* @return ValidationException
*/
protected function getValidationException(
string $code, $value, array $settings = []
): ValidationException {
return new ValidationException(
DataMessageValue::new( "paramvalidator-$code", [], $code ),
'test', $value, $settings );
}
}
Back to Directory
File Manager