options[$name] = $option; } /** * Remove an option being handled by this FormOptions instance. This is the inverse of add(). * * @param string $name Request parameter name */ public function delete( $name ) { $this->validateName( $name, true ); unset( $this->options[$name] ); } /** * Used to find out which type the data is. All types are defined in the 'Type constants' section * of this class. * * Detection of the INTNULL type is not supported; INT will be assumed if the data is an integer, * MWException will be thrown if it's null. * * @param mixed $data Value to guess the type for * @throws MWException If unable to guess the type * @return int Type constant */ public static function guessType( $data ) { if ( is_bool( $data ) ) { return self::BOOL; } elseif ( is_int( $data ) ) { return self::INT; } elseif ( is_float( $data ) ) { return self::FLOAT; } elseif ( is_string( $data ) ) { return self::STRING; } elseif ( is_array( $data ) ) { return self::ARR; } else { throw new MWException( 'Unsupported datatype' ); } } # Handling values /** * Verify that the given option name exists. * * @param string $name Option name * @param bool $strict Throw an exception when the option doesn't exist instead of returning false * @throws MWException * @return bool True if the option exists, false otherwise */ public function validateName( $name, $strict = false ) { if ( !isset( $this->options[$name] ) ) { if ( $strict ) { throw new MWException( "Invalid option $name" ); } else { return false; } } return true; } /** * Use to set the value of an option. * * @param string $name Option name * @param mixed $value Value for the option * @param bool $force Whether to set the value when it is equivalent to the default value for this * option (default false). */ public function setValue( $name, $value, $force = false ) { $this->validateName( $name, true ); if ( !$force && $value === $this->options[$name]['default'] ) { // null default values as unchanged $this->options[$name]['value'] = null; } else { $this->options[$name]['value'] = $value; } } /** * Get the value for the given option name. Uses getValueReal() internally. * * @param string $name Option name * @return mixed */ public function getValue( $name ) { $this->validateName( $name, true ); return $this->getValueReal( $this->options[$name] ); } /** * Return current option value, based on a structure taken from $options. * * @param array $option Array structure describing the option * @return mixed Value, or the default value if it is null */ protected function getValueReal( $option ) { if ( $option['value'] !== null ) { return $option['value']; } else { return $option['default']; } } /** * Delete the option value. * This will make future calls to getValue() return the default value. * @param string $name Option name */ public function reset( $name ) { $this->validateName( $name, true ); $this->options[$name]['value'] = null; } /** * Get the value of given option and mark it as 'consumed'. Consumed options are not returned * by getUnconsumedValues(). * * @see consumeValues() * @throws MWException If the option does not exist * @param string $name Option name * @return mixed Value, or the default value if it is null */ public function consumeValue( $name ) { $this->validateName( $name, true ); $this->options[$name]['consumed'] = true; return $this->getValueReal( $this->options[$name] ); } /** * Get the values of given options and mark them as 'consumed'. Consumed options are not returned * by getUnconsumedValues(). * * @see consumeValue() * @throws MWException If any option does not exist * @param array $names Array of option names as strings * @return array Array of option values, or the default values if they are null */ public function consumeValues( $names ) { $out = []; foreach ( $names as $name ) { $this->validateName( $name, true ); $this->options[$name]['consumed'] = true; $out[] = $this->getValueReal( $this->options[$name] ); } return $out; } /** * @see validateBounds() * @param string $name * @param int $min * @param int $max */ public function validateIntBounds( $name, $min, $max ) { $this->validateBounds( $name, $min, $max ); } /** * Constrain a numeric value for a given option to a given range. The value will be altered to fit * in the range. * * @since 1.23 * * @param string $name Option name * @param int|float $min Minimum value * @param int|float $max Maximum value * @throws MWException If option is not of type INT */ public function validateBounds( $name, $min, $max ) { $this->validateName( $name, true ); $type = $this->options[$name]['type']; if ( $type !== self::INT && $type !== self::FLOAT ) { throw new MWException( "Option $name is not of type INT or FLOAT" ); } $value = $this->getValueReal( $this->options[$name] ); $value = max( $min, min( $max, $value ) ); $this->setValue( $name, $value ); } /** * Get all remaining values which have not been consumed by consumeValue() or consumeValues(). * * @param bool $all Whether to include unchanged options (default: false) * @return array */ public function getUnconsumedValues( $all = false ) { $values = []; foreach ( $this->options as $name => $data ) { if ( !$data['consumed'] ) { if ( $all || $data['value'] !== null ) { $values[$name] = $this->getValueReal( $data ); } } } return $values; } /** * Return options modified as an array ( name => value ) * @return array */ public function getChangedValues() { $values = []; foreach ( $this->options as $name => $data ) { if ( $data['value'] !== null ) { $values[$name] = $data['value']; } } return $values; } /** * Format options to an array ( name => value ) * @return array */ public function getAllValues() { $values = []; foreach ( $this->options as $name => $data ) { $values[$name] = $this->getValueReal( $data ); } return $values; } # Reading values /** * Fetch values for all options (or selected options) from the given WebRequest, making them * available for accessing with getValue() or consumeValue() etc. * * @param WebRequest $r The request to fetch values from * @param array|null $optionKeys Which options to fetch the values for (default: * all of them). Note that passing an empty array will also result in * values for all keys being fetched. * @throws MWException If the type of any option is invalid */ public function fetchValuesFromRequest( WebRequest $r, $optionKeys = null ) { if ( !$optionKeys ) { $optionKeys = array_keys( $this->options ); } foreach ( $optionKeys as $name ) { $default = $this->options[$name]['default']; $type = $this->options[$name]['type']; switch ( $type ) { case self::BOOL: $value = $r->getBool( $name, $default ); break; case self::INT: $value = $r->getInt( $name, $default ); break; case self::FLOAT: $value = $r->getFloat( $name, $default ); break; case self::STRING: $value = $r->getText( $name, $default ); break; case self::INTNULL: $value = $r->getIntOrNull( $name ); break; case self::ARR: $value = $r->getArray( $name ); break; default: throw new MWException( 'Unsupported datatype' ); } if ( $value !== null ) { $this->options[$name]['value'] = $value === $default ? null : $value; } } } /** @name ArrayAccess functions * These functions implement the ArrayAccess PHP interface. * @see https://www.php.net/manual/en/class.arrayaccess.php */ /* @{ */ /** * Whether the option exists. * @param string $name * @return bool */ public function offsetExists( $name ) { return isset( $this->options[$name] ); } /** * Retrieve an option value. * @param string $name * @return mixed */ public function offsetGet( $name ) { return $this->getValue( $name ); } /** * Set an option to given value. * @param string $name * @param mixed $value */ public function offsetSet( $name, $value ) { $this->setValue( $name, $value ); } /** * Delete the option. * @param string $name */ public function offsetUnset( $name ) { $this->delete( $name ); } /* @} */ }