├── README.md ├── RequestObject.php ├── test1.php ├── test2.php ├── test3.php ├── test4.php ├── test5.php └── test6.php /README.md: -------------------------------------------------------------------------------- 1 | # Example 1: simple example 2 | class Request1 extends RequestObject 3 | { 4 | public $param1; 5 | } 6 | 7 | $request = new Request1(); 8 | echo "param1: " . $request->param1 . '

'; 9 | 10 | output: 11 | 12 | test1.php?param1=11212 13 | param1: 11212 14 | 15 | test1.php?param1=hi 16 | param1: hi 17 | 18 | 19 | # Example 2: data types and default values 20 | 21 | ```php 22 | "; 39 | var_dump($request->param1); 40 | echo "
"; 41 | 42 | echo "param2:
"; 43 | var_dump($request->param2); 44 | echo "
"; 45 | ``` 46 | 47 | output: 48 | 49 | test2.php?param1=hi&param2=1 50 | 51 | param1: string(2) "hi" 52 | param2: string(1) "1" 53 | 54 | test2.php?param1=1&param2=hi 55 | 56 | param1: string(1) "1" 57 | param2: string(2) "hi" 58 | 59 | test2.php?param1=1 60 | param1: string(1) "1" 61 | param2: string(13) "default value" 62 | 63 | # Example 3: validadors 64 | ```php 65 | "; 89 | var_dump($request->param1); 90 | echo "
"; 91 | 92 | echo "param2:
"; 93 | var_dump($request->param2); 94 | echo "
"; 95 | } catch (RequestObjectException $e) { 96 | echo $e->getMessage(); 97 | echo "
"; 98 | var_dump($e->getValidationErrors()); 99 | } 100 | ``` 101 | 102 | output: 103 | 104 | test3.php?param2=2&param1=hi 105 | param1: string(2) "ih" 106 | param2: int(2) 107 | 108 | test3.php?param1=hola&param2=1 109 | Validation error 110 | array(1) { ["param2"]=> array(1) { ["value"]=> int(1) } } 111 | 112 | # Example 4: Dynamic validations 113 | 114 | ```php 115 | appendValidateTo('param2', function($value) { 127 | if ($value == 1) { 128 | return false; 129 | } 130 | }); 131 | 132 | try { 133 | $request->validateAll(); // now we perform the validation 134 | 135 | echo "param1:
"; 136 | var_dump($request->param1); 137 | echo "
"; 138 | 139 | echo "param2:
"; 140 | var_dump($request->param2); 141 | echo "
"; 142 | } catch (RequestObjectException $e) { 143 | echo $e->getMessage(); 144 | echo "
"; 145 | var_dump($e->getValidationErrors()); 146 | } 147 | ``` 148 | 149 | output: 150 | 151 | test4.php?param1=hi&param2=2 152 | param1: string(4) "hi" 153 | param2: int(2) 154 | 155 | test4.php?param1=hola&param2=1 156 | Validation error 157 | array(1) { ["param2"]=> array(1) { ["value"]=> int(1) } } 158 | 159 | 160 | 161 | # Example 5: Arrays and default params 162 | 163 | ```php 164 | param1:

"; 188 | var_dump($request->param1); 189 | 190 | echo "

param2:

"; 191 | var_dump($request->param2); 192 | 193 | echo "

param3:

"; 194 | var_dump($request->param3); 195 | ``` 196 | 197 | output: 198 | 199 | test5.php?param1[]=1&param1[]=2&param2[]=hi 200 | param1: array(2) { [0]=> int(1) [1]=> int(2) } 201 | param2: int(1) 202 | param3: array(2) { [0]=> string(5) "hello" [1]=> string(5) "world" } 203 | 204 | test5.php?param1[]=1&param1[]=2&param2=2 205 | param1: array(2) { [0]=> string(1) "1" [1]=> string(1) "2" } 206 | param2: int(3) 207 | param3: array(2) { [0]=> string(5) "hello" [1]=> string(5) "world" } 208 | -------------------------------------------------------------------------------- /RequestObject.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * @copyright under GPL 2 licence 9 | */ 10 | class RequestObjectException extends Exception 11 | { 12 | private $validationErrors = array(); 13 | function __construct($message, $validationErrors) 14 | { 15 | $this->validationErrors = $validationErrors; 16 | parent::__construct($message); 17 | } 18 | 19 | public function getValidationErrors() 20 | { 21 | return $this->validationErrors; 22 | } 23 | } 24 | 25 | abstract class RequestObject 26 | { 27 | private $parameters = array(); 28 | private $reflection; 29 | 30 | private function processPHPDoc(ReflectionProperty $reflect) 31 | { 32 | $out = array('default' => null, 'cast' => 'raw'); 33 | $docComment = $reflect->getDocComment(); 34 | if (trim($docComment) == '') return $out; 35 | 36 | $docComment = preg_replace('#[ \t]*(?:\/\*\*|\*\/|\*)?[ ]{0,1}(.*)?#', '$1', $docComment); 37 | $docComment = trim(str_replace('*/', null, $docComment)); 38 | 39 | foreach (explode("\n", $docComment) as $item) { 40 | $item = strtolower($item); 41 | if (strpos($item, '@cast') !== false) { 42 | $cast = trim(str_replace('@cast', null, $item)); 43 | $out['cast'] = $cast; 44 | } 45 | 46 | if (strpos($item, '@default') !== false) { 47 | $default = trim(str_replace('@default', null, $item)); 48 | $out['default'] = $default; 49 | } 50 | } 51 | return $out; 52 | } 53 | 54 | /** 55 | * @static 56 | * @param bool $validateInConstructor 57 | * @return RequestObject 58 | */ 59 | public static function factory($validateInConstructor = true) 60 | { 61 | return new self($validateInConstructor); 62 | } 63 | 64 | /** 65 | * @var RequestObject 66 | */ 67 | private static $instance = null; 68 | 69 | /** 70 | * @static 71 | * @return RequestObject 72 | */ 73 | public static function singleton() 74 | { 75 | if (is_null(self::$instance)) { 76 | self::$instance = self::factory(); 77 | } 78 | return self::$instance; 79 | } 80 | 81 | public function __construct($validateInConstructor = true) 82 | { 83 | $this->reflection = new ReflectionClass($this); 84 | 85 | $args = array(); 86 | $availablefilters = $this->getAvailableFilters(); 87 | 88 | foreach ($this->reflection->getProperties(ReflectionProperty::IS_PUBLIC) as $reflectionProperty) { 89 | $propertyName = $reflectionProperty->getName(); 90 | $doc = $this->processPHPDoc($this->reflection->getProperty($propertyName)); 91 | 92 | $values = array('default' => $doc['default']); 93 | if (strpos($doc['cast'], 'array') !== false) { 94 | $values['flags'] = FILTER_REQUIRE_ARRAY; 95 | $cast = str_replace('array', null, $doc['cast']); 96 | } else { 97 | $cast = $doc['cast']; 98 | } 99 | 100 | if (!array_key_exists($cast, $availablefilters)) { 101 | throw new Exception("cast '{$cast}' not available"); 102 | } 103 | $values['filter'] = $availablefilters[$cast]; 104 | $args[$propertyName] = $values; 105 | } 106 | 107 | $inputMethods = array('GET' => INPUT_GET, 'POST' => INPUT_POST); 108 | $inputMethod = $inputMethods[$_SERVER['REQUEST_METHOD']]; 109 | 110 | $myinputs = filter_input_array($inputMethod, $args); 111 | 112 | foreach ($myinputs as $propertyName => $filteredInput) { 113 | if (is_null($filteredInput) && ! is_null($args[$propertyName])) { 114 | $this->$propertyName = filter_var($args[$propertyName]['default'], $args[$propertyName]['filter']); 115 | } else { 116 | $this->$propertyName = $filteredInput; 117 | } 118 | $this->filteredParameters[$propertyName] = $this->$propertyName; 119 | 120 | } 121 | 122 | if ($validateInConstructor === true) { 123 | $this->validateAll(); 124 | } 125 | } 126 | 127 | private $filteredParameters = array(); 128 | 129 | public function getFilteredParameters() 130 | { 131 | return $this->filteredParameters; 132 | } 133 | 134 | private function filterInput($cast, $key, $default) 135 | { 136 | $inputMethods = array('GET' => INPUT_GET, 'POST' => INPUT_POST); 137 | 138 | if (!array_key_exists($_SERVER['REQUEST_METHOD'], $inputMethods)) { 139 | throw new Exception("Input method not supported"); 140 | } 141 | 142 | $inputMethod = $inputMethods[$_SERVER['REQUEST_METHOD']]; 143 | 144 | $filters = $this->getAvailableFilters(); 145 | $filter = $filters[$cast]; 146 | $out = filter_input($inputMethod, $key, $filter); 147 | 148 | if (is_null($out) && $default != '') { 149 | $out = filter_var($default, $filter); 150 | } 151 | return $out; 152 | } 153 | 154 | private function getAvailableFilters() 155 | { 156 | $filters = array( 157 | 'string' => FILTER_SANITIZE_STRING, 158 | 'int' => FILTER_VALIDATE_INT, 159 | 'integer' => FILTER_VALIDATE_INT, 160 | 'bool' => FILTER_VALIDATE_BOOLEAN, 161 | 'float' => FILTER_VALIDATE_FLOAT, 162 | 'url' => FILTER_VALIDATE_EMAIL, 163 | 'email' => FILTER_SANITIZE_STRING, 164 | 'raw' => FILTER_UNSAFE_RAW 165 | ); 166 | return $filters; 167 | } 168 | 169 | public function validateAll() 170 | { 171 | $validationErrors = array(); 172 | foreach (array_keys($this->parameters) as $parameter) { 173 | $validateMethod = "validate_{$parameter}"; 174 | if ($this->reflection->hasMethod($validateMethod) || isset($this->dinamicCallbacks[$validateMethod])) { 175 | $parameter = $this->$parameter; 176 | if ($this->$validateMethod($parameter) === false) { 177 | $validationErrors[$parameter] = array( 178 | 'value' => $this->$parameter 179 | ); 180 | } 181 | } 182 | } 183 | 184 | if (count($validationErrors) > 0) { 185 | throw new RequestObjectException('Validation error', $validationErrors); 186 | } 187 | } 188 | 189 | private $dinamicCallbacks = array(); 190 | 191 | public function appendValidateTo($property, Closure $callback) 192 | { 193 | $this->dinamicCallbacks["validate_{$property}"] = $callback; 194 | } 195 | 196 | public function __call($name, $arguments) 197 | { 198 | if (isset($this->dinamicCallbacks[$name])) { 199 | return call_user_func_array($this->dinamicCallbacks[$name], $arguments); 200 | } else { 201 | return null; 202 | } 203 | } 204 | } 205 | -------------------------------------------------------------------------------- /test1.php: -------------------------------------------------------------------------------- 1 | param1 . '

'; 12 | /* 13 | test1.php?param1=11212 14 | param1: 11212 15 | 16 | test1.php?param1=hola 17 | param1: hola 18 | */ 19 | -------------------------------------------------------------------------------- /test2.php: -------------------------------------------------------------------------------- 1 | "; 18 | var_dump($request->param1); 19 | echo "
"; 20 | 21 | echo "param2:
"; 22 | var_dump($request->param2); 23 | echo "
"; 24 | 25 | /* 26 | test2.php?param1=hi¶m2=1 27 | 28 | param1: string(2) "hi" 29 | param2: string(1) "1" 30 | 31 | test2.php?param1=1¶m2=hi 32 | 33 | param1: string(1) "1" 34 | param2: string(2) "hi" 35 | 36 | test2.php?param1=1 37 | param1: string(1) "1" 38 | param2: string(13) "default value" 39 | */ 40 | -------------------------------------------------------------------------------- /test3.php: -------------------------------------------------------------------------------- 1 | "; 27 | var_dump($request->param1); 28 | echo "
"; 29 | 30 | echo "param2:
"; 31 | var_dump($request->param2); 32 | echo "
"; 33 | } catch (RequestObjectException $e) { 34 | echo $e->getMessage(); 35 | echo "
"; 36 | var_dump($e->getValidationErrors()); 37 | } 38 | 39 | /* 40 | test3.php?param2=2¶m1=hi 41 | param1: string(2) "ih" 42 | param2: int(2) 43 | 44 | test3.php?param1=hola¶m2=1 45 | Validation error 46 | array(1) { [1]=> array(1) { ["value"]=> NULL } } 47 | */ 48 | -------------------------------------------------------------------------------- /test4.php: -------------------------------------------------------------------------------- 1 | appendValidateTo('param2', function($value) { 15 | if ($value == 1) { 16 | return false; 17 | } 18 | 19 | $value = '1'; 20 | }); 21 | 22 | try { 23 | $request->validateAll(); 24 | 25 | echo "param1:
"; 26 | var_dump($request->param1); 27 | echo "
"; 28 | 29 | echo "param2:
"; 30 | var_dump($request->param2); 31 | echo "
"; 32 | } catch (RequestObjectException $e) { 33 | echo $e->getMessage(); 34 | echo "
"; 35 | var_dump($e->getValidationErrors()); 36 | } 37 | 38 | 39 | /* 40 | test4.php?param1=hola¶m2=2 41 | param1: string(4) "hola" 42 | param2: int(2) 43 | 44 | test4.php?param1=hola¶m2=1 45 | Validation error 46 | array(1) { [1]=> array(1) { ["value"]=> NULL } } 47 | */ 48 | -------------------------------------------------------------------------------- /test5.php: -------------------------------------------------------------------------------- 1 | "; 13 | var_dump($request->param1); 14 | /* 15 | test5.php?param1[]=1¶m1[]=2 16 | param1: 11212 17 | 18 | test1.php?param1=hola 19 | param1: hola 20 | */ 21 | -------------------------------------------------------------------------------- /test6.php: -------------------------------------------------------------------------------- 1 | getFilteredParameters()); 18 | 19 | echo "param1:
"; 20 | var_dump($param1); 21 | echo "
"; 22 | 23 | echo "param2:
"; 24 | var_dump($param2); 25 | echo "
"; 26 | --------------------------------------------------------------------------------