Current File : /home/jvzmxxx/wiki/extensions/Wikibase/repo/includes/Api/ParseValue.php
<?php

namespace Wikibase\Repo\Api;

use ApiBase;
use ApiMain;
use ApiResult;
use DataTypes\DataTypeFactory;
use DataValues\DataValue;
use Exception;
use LogicException;
use OutOfBoundsException;
use Status;
use ValueParsers\ParseException;
use ValueParsers\ParserOptions;
use ValueParsers\ValueParser;
use ValueValidators\Error;
use ValueValidators\NullValidator;
use ValueValidators\ValueValidator;
use Wikibase\Repo\DataTypeValidatorFactory;
use Wikibase\Repo\Localizer\ExceptionLocalizer;
use Wikibase\Repo\Validators\CompositeValidator;
use Wikibase\Repo\Validators\ValidatorErrorLocalizer;
use Wikibase\Repo\ValueParserFactory;
use Wikibase\Repo\WikibaseRepo;

/**
 * API module for using value parsers.
 *
 * @since 0.1
 *
 * @license GPL-2.0+
 * @author Jeroen De Dauw < jeroendedauw@gmail.com >
 * @author Daniel Kinzler
 * @author Addshore
 */
class ParseValue extends ApiBase {

	/**
	 * @var DataTypeFactory
	 */
	private $dataTypeFactory;

	/**
	 * @var ValueParserFactory
	 */
	private $valueParserFactory;

	/**
	 * @var DataTypeValidatorFactory
	 */
	private $dataTypeValidatorFactory;

	/**
	 * @var ValidatorErrorLocalizer
	 */
	private $validatorErrorLocalizer;

	/**
	 * @var ExceptionLocalizer
	 */
	private $exceptionLocalizer;

	/**
	 * @var ApiErrorReporter
	 */
	private $errorReporter;

	/**
	 * @param ApiMain $mainModule
	 * @param string $moduleName
	 * @param string $modulePrefix
	 *
	 * @see ApiBase::__construct
	 */
	public function __construct( ApiMain $mainModule, $moduleName, $modulePrefix = '' ) {
		parent::__construct( $mainModule, $moduleName, $modulePrefix );

		$wikibaseRepo = WikibaseRepo::getDefaultInstance();
		$apiHelperFactory = $wikibaseRepo->getApiHelperFactory( $this->getContext() );

		$this->setServices(
			$wikibaseRepo->getDataTypeFactory(),
			$wikibaseRepo->getValueParserFactory(),
			$wikibaseRepo->getDataTypeValidatorFactory(),
			$wikibaseRepo->getExceptionLocalizer(),
			$wikibaseRepo->getValidatorErrorLocalizer(),
			$apiHelperFactory->getErrorReporter( $this )
		);
	}

	public function setServices(
		DataTypeFactory $dataTypeFactory,
		ValueParserFactory $valueParserFactory,
		DataTypeValidatorFactory $dataTypeValidatorFactory,
		ExceptionLocalizer $exceptionLocalizer,
		ValidatorErrorLocalizer $validatorErrorLocalizer,
		ApiErrorReporter $errorReporter
	) {
		$this->dataTypeFactory = $dataTypeFactory;
		$this->valueParserFactory = $valueParserFactory;
		$this->dataTypeValidatorFactory = $dataTypeValidatorFactory;
		$this->exceptionLocalizer = $exceptionLocalizer;
		$this->validatorErrorLocalizer = $validatorErrorLocalizer;
		$this->errorReporter = $errorReporter;
	}

	/**
	 * @see ApiBase::execute
	 *
	 * @since 0.1
	 */
	public function execute() {
		$parser = $this->getParser();

		$results = array();

		$params = $this->extractRequestParams();
		$validator = $params['validate'] ? $this->getValidator() : null;

		foreach ( $params['values'] as $value ) {
			$results[] = $this->parseStringValue( $parser, $value, $validator );
		}

		$this->outputResults( $results );
	}

	/**
	 * @return ValueParser
	 * @throws LogicException
	 */
	private function getParser() {
		$params = $this->extractRequestParams();

		$options = $this->getOptionsObject( $params['options'] );

		// Parsers are registered by datatype.
		// Note: parser used to be addressed by a name independent of datatype, using the 'parser'
		// parameter. For backwards compatibility, parsers are also registered under their old names
		// in $wgValueParsers, and thus in the ValueParserFactory.
		$name = $params['datatype'] ?: $params['parser'];

		if ( empty( $name ) ) {
			// If neither 'datatype' not 'parser' is given, tell the client to use 'datatype'.
			$this->errorReporter->dieError( 'No datatype given', 'param-illegal' );
		}

		try {
			$parser = $this->valueParserFactory->newParser( $name, $options );
			return $parser;
		} catch ( OutOfBoundsException $ex ) {
			$this->errorReporter->dieError( "Unknown datatype `$name`", 'unknown-datatype' );
			throw new LogicException( 'dieError() did not throw an exception' );
		}
	}

	/**
	 * @return ValueValidator
	 */
	private function getValidator() {
		$params = $this->extractRequestParams();

		$name = $params['datatype'];

		if ( empty( $name ) ) {
			// 'datatype' parameter is required for validation.
			$this->errorReporter->dieError( 'No datatype given', 'param-illegal' );
		}

		// Note: For unknown datatype, we'll get an empty list.
		$validators = $this->dataTypeValidatorFactory->getValidators( $name );
		return $this->wrapValidators( $validators );
	}

	/**
	 * @param ValueValidator[] $validators
	 *
	 * @return ValueValidator
	 */
	private function wrapValidators( array $validators ) {
		if ( count( $validators ) === 0 ) {
			return new NullValidator();
		} elseif ( count( $validators ) === 1 ) {
			return reset( $validators );
		} else {
			return new CompositeValidator( $validators, true );
		}
	}

	/**
	 * @param ValueParser $parser
	 * @param string $value
	 * @param ValueValidator|null $validator
	 *
	 * @return array
	 */
	private function parseStringValue( ValueParser $parser, $value, ValueValidator $validator = null ) {
		$result = array(
			'raw' => $value
		);

		try {
			$parseResult = $parser->parse( $value );
		} catch ( ParseException $parseError ) {
			$this->addParseErrorToResult( $result, $parseError );
			return $result;
		}

		if ( $parseResult instanceof DataValue ) {
			$result['value'] = $parseResult->getArrayValue();
			$result['type'] = $parseResult->getType();
		} else {
			$result['value'] = $parseResult;
		}

		if ( $validator ) {
			$validatorResult = $validator->validate( $parseResult );
			$validationStatus = $this->validatorErrorLocalizer->getResultStatus( $validatorResult );

			$result['valid'] = $validationStatus->isOK();

			if ( !$validationStatus->isOK() ) {
				$result['error'] = 'ValidationError';
				$this->errorReporter->addStatusToResult( $validationStatus, $result );
				$result['validation-errors'] = $this->getValidatorErrorCodes( $validatorResult->getErrors() );
			}
		}

		return $result;
	}

	/**
	 * @param Error[] $errors
	 *
	 * @return string[]
	 */
	private function getValidatorErrorCodes( array $errors ) {
		return array_map(
			function ( Error $error ) {
				return $error->getCode();
			},
			$errors
		);
	}

	private function addParseErrorToResult( array &$result, ParseException $parseError ) {
		$result['error'] = get_class( $parseError );

		$result['error-info'] = $parseError->getMessage();
		$result['expected-format'] = $parseError->getExpectedFormat();

		$status = $this->getExceptionStatus( $parseError );
		$this->errorReporter->addStatusToResult( $status, $result );
	}

	private function outputResults( array $results ) {
		ApiResult::setIndexedTagName( $results, 'result' );

		$this->getResult()->addValue(
			null,
			'results',
			$results
		);
	}

	/**
	 * @param string|null $optionsParam
	 *
	 * @return ParserOptions
	 */
	private function getOptionsObject( $optionsParam ) {
		$parserOptions = new ParserOptions();
		$parserOptions->setOption( ValueParser::OPT_LANG, $this->getLanguage()->getCode() );

		if ( is_string( $optionsParam ) && $optionsParam !== '' ) {
			$options = json_decode( $optionsParam, true );

			if ( !is_array( $options ) ) {
				$this->errorReporter->dieError( 'Malformed options parameter', 'malformed-options' );
			}

			foreach ( $options as $name => $value ) {
				$parserOptions->setOption( $name, $value );
			}
		}

		return $parserOptions;
	}

	/**
	 * Returns a Status object representing the given exception using a localized message.
	 *
	 * @note: The returned Status will always be fatal, that is, $status->isOk() will return false.
	 *
	 * @see getExceptionMessage().
	 *
	 * @param Exception $error
	 *
	 * @return Status
	 */
	protected function getExceptionStatus( Exception $error ) {
		$msg = $this->exceptionLocalizer->getExceptionMessage( $error );
		$status = Status::newFatal( $msg );
		$status->setResult( false, $error->getMessage() );

		return $status;
	}

	/**
	 * @see ApiBase::getAllowedParams
	 */
	public function getAllowedParams() {
		return array(
			'datatype' => array(
				ApiBase::PARAM_TYPE => $this->dataTypeFactory->getTypeIds(),

				// Currently, the deprecated 'parser' parameter may be used as an
				// alternative to the 'datatype' parameter. Once 'parser' is removed,
				// 'datatype' should be required.
				ApiBase::PARAM_REQUIRED => false,
			),
			'parser' => array(
				self::PARAM_TYPE => $this->valueParserFactory->getParserIds(),

				// Use 'datatype' instead!
				// NOTE: when removing the 'parser' parameter, set 'datatype' to PARAM_REQUIRED
				self::PARAM_DEPRECATED => true,
				self::PARAM_REQUIRED => false,
			),
			'values' => array(
				self::PARAM_TYPE => 'string',
				self::PARAM_REQUIRED => true,
				self::PARAM_ISMULTI => true,
			),
			'options' => array(
				self::PARAM_TYPE => 'text',
				self::PARAM_REQUIRED => false,
			),
			'validate' => array(
				ApiBase::PARAM_TYPE => 'boolean',
			),
		);
	}

	/**
	 * @see ApiBase::getExamplesMessages
	 */
	protected function getExamplesMessages() {
		return array(
			'action=wbparsevalue&datatype=string&values=foo|bar' =>
				'apihelp-wbparsevalue-example-1',
			'action=wbparsevalue&datatype=time&values=1994-02-08&options={"precision":9}' =>
				'apihelp-wbparsevalue-example-2',
			'action=wbparsevalue&datatype=time&validate&values=1994-02-08&options={"precision":14}' =>
				'apihelp-wbparsevalue-example-3',
		);
	}

}