| Current File : /home/jvzmxxx/wiki1/vendor/param-processor/param-processor/src/Param.php |
<?php
namespace ParamProcessor;
use Exception;
use ValueParsers\NullParser;
use ValueParsers\ParseException;
use ValueParsers\ValueParser;
/**
* Parameter class, representing the "instance" of a parameter.
* Holds a ParamDefinition, user provided input (name & value) and processing state.
*
* NOTE: as of version 1.0, this class is for internal use only!
*
* @since 1.0
*
* @licence GNU GPL v2+
* @author Jeroen De Dauw < jeroendedauw@gmail.com >
*/
final class Param implements IParam {
/**
* Indicates whether parameters not found in the criteria list
* should be stored in case they are not accepted. The default is false.
*
* @since 1.0
*
* @var boolean
*/
public static $accumulateParameterErrors = false;
/**
* The original parameter name as provided by the user. This can be the
* main name or an alias.
*
* @since 1.0
*
* @var string
*/
protected $originalName;
/**
* The original value as provided by the user. This is mainly retained for
* usage in error messages when the parameter turns out to be invalid.
*
* @since 1.0
*
* @var string
*/
protected $originalValue;
/**
* The value of the parameter.
*
* @since 1.0
*
* @var mixed
*/
protected $value;
/**
* Keeps track of how many times the parameter has been set by the user.
* This is used to detect overrides and for figuring out a parameter is missing.
*
* @since 1.0
*
* @var integer
*/
protected $setCount = 0;
/**
* List of validation errors for this parameter.
*
* @since 1.0
*
* @var array of ProcessingError
*/
protected $errors = [];
/**
* Indicates if the parameter was set to it's default.
*
* @since 1.0
*
* @var boolean
*/
protected $defaulted = false;
/**
* The definition of the parameter.
*
* @since 1.0
*
* @var ParamDefinition
*/
protected $definition;
/**
* Constructor.
*
* @since 1.0
*
* @param IParamDefinition $definition
*/
public function __construct( IParamDefinition $definition ) {
$this->definition = $definition;
}
/**
* Sets and cleans the original value and name.
* @see IParam::setUserValue
*
* @since 1.0
*
* @param string $paramName
* @param string $paramValue
* @param Options $options
*
* @return boolean
*/
public function setUserValue( $paramName, $paramValue, Options $options ) {
if ( $this->setCount > 0 && !$options->acceptOverriding() ) {
// TODO
return false;
}
else {
$this->originalName = $paramName;
$this->originalValue = $paramValue;
$this->cleanValue( $options );
$this->setCount++;
return true;
}
}
/**
* Sets the value.
*
* @since 1.0
*
* @param mixed $value
*/
public function setValue( $value ) {
$this->value = $value;
}
/**
* Sets the $value to a cleaned value of $originalValue.
*
* TODO: the per-parameter lowercaseing and trimming here needs some thought
*
* @since 1.0
*
* @param Options $options
*/
protected function cleanValue( Options $options ) {
$this->value = $this->originalValue;
$trim = $this->getDefinition()->trimDuringClean();
if ( $trim === true || ( is_null( $trim ) && $options->trimValues() ) ) {
if ( is_string( $this->value ) ) {
$this->value = trim( $this->value );
}
}
if ( $this->definition->isList() ) {
$this->value = explode( $this->definition->getDelimiter(), $this->value );
if ( $trim === true || ( is_null( $trim ) && $options->trimValues() ) ) {
foreach ( $this->value as &$element ) {
if ( is_string( $element ) ) {
$element = trim( $element );
}
}
}
}
$definitionOptions = $this->definition->getOptions();
if ( $options->lowercaseValues() || ( array_key_exists( 'tolower', $definitionOptions ) && $definitionOptions['tolower'] ) ) {
if ( $this->definition->isList() ) {
foreach ( $this->value as &$element ) {
if ( is_string( $element ) ) {
$element = strtolower( $element );
}
}
}
elseif ( is_string( $this->value ) ) {
$this->value = strtolower( $this->value );
}
}
}
/**
* Parameter processing entry point.
* Processes the parameter. This includes parsing, validation and additional formatting.
*
* @since 1.0
*
* @param $definitions array of IParamDefinition
* @param $params array of IParam
* @param Options $options
*
* @throws Exception
*/
public function process( array &$definitions, array $params, Options $options ) {
if ( $this->setCount == 0 ) {
if ( $this->definition->isRequired() ) {
// This should not occur, so throw an exception.
throw new Exception( 'Attempted to validate a required parameter without first setting a value.' );
}
else {
$this->setToDefault();
}
}
else {
$this->parseAndValidate( $options );
}
if ( !$this->hasFatalError() && ( $this->definition->shouldManipulateDefault() || !$this->wasSetToDefault() ) ) {
$this->definition->format( $this, $definitions, $params );
}
}
/**
* @since 1.0
*
* @param Options $options
*
* @return ValueParser
*/
public function getValueParser( Options $options ) {
$parser = $this->definition->getValueParser();
if ( get_class( $parser ) === NullParser::class ) {
$parserType = $options->isStringlyTyped() ? 'string-parser' : 'typed-parser';
// TODO: inject factory
$parserClass = ParamDefinitionFactory::singleton()->getComponentForType( $this->definition->getType(), $parserType );
if ( $parserClass !== NullParser::class ) {
$parser = new $parserClass( new \ValueParsers\ParserOptions() );
}
}
return $parser;
}
/**
* @since 1.0
*
* @param Options $options
*/
protected function parseAndValidate( Options $options ) {
$parser = $this->getValueParser( $options );
if ( $this->definition->isList() ) {
$values = [];
foreach ( $this->getValue() as $value ) {
$parsedValue = $this->parseAndValidateValue( $parser, $value );
if ( is_array( $parsedValue ) ) {
$values[] = $parsedValue[0];
}
}
$this->value = $values;
}
else {
$parsedValue = $this->parseAndValidateValue( $parser, $this->getValue() );
if ( is_array( $parsedValue ) ) {
$this->value = $parsedValue[0];
}
}
$this->setToDefaultIfNeeded();
}
/**
* Parses and validates the provided with with specified parser.
* The result is returned in an array on success. On fail, false is returned.
* The result is wrapped in an array since we need to be able to distinguish
* between the method returning false and the value being false.
*
* Parsing and validation errors get added to $this->errors.
*
* @since 1.0
*
* @param ValueParser $parser
* @param mixed $value
*
* @return array|bool
*/
protected function parseAndValidateValue( ValueParser $parser, $value ) {
try {
$value = $parser->parse( $value );
}
catch ( ParseException $parseException ) {
$this->registerProcessingError( $parseException->getMessage() );
return false;
}
if ( $value instanceof \DataValues\DataValue ) {
$value = $value->getValue();
}
$this->validateValue( $value );
return [ $value ];
}
/**
* @since 1.0
*
* @param string $message
*/
protected function registerProcessingError( $message ) {
$this->errors[] = $this->newProcessingError( $message );
}
/**
* @since 1.0
*
* @param string $message
*
* @return ProcessingError
*/
protected function newProcessingError( $message ) {
$severity = $this->isRequired() ? ProcessingError::SEVERITY_FATAL : ProcessingError::SEVERITY_NORMAL;
return new ProcessingError( $message, $severity );
}
/**
* @since 1.0
*
* @param mixed $value
*/
protected function validateValue( $value ) {
$validationCallback = $this->definition->getValidationCallback();
if ( $validationCallback !== null && $validationCallback( $value ) !== true ) {
$this->registerProcessingError( 'Validation callback failed' );
}
else {
$validator = $this->definition->getValueValidator();
$validator->setOptions( $this->definition->getOptions() ); // TODO
$validationResult = $validator->validate( $value );
if ( !$validationResult->isValid() ) {
foreach ( $validationResult->getErrors() as $error ) {
$this->registerProcessingError( $error->getText() );
}
}
}
}
/**
* Sets the parameter value to the default if needed.
*
* @since 1.0
*/
protected function setToDefaultIfNeeded() {
if ( $this->errors !== [] && !$this->hasFatalError() ) {
$this->setToDefault();
}
}
/**
* Returns the original use-provided name.
*
* @since 1.0
*
* @throws Exception
* @return string
*/
public function getOriginalName() {
if ( $this->setCount == 0 ) {
throw new Exception( 'No user input set to the parameter yet, so the original name does not exist' );
}
return $this->originalName;
}
/**
* Returns the original use-provided value.
*
* @since 1.0
*
* @throws Exception
* @return string
*/
public function getOriginalValue() {
if ( $this->setCount == 0 ) {
throw new Exception( 'No user input set to the parameter yet, so the original value does not exist' );
}
return $this->originalValue;
}
/**
* Returns all validation errors that occurred so far.
*
* @since 1.0
*
* @return ProcessingError[]
*/
public function getErrors() {
return $this->errors;
}
/**
* Sets the parameter value to the default.
*
* @since 1.0
*/
protected function setToDefault() {
$this->defaulted = true;
$this->value = $this->definition->getDefault();
}
/**
* Gets if the parameter was set to it's default.
*
* @since 1.0
*
* @return boolean
*/
public function wasSetToDefault() {
return $this->defaulted;
}
/**
* Returns false when there are no fatal errors or an ProcessingError when one is found.
*
* @return mixed false or ProcessingError
*/
public function hasFatalError() {
foreach ( $this->errors as $error ) {
if ( $error->isFatal() ) {
return true;
}
}
return false;
}
/**
* Returns the IParamDefinition this IParam was constructed from.
*
* @since 1.0
*
* @return IParamDefinition
*/
public function getDefinition() {
return $this->definition;
}
/**
* Returns the parameters value.
*
* @since 1.0
*
* @return mixed
*/
public function &getValue() {
return $this->value;
}
/**
* Returns if the parameter is required or not.
*
* @since 1.0
*
* @return boolean
*/
public function isRequired() {
return $this->definition->isRequired();
}
/**
* Returns if the name of the parameter.
*
* @since 1.0
*
* @return boolean
*/
public function getName() {
return $this->definition->getName();
}
/**
* Returns the parameter name aliases.
*
* @since 1.0
*
* @return array
*/
public function getAliases() {
return $this->definition->getAliases();
}
}