1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330
|
<?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.
*
* @since 1.34
* @unstable for use in extensions. Intended to become stable to extend, at
* least for use in MediaWiki, which already defines some subclasses.
*/
abstract class TypeDef {
/**
* @unstable Temporarily log warnings to detect misbehaving clients (T305973)
*/
public const OPT_LOG_BAD_TYPES = 'log-bad-types';
/**
* Option that instructs TypeDefs to enforce the native type of parameter
* values, instead of allowing string values as input. This is intended for
* use with values coming from a JSON request body, and may accommodate for
* differences between the type system of PHP and JSON.
*/
public const OPT_ENFORCE_JSON_TYPES = 'enforce-json-types';
/** @var Callbacks */
protected $callbacks;
/**
* @stable to call
*
* @param Callbacks $callbacks
*/
public function __construct( Callbacks $callbacks ) {
$this->callbacks = $callbacks;
}
/**
* Whether the value may be an array.
* Note that this is different from multi-value.
* This should only return true if each value can be an array.
* @since 1.41
* @stable to override
* @return bool
*/
public function supportsArrays() {
return false;
}
/**
* Fails if $value is not a string.
*
* @param string $name Parameter name being validated.
* @param mixed $value Value being validated.
* @param array $settings Parameter settings array.
* @param array $options Options array.
*
* @return void
*/
protected function failIfNotString(
string $name,
$value,
array $settings,
array $options
): void {
if ( !is_string( $value ) ) {
$this->fatal(
$this->failureMessage( 'needstring' )
->params( gettype( $value ) ),
$name, $value, $settings, $options
);
}
}
/**
* Throw a ValidationException.
* This is a wrapper for failure() which explicitly declares that it
* never returns, which is useful to static analysis tools like Phan.
*
* 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.
* @return never
* @throws ValidationException always
*/
protected function fatal(
$failure, $name, $value, array $settings, array $options
) {
$this->failure( $failure, $name, $value, $settings, $options );
}
/**
* 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 ) {
if ( is_array( $value ) ) {
return '(array)';
}
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 [];
}
}
|