1<?php
2
3namespace MediaWiki\ParamValidator\TypeDef;
4
5use ApiResult;
6use NamespaceInfo;
7use Wikimedia\ParamValidator\Callbacks;
8use Wikimedia\ParamValidator\ParamValidator;
9use Wikimedia\ParamValidator\TypeDef\EnumDef;
10
11/**
12 * Type definition for namespace types
13 *
14 * A namespace type is an enum type that accepts MediaWiki namespace IDs.
15 *
16 * @since 1.35
17 */
18class NamespaceDef extends EnumDef {
19
20	/**
21	 * (int[]) Additional namespace IDs to recognize.
22	 *
23	 * Generally this will be used to include NS_SPECIAL and/or NS_MEDIA.
24	 */
25	public const PARAM_EXTRA_NAMESPACES = 'param-extra-namespaces';
26
27	/** @var NamespaceInfo */
28	private $nsInfo;
29
30	public function __construct( Callbacks $callbacks, NamespaceInfo $nsInfo ) {
31		parent::__construct( $callbacks );
32		$this->nsInfo = $nsInfo;
33	}
34
35	public function validate( $name, $value, array $settings, array $options ) {
36		if ( !is_int( $value ) && preg_match( '/^[+-]?\d+$/D', $value ) ) {
37			// Convert to int since that's what getEnumValues() returns.
38			$value = (int)$value;
39		}
40
41		return parent::validate( $name, $value, $settings, $options );
42	}
43
44	public function getEnumValues( $name, array $settings, array $options ) {
45		$namespaces = $this->nsInfo->getValidNamespaces();
46		$extra = $settings[self::PARAM_EXTRA_NAMESPACES] ?? [];
47		if ( is_array( $extra ) && $extra !== [] ) {
48			$namespaces = array_merge( $namespaces, $extra );
49		}
50		sort( $namespaces );
51		return $namespaces;
52	}
53
54	public function normalizeSettings( array $settings ) {
55		// Force PARAM_ALL
56		if ( !empty( $settings[ParamValidator::PARAM_ISMULTI] ) ) {
57			$settings[ParamValidator::PARAM_ALL] = true;
58		}
59		return parent::normalizeSettings( $settings );
60	}
61
62	public function checkSettings( string $name, $settings, array $options, array $ret ) : array {
63		$ret = parent::checkSettings( $name, $settings, $options, $ret );
64
65		$ret['allowedKeys'] = array_merge( $ret['allowedKeys'], [
66			self::PARAM_EXTRA_NAMESPACES,
67		] );
68
69		if ( !empty( $settings[ParamValidator::PARAM_ISMULTI] ) &&
70			( $settings[ParamValidator::PARAM_ALL] ?? true ) !== true &&
71			!isset( $ret['issues'][ParamValidator::PARAM_ALL] )
72		) {
73			$ret['issues'][ParamValidator::PARAM_ALL] =
74				'PARAM_ALL cannot be false or a string for namespace-type parameters';
75		}
76
77		$ns = $settings[self::PARAM_EXTRA_NAMESPACES] ?? [];
78		if ( !is_array( $ns ) ) {
79			$type = gettype( $ns );
80		} elseif ( $ns === [] ) {
81			$type = 'integer[]';
82		} else {
83			$types = array_unique( array_map( 'gettype', $ns ) );
84			$type = implode( '|', $types );
85			$type = count( $types ) > 1 ? "($type)[]" : "{$type}[]";
86		}
87		if ( $type !== 'integer[]' ) {
88			$ret['issues'][self::PARAM_EXTRA_NAMESPACES] =
89				"PARAM_EXTRA_NAMESPACES must be an integer[], got $type";
90		}
91
92		return $ret;
93	}
94
95	public function getParamInfo( $name, array $settings, array $options ) {
96		$info = parent::getParamInfo( $name, $settings, $options );
97
98		$info['type'] = 'namespace';
99		$extra = $settings[self::PARAM_EXTRA_NAMESPACES] ?? [];
100		if ( is_array( $extra ) && $extra !== [] ) {
101			$info['extranamespaces'] = array_values( $extra );
102			if ( isset( $options['module'] ) ) {
103				// ApiResult metadata when used with the Action API.
104				ApiResult::setIndexedTagName( $info['extranamespaces'], 'ns' );
105			}
106		}
107
108		return $info;
109	}
110
111}
112