| <?php |
| /** |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| * |
| * @copyright Copyright (C) 2005-2010 eZ Systems AS. All rights reserved. |
| * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0 |
| * @version //autogentag// |
| * @filesource |
| * @package UserInput |
| */ |
| |
| /** |
| * Provides access to form variables. |
| * |
| * This class allows you to retrieve input variables from the request in a safe |
| * way, by applying filters to allow only wanted data into your application. It |
| * works by passing an array that describes your form definition to the |
| * constructor of the class. The constructor will then initialize the class |
| * with properties that contain the value of your request's input fields. |
| * |
| * Example: |
| * <code> |
| * <?php |
| * if ( ezcInputForm::hasGetData() ) |
| * { |
| * $definition = array( |
| * 'fieldname' => new ezcInputFormDefinitionElement( |
| * ezcInputFormDefinitionElement::REQUIRED, 'filtername' |
| * ), |
| * 'textfield' => new ezcInputFormDefinitionElement( |
| * ezcInputFormDefinitionElement::OPTIONAL, 'string' |
| * ), |
| * 'integer1' => new ezcInputFormDefinitionElement( |
| * ezcInputFormDefinitionElement::REQUIRED, 'int', |
| * array( 'min_range' => 0, 'max_range' => 42 ) |
| * ), |
| * 'xmlfield' => new ezcInputFormDefinitionElement( |
| * ezcInputFormDefinitionElement::REQUIRED, 'unsafe_raw' |
| * ), |
| * 'special' => new ezcInputFormDefinitionElement( |
| * ezcInputFormDefinitionElement::OPTIONAL, 'callback', |
| * array( 'ezcInputFilter', 'special' ) |
| * ), |
| * ); |
| * $form = new ezcInputForm( INPUT_GET, $definition ); |
| * if ( $form->hasInputField( 'textfield' ) ) // check for optional field |
| * { |
| * $text = $form->textfield; |
| * } |
| * |
| * try |
| * { |
| * $xml = $form->xmlfield; // Uses dynamic properties through __get(). |
| * $field = $form->fieldname; |
| * $int = $form->integer1; |
| * } |
| * catch ( ezcInputFormException $e ) |
| * { |
| * // one of the required fields didn't have valid data. |
| * $invalidProperties = $form->getInvalidProperties(); |
| * |
| * // Retrieve RAW data for invalid properties so that we can fill in the |
| * // forms online with this RAW data again - Make sure to escape it on |
| * // output though, but that should be done for all data anyway. |
| * if ( in_array( 'xmlfield', $invalidProperties ) ) |
| * { |
| * $xml = $form->getUnsafeRawData( 'xmlfield' ); |
| * } |
| * } |
| * |
| * // Checking optional fields |
| * foreach ( $form->getOptionalProperties() as $property ) |
| * { |
| * $name = "property_{$property}"; |
| * if ( $form->hasValidData( $property ) ) |
| * { |
| * $$name = $form->$property; |
| * } |
| * } |
| * } |
| * ?> |
| * </code> |
| * |
| * @property-read string $formFields |
| * There is a read-only property for each field that is defined |
| * as input field. |
| * |
| * @package UserInput |
| * @version //autogentag// |
| * @mainclass |
| */ |
| class ezcInputForm |
| { |
| /** |
| * @var VALID is used in the $properties array to record whether the data |
| * in a specific input variable contained valid data according |
| * to the filter. |
| */ |
| const VALID = 0; |
| |
| /** |
| * @var INVALID is used in the $properties array to record whether the data |
| * in a specific input variable contained valid data according |
| * to the filter. |
| */ |
| const INVALID = 1; |
| |
| const DEF_NO_ARRAY = 1; |
| const DEF_EMPTY = 2; |
| const DEF_ELEMENT_NO_DEFINITION_ELEMENT = 3; |
| const DEF_NOT_REQUIRED_OR_OPTIONAL = 5; |
| const DEF_WRONG_FLAGS_TYPE = 6; |
| const DEF_UNSUPPORTED_FILTER = 7; |
| const DEF_FIELD_NAME_BROKEN = 8; |
| |
| /** |
| * Contains the definition for this form (as passed in the constructor). |
| * @var array(string=>ezcInputFormDefinitionElement) |
| */ |
| private $definition; |
| |
| /** |
| * Contains a list of all retrieved properties and their status. The key |
| * for each array element is the field name, and the value associated with |
| * this key is one of the constants VALID or INVALID. |
| * @var array |
| */ |
| private $properties; |
| |
| /** |
| * Contains the values of the input variables. The key for each array |
| * element is the field name, and the value associated with this key is the |
| * property's value. This array does not have an entry for input fields |
| * that do not have valid data. |
| * @var array |
| */ |
| protected $propertyValues; |
| |
| /** |
| * Contains the input source to be used. |
| * @var int |
| */ |
| private $inputSource; |
| |
| /** |
| * Whether all the input elements are valid |
| */ |
| private $allElementsValid; |
| |
| /** |
| * Constructs a new ezcInputForm for $inputSource with $definition. |
| * |
| * This method constructs a new ezcInputForm with three parameters. The |
| * $inputSource parameter selects the input source and should be one of the |
| * constants INPUT_GET, INPUT_POST or INPUT_COOKIE. The $definition |
| * parameter is an array of ezcInputFormDefinitionElement items and |
| * determines which input variables make up this form (see the example at |
| * the top of this class). The last parameter, $characterEncoding is the |
| * character encoding to use while retrieving input variable data. This |
| * parameter has currently no function as it will depend on PHP 6 |
| * functionality which does not exist yet in the input filter extension. |
| * |
| * @throws ezcInputFormVariableMissingException when one of the required |
| * input variables is missing. |
| * @throws ezcInputFormInvalidDefinitionException when the definition array |
| * is invalid or when the input source was invalid. |
| * |
| * @param int $inputSource |
| * @param array(ezcInputFormDefinitionElement) $definition |
| * @param string $characterEncoding |
| */ |
| public function __construct( $inputSource, $definition, $characterEncoding = null ) |
| { |
| if ( ( $returnValue = ezcInputForm::validateDefinition( $definition ) ) !== true ) |
| { |
| throw new ezcInputFormInvalidDefinitionException( $returnValue[1] ); |
| } |
| $this->definition = $definition; |
| $this->inputSource = $inputSource; |
| |
| $this->parseInput(); |
| } |
| |
| /** |
| * Returns whether there is GET data available |
| * |
| * @return bool True if there is GET data available |
| */ |
| static public function hasGetData() |
| { |
| return count( $_GET ) > 0; |
| } |
| |
| /** |
| * Returns whether there is POST data available |
| * |
| * @return bool True if there is POST data available |
| */ |
| static public function hasPostData() |
| { |
| return count( $_POST ) > 0; |
| } |
| |
| /** |
| * Parses the input according to the definition array. |
| * |
| * @throws ezcInputFormInvalidDefinitionException when one of the required |
| * input variables is missing or when the input source was invalid. |
| */ |
| private function parseInput() |
| { |
| $this->allElementsValid = true; |
| |
| if ( !in_array( $this->inputSource, array( INPUT_GET, INPUT_POST, INPUT_COOKIE ) ) ) |
| { |
| throw new ezcInputFormWrongInputSourceException( $this->inputSource ); |
| } |
| |
| foreach ( $this->definition as $elementName => $inputElement ) |
| { |
| $hasVariable = filter_has_var( $this->inputSource, $elementName ); |
| if ( ! $hasVariable ) |
| { |
| if ( $inputElement->type === ezcInputFormDefinitionElement::REQUIRED ) |
| { |
| throw new ezcInputFormVariableMissingException( $elementName ); |
| } |
| else |
| { |
| $this->properties[$elementName] = ezcInputForm::INVALID; |
| $this->allElementsValid = false; |
| continue; |
| } |
| } |
| |
| $flags = FILTER_NULL_ON_FAILURE | $inputElement->flags; |
| $value = filter_input( $this->inputSource, $elementName, filter_id( $inputElement->filterName ), array( 'options' => $inputElement->options, 'flags' => $flags ) ); |
| |
| if ( $value !== null ) |
| { |
| $this->properties[$elementName] = ezcInputForm::VALID; |
| $this->propertyValues[$elementName] = $value; |
| } |
| else |
| { |
| $this->properties[$elementName] = ezcInputForm::INVALID; |
| $this->allElementsValid = false; |
| } |
| } |
| } |
| |
| /** |
| * Validates the definition array $definition. |
| * |
| * Before reading the values from the input source, the definition array |
| * can be validated by this method to check whether all necessary |
| * elements are correctly formed. |
| * |
| * With the following code you can check whether the definition is valid: |
| * <code> |
| * <?php |
| * if ( ( $returnValue = ezcInputForm::validateDefinition( $definition ) ) !== true ) |
| * { |
| * // do something with the error type and error message in $returnValue |
| * } |
| * else |
| * { |
| * // the definition was correct |
| * } |
| * ?> |
| * </code> |
| * |
| * @param array $definition |
| * @return array|bool If the definition is correct the method returns |
| * boolean true. When an error is found the function |
| * returns an array where the first element is the type, |
| * and the second element the error message. |
| */ |
| static public function validateDefinition( $definition ) |
| { |
| // The definition parameter should be an array |
| if ( !is_array( $definition ) ) |
| { |
| return array( ezcInputForm::DEF_NO_ARRAY, "The definition array is not an array" ); |
| } |
| |
| // There should be atleast one element |
| if ( count( $definition ) === 0 ) |
| { |
| return array( ezcInputForm::DEF_EMPTY, "The definition array is empty" ); |
| } |
| |
| foreach ( $definition as $name => $element ) |
| { |
| // Each element should be an ezcInputFormDefinitionElement |
| if ( !$element instanceof ezcInputFormDefinitionElement ) |
| { |
| return array( ezcInputForm::DEF_ELEMENT_NO_DEFINITION_ELEMENT, "The definition for element '{$name}' is not an ezcInputFormDefinitionElement" ); |
| } |
| |
| // The first value in an element should be REQUIRED or OPTIONAL |
| if ( !in_array( $element->type, array( ezcInputFormDefinitionElement::OPTIONAL, ezcInputFormDefinitionElement::REQUIRED ), true ) ) |
| { |
| return array( ezcInputForm::DEF_NOT_REQUIRED_OR_OPTIONAL, "The first element definition for element '{$name}' is not ezcInputFormDefinitionElement::OPTIONAL or ezcInputFormDefinitionElement::REQUIRED" ); |
| } |
| |
| // The options should either be an array, a string, or an int |
| if ( $element->options !== null ) |
| { |
| $filterOptionsType = gettype( $element->options ); |
| if ( !in_array( $filterOptionsType, array( 'integer', 'string', 'array' ) ) ) |
| { |
| return array( ezcInputForm::DEF_WRONG_FLAGS_TYPE, "The options to the definition for element '{$name}' is not of type integer, string or array" ); |
| } |
| |
| // A callback filter should have the form "string" or "array(string, string)" |
| if ( $element->filterName == 'callback' ) |
| { |
| if ( $filterOptionsType == 'integer' ) |
| { |
| return array( ezcInputForm::DEF_WRONG_FLAGS_TYPE, "The callback filter for element '{$name}' should not be an integer" ); |
| } |
| if ( $filterOptionsType == 'array' ) |
| { |
| if ( count( $element->options ) != 2 ) |
| { |
| return array( ezcInputForm::DEF_WRONG_FLAGS_TYPE, "The array parameter for the callback filter for element '{$name}' should have exactly two elements" ); |
| } |
| if ( gettype( $element->options[0] ) != 'string' || gettype( $element->options[1] ) != 'string' ) |
| { |
| return array( ezcInputForm::DEF_WRONG_FLAGS_TYPE, "The array elements for the callback filter for element '{$name}' should both be a string" ); |
| } |
| } |
| } |
| } |
| |
| // The options should either be an int |
| if ( $element->flags !== null ) |
| { |
| if ( gettype( $element->flags ) !== 'integer' ) |
| { |
| return array( ezcInputForm::DEF_WRONG_FLAGS_TYPE, "The flags to the definition for element '{$name}' is not of type integer, string or array" ); |
| } |
| } |
| |
| // The filter should be an existing filter |
| if ( !in_array( $element->filterName, filter_list() ) ) |
| { |
| $filters = join( ', ', filter_list() ); |
| return array( ezcInputForm::DEF_UNSUPPORTED_FILTER, "The filter '{$element->filterName}' for element '{$name}' does not exist. Pick one of: $filters" ); |
| } |
| |
| // The input field name should have a sane format |
| if ( gettype( $name ) != 'string' ) |
| { |
| return array( ezcInputForm::DEF_FIELD_NAME_BROKEN, "The element name '{$name}' is not a string" ); |
| } |
| if (! preg_match( '@^[a-z][a-z0-9_]*$@i', $name ) ) |
| { |
| return array( ezcInputForm::DEF_FIELD_NAME_BROKEN, "The element name '{$name}' has an unsupported format. It should start with an a-z and followed by a-z0-9_" ); |
| } |
| |
| } |
| return true; |
| } |
| |
| /** |
| * This function is called when a variable is assigned to a magic property. |
| * |
| * When the value of a property is requested this function checks with the |
| * $properties array whether it contains valid data or not. If there is no |
| * valid data, the UserInputInValidData exception is thrown, otherwise the |
| * function returns the value associated with the input variable. |
| * |
| * @throws ezcInputFormInvalidDataException when trying to read a property |
| * which has no valid data. |
| * @throws ezcInputFormUnknownFieldException when a property is being |
| * accessed which is not defined in the definition array. |
| * |
| * @param string $propertyName |
| * @return mixed The value of the input variable. |
| * @ignore |
| */ |
| public function __get( $propertyName ) |
| { |
| if ( isset( $this->properties[$propertyName] ) ) |
| { |
| if ( $this->properties[$propertyName] === ezcInputForm::VALID ) |
| { |
| return $this->propertyValues[$propertyName]; |
| } |
| else |
| { |
| throw new ezcInputFormNoValidDataException( $propertyName ); |
| } |
| } |
| throw new ezcInputFormUnknownFieldException( $propertyName ); |
| } |
| |
| /** |
| * Returns whether a magic property was is used on a magic property. |
| * |
| * This method checks whether a magic property exists and returns true of |
| * it does and false if it doesn't. The list of properties which exist is |
| * determined by the $definition array that was passed to the constructor. |
| * |
| * @param string $propertyName |
| * @return bool Whether the $propertyName exists or not. |
| * @ignore |
| */ |
| public function __isset( $propertyName ) |
| { |
| return isset( $this->properties[$propertyName] ); |
| } |
| |
| /** |
| * Sets a new magic property. |
| * |
| * This function is called when one of the magic properties was assigned a |
| * new value. As all magic properties are read-only for this class, all |
| * that this function does is return the exception |
| * ezcBasePropertyReadOnlyException. |
| * |
| * @throws ezcBasePropertyPermissionException for every call to this |
| * function. |
| * @param string $propertyName |
| * @param mixed $newValue |
| * @ignore |
| */ |
| public function __set( $propertyName, $newValue ) |
| { |
| throw new ezcBasePropertyPermissionException( $propertyName, ezcBasePropertyPermissionException::READ ); |
| } |
| |
| /** |
| * Returns whether the optional field $fieldName exists. |
| * |
| * @param string $fieldName |
| * @return bool true if the input field was available and false otherwise. |
| */ |
| public function hasInputField( $fieldName ) |
| { |
| if ( isset( $this->properties[$fieldName] ) ) |
| { |
| return true; |
| } |
| return false; |
| } |
| |
| /** |
| * Returns whether the filters for required field $fieldName returned valid data. |
| * |
| * @param string $fieldName |
| * @return bool true if the input field was available and false otherwise. |
| */ |
| public function hasValidData( $fieldName ) |
| { |
| if ( isset( $this->properties[$fieldName] ) && $this->properties[$fieldName] === ezcInputForm::VALID ) |
| { |
| return true; |
| } |
| return false; |
| } |
| |
| /** |
| * Returns RAW input variable values for invalid field $fieldName. |
| * |
| * The return value of this function can be used to prefill forms on the |
| * next request. It will only work for invalid input fields, as for valid |
| * input fields you should never have to get to the original RAW data. In |
| * the case a $fieldName is passed that has valid data, an |
| * ezcInputFormException will be thrown. |
| * |
| * @throws ezcInputFormValidDataException when trying to get unsafe raw |
| * data from a input field with valid data. |
| * @throws ezcInputFormFieldNotFoundException when trying to get data from a |
| * property that does not exist. |
| * @param string $fieldName |
| * @return string The original RAW data of the specified input field. |
| */ |
| public function getUnsafeRawData( $fieldName ) |
| { |
| if ( isset( $this->properties[$fieldName] ) ) |
| { |
| if ( $this->properties[$fieldName] === ezcInputForm::VALID ) |
| { |
| throw new ezcInputFormValidDataAvailableException( $fieldName ); |
| } |
| else |
| { |
| if ( filter_has_var( $this->inputSource, $fieldName ) ) |
| { |
| return filter_input( $this->inputSource, $fieldName, FILTER_UNSAFE_RAW ); |
| } |
| else |
| { |
| throw new ezcInputFormFieldNotFoundException( $fieldName ); |
| } |
| } |
| } |
| throw new ezcInputFormUnknownFieldException( $fieldName ); |
| } |
| |
| /** |
| * Returns a list with all optional properties. |
| * @return array(string) |
| */ |
| public function getOptionalProperties() |
| { |
| $properties = array(); |
| foreach ( $this->definition as $fieldName => $fieldDefinition ) |
| { |
| if ( $fieldDefinition->type === ezcInputFormDefinitionElement::OPTIONAL ) |
| { |
| $properties[] = $fieldName; |
| } |
| } |
| return $properties; |
| } |
| |
| /** |
| * Returns a list with all required properties. |
| * @return array(string) |
| */ |
| public function getRequiredProperties() |
| { |
| $properties = array(); |
| foreach ( $this->definition as $fieldName => $fieldDefinition ) |
| { |
| if ( $fieldDefinition->type === ezcInputFormDefinitionElement::REQUIRED ) |
| { |
| $properties[] = $fieldName; |
| } |
| } |
| return $properties; |
| } |
| |
| /** |
| * Returns a list with all properties that have valid data. |
| * @return array(string) |
| */ |
| public function getValidProperties() |
| { |
| $properties = array(); |
| foreach ( $this->properties as $fieldName => $fieldStatus ) |
| { |
| if ( $fieldStatus === ezcInputForm::VALID ) |
| { |
| $properties[] = $fieldName; |
| } |
| } |
| return $properties; |
| } |
| |
| /** |
| * Returns a list with all properties having invalid data. |
| * @return array(string) |
| */ |
| public function getInvalidProperties() |
| { |
| $properties = array(); |
| foreach ( $this->properties as $fieldName => $fieldStatus ) |
| { |
| if ( $fieldStatus === ezcInputForm::INVALID ) |
| { |
| $properties[] = $fieldName; |
| } |
| } |
| return $properties; |
| } |
| |
| /** |
| * Returns whether all the input elements were valid or not. |
| * |
| * @return bool |
| */ |
| public function isValid() |
| { |
| return $this->allElementsValid; |
| } |
| } |
| ?> |