├── .gitignore ├── RedBeanFVM ├── Locale │ ├── Template.php │ └── US.php └── RedBeanFVM.php ├── composer.json ├── examples ├── post.php └── custom_filter.php └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | /vendor 2 | 3 | composer.lock 4 | 5 | /redbean 6 | /tests -------------------------------------------------------------------------------- /RedBeanFVM/Locale/Template.php: -------------------------------------------------------------------------------- 1 | =4.1" 14 | }, 15 | "require-dev": {}, 16 | "autoload": { 17 | "psr-0": { 18 | "RedBeanFVM": "." 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /examples/post.php: -------------------------------------------------------------------------------- 1 | 'name', 18 | 'Email'=>'email', 19 | 'User_Name'=>['rmnl','az_lower'], 20 | 'Password'=>'password_hash', 21 | ]; 22 | 23 | $fvm->generate_model($bean,$required); //boom! now your bean is ready for storage. 24 | 25 | R::store($bean); 26 | 27 | } -------------------------------------------------------------------------------- /examples/custom_filter.php: -------------------------------------------------------------------------------- 1 | 'min', 20 | 'model'=>'min', 21 | 'year'=>'car_year', //this is custom filter 22 | 'vin-number'=>'car_vin_number',// so is this 23 | ]; 24 | 25 | //now we must create the custom filters ... 26 | 27 | $fvm->custom_filter('car_year',function($input){ 28 | if(!preg_match('/^[0-9]{4}$/',$input)){ 29 | throw new \exception('Invalid Year entered'); 30 | } 31 | return $input; 32 | }); 33 | 34 | $fvm->custom_filter('car_vin_number',function($input){ 35 | // vin numbers are 17 in length alphanumeric 36 | if(!preg_match('/^[0-9A-Za-z]{17}$/',$input)){ 37 | throw new \exception('Invalid VIN entered.'); 38 | } 39 | return strtoupper($input);//we dont really care if they typed lower case. we can fix it for them. 40 | }); 41 | 42 | $fvm->generate_model($bean,$required); //boom! now your bean is ready for storage. 43 | 44 | R::store($bean); 45 | 46 | } -------------------------------------------------------------------------------- /RedBeanFVM/Locale/US.php: -------------------------------------------------------------------------------- 1 | 'ALABAMA', 48 | 'AK'=>'ALASKA', 49 | 'AZ'=>'ARIZONA', 50 | 'AR'=>'ARKANSAS', 51 | 'CA'=>'CALIFORNIA', 52 | 'CO'=>'COLORADO', 53 | 'CT'=>'CONNECTICUT', 54 | 'DE'=>'DELAWARE', 55 | 'DC'=>'DISTRICT OF COLUMBIA', 56 | 'FL'=>'FLORIDA', 57 | 'GA'=>'GEORGIA', 58 | 'HI'=>'HAWAII', 59 | 'ID'=>'IDAHO', 60 | 'IL'=>'ILLINOIS', 61 | 'IN'=>'INDIANA', 62 | 'IA'=>'IOWA', 63 | 'KS'=>'KANSAS', 64 | 'KY'=>'KENTUCKY', 65 | 'LA'=>'LOUISIANA', 66 | 'ME'=>'MAINE', 67 | 'MD'=>'MARYLAND', 68 | 'MA'=>'MASSACHUSETTS', 69 | 'MI'=>'MICHIGAN', 70 | 'MN'=>'MINNESOTA', 71 | 'MS'=>'MISSISSIPPI', 72 | 'MO'=>'MISSOURI', 73 | 'MT'=>'MONTANA', 74 | 'NE'=>'NEBRASKA', 75 | 'NV'=>'NEVADA', 76 | 'NH'=>'NEW HAMPSHIRE', 77 | 'NJ'=>'NEW JERSEY', 78 | 'NM'=>'NEW MEXICO', 79 | 'NY'=>'NEW YORK', 80 | 'NC'=>'NORTH CAROLINA', 81 | 'ND'=>'NORTH DAKOTA', 82 | 'OH'=>'OHIO', 83 | 'OK'=>'OKLAHOMA', 84 | 'OR'=>'OREGON', 85 | 'PA'=>'PENNSYLVANIA', 86 | 'RI'=>'RHODE ISLAND', 87 | 'SC'=>'SOUTH CAROLINA', 88 | 'SD'=>'SOUTH DAKOTA', 89 | 'TN'=>'TENNESSEE', 90 | 'TX'=>'TEXAS', 91 | 'UT'=>'UTAH', 92 | 'VT'=>'VERMONT', 93 | 'VA'=>'VIRGINIA', 94 | 'WA'=>'WASHINGTON', 95 | 'WV'=>'WEST VIRGINIA', 96 | 'WI'=>'WISCONSIN', 97 | 'WY'=>'WYOMING' 98 | ]; 99 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # RedBeanFVM 2 | 3 | RedbeanFVM makes Filtering, Validating , and Generating RedBean Models easy. 4 | 5 | **Features:** 6 | - 16 built in filter and validation functions. 7 | - simple api allows users to define custom named filters. 8 | - chainable filters 9 | - fully automates the process of creating a redbean Model - simply pass in a the bean and a list of required rules Multi-deminsional Array: `Array(datakey => rules)`, 10 | a list of optional rules, and the data source, which defaults to the `$_POST` superglobal. RedBeanFVM searches your data source for the key, performs the filtering rule(s) 11 | and stores this into the model. pretty flippin sweet huh? 12 | - automatically converts datakeys to snake case as required by RedBean. 13 | - Singleton Interface with dynamic methods. No need to juggle references across business logic or pass it down through functions. create custom filters anywhere, and they will persist via static references. 14 | simply retrieve a reference to the library at anytime like so `$fvm = \RedBeanFVM\RedBeanFVM::getInstance();` 15 | 16 | 17 | ### Installation 18 | 19 | 20 | **Install via Composer:** 21 | 22 | 1. install with composer: 23 | ```sh 24 | composer require redbean-fvm/redbean-fvm 25 | ``` 26 | 27 | 2. add code to project: 28 | ```php 29 | require 'vendor/autoload.php'; 30 | $fvm = \RedBeanFVM\RedBeanFVM::getInstance(); 31 | ``` 32 | 33 | **Download and Manually Install:** 34 | 35 | 1. download/clone the package: 36 | ```sh 37 | git clone https://github.com/r3wt/RedBeanFVM.git 38 | ``` 39 | 40 | 2. add this snipped of code: 41 | ```php 42 | require 'RedBeanFVM/RedBeanFVM.php'; 43 | \RedBeanFVM\RedBeanFVM::registerAutoloader(); // for future use 44 | $fvm = \RedBeanFVM\RedBeanFVM::getInstance(); 45 | ``` 46 | 47 | ### Examples 48 | 49 | 1. basic usage: 50 | ```php 51 | $bean = R::dispense('user'); // the redbean model 52 | 53 | $required = [ 54 | 'Name'=>'name', // post key + rule(s) 55 | 'Email'=>'email', 56 | 'User_Name'=>['rmnl','az_lower'], 57 | 'Password'=>'password_hash', 58 | ]; 59 | 60 | $fvm->generate_model($bean,$required); //the magic 61 | 62 | R::store($bean); 63 | ``` 64 | 65 | 2. optional parameters: 66 | ```php 67 | $bean = R::dispense('user'); // the redbean model 68 | 69 | $required = [ 70 | 'Name'=>'name', // post key + rule(s) 71 | 'Email'=>'email', 72 | 'User_Name'=>['rmnl','az_lower'], 73 | 'Password'=>'password_hash', 74 | ]; 75 | //here we are adding the optional array. these fields are optional, so we raise no exception for missing values. 76 | $optional = [ 77 | 'username'=>'min' //min is the minimum validation/filter 78 | ]; 79 | 80 | $fvm->generate_model($bean,$required,$optional); //the magic 81 | 82 | R::store($bean); 83 | ``` 84 | 85 | 3. custom data source 86 | 87 | ```php 88 | $bean = R::dispense('user'); // the redbean model 89 | 90 | $required = [ 91 | 'Name'=>'name', // post key + rule(s) 92 | 'Email'=>'email', 93 | 'User_Name'=>['rmnl','az_lower'], 94 | 'Password'=>'password_hash', 95 | ]; 96 | $optional = [ 97 | 'username'=>'min' //min is the minimum validation/filter 98 | ]; 99 | 100 | // here we add a custom data source 101 | 102 | $data = array_merge($_POST,$_GET); 103 | 104 | $fvm->generate_model($bean,$required,$optional,$data); //the magic 105 | 106 | R::store($bean); 107 | ``` 108 | 109 | 4. manual usage of the methods. 110 | ```php 111 | $unsafeData = 'alfjasldfajsl1000afdasjlkl'; 112 | //we can use RedBeanFVM manually too. 113 | $bean->someProperty = $fvm->cast_int($unsafeData); 114 | ``` 115 | 116 | 5. chainable methods with chain() 117 | ```php 118 | $input = $_POST['username']; 119 | 120 | $rules = ['rmnl','az','name']; 121 | 122 | $bean->user_name = $fvm->chain($rules,$input); 123 | ``` 124 | 125 | 6. custom filters(named closures) 126 | ```php 127 | $fvm = \RedBeanFVM\RedBeanFVM::getInstance(); 128 | 129 | $bean = R::dispense('automobile'); 130 | 131 | $required = [ 132 | 'make'=>'min', 133 | 'model'=>'min', 134 | 'year'=>'car_year', //this is custom filter 135 | 'vin-number'=>'car_vin_number',// so is this 136 | ]; 137 | 138 | //now we must create the custom filters ... 139 | 140 | //create a custom filter to validate the `year` of the automobile 141 | $fvm->custom_filter('car_year',function($input){ 142 | if(!preg_match('/^[0-9]{4}$/',$input)){ 143 | throw new \exception('Invalid Year entered'); 144 | } 145 | return $input; 146 | }); 147 | 148 | //create a custom filter to validate the vin number of the automobile. 149 | $fvm->custom_filter('car_vin_number',function($input){ 150 | // vin numbers are 17 in length alphanumeric 151 | if(!preg_match('/^[0-9A-Za-z]{17}$/',$input)){ 152 | throw new \exception('Invalid VIN entered.'); 153 | } 154 | return strtoupper($input);//we dont really care if they typed lower case. we can fix it for them. 155 | }); 156 | 157 | //now we can use our custom filters for year and vin. 158 | $required = [ 159 | 'make'=>'min', 160 | 'model'=>'min', 161 | 'year'=>'car_year', //this is custom filter 162 | 'vin-number'=>'car_vin_number',// so is this 163 | ]; 164 | 165 | $fvm->generate_model($bean,$required); 166 | ``` 167 | 168 | 7. advanced custom filters. 169 | - Some functions like `name` accept optional second parameters. 170 | - by design, FVM only accepts 1 argument, the $input to be filtered. 171 | - we can work around this like so: 172 | ```php 173 | $min_length = 10; 174 | $max_length = 55; 175 | 176 | $fvm->custom_filter('name_custom',function($input) use($fvm,$min_length,$max_length){ 177 | return $fvm->name($input,$min_length,$max_length); 178 | }); 179 | ``` 180 | 181 | 8. calling custom filter directly on $fvm is possible. 182 | 183 | ```php 184 | 185 | $fvm->custom_filter('foo',function($input){ 186 | return 'foo'; 187 | }); 188 | 189 | $input = 'abcdefg'; 190 | 191 | $bean->foo = $fvm->foo($input); 192 | ``` 193 | 194 | 9. checkboxes 195 | 196 | ```php 197 | 198 | $checkboxes = [ 199 | 'remember_me'=>1,//the expected value of the input field. 200 | 'email_subscribe'=>1, 201 | ]; 202 | 203 | $fvm->checkbox($bean,$checkboxes,$_POST); 204 | ``` 205 | 206 | 10. loading a custom filter class. 207 | 208 | ```php 209 | //latest version of RedbeanFVM introduces the ability to load a custom class of filters. this way you 210 | //can write code for your filters in a normal class instead of relying on `RedbeanFVM::custom_filter()` 211 | //at present time, it is only possible to load a single class, in future support for passing an array of classes is possible, 212 | //although personally i believe it to be cleaner to use a single class. 213 | 214 | //note: namespaces must be escaped as shown 215 | 216 | \RedBeanFVM\RedBeanFVM::configure([ 217 | 'user_filters'=>'\\App\\Util\\CustomFilters' 218 | ]); 219 | 220 | ``` 221 | 222 | 11. required and optional can be called directly. 223 | 224 | ``` 225 | $required = [...rules]; 226 | $optional = [...rules; 227 | 228 | $fvm->required($bean,$required,$_POST); 229 | $fvm->optional($bean,$optional,$_POST); 230 | 231 | ``` 232 | 233 | 12. file uploads (coming soonish) 234 | 235 | 13. all config options and their default values. 236 | 237 | ```php 238 | \RedbeanFVM\RedbeanFVM::configure([ 239 | 'password'=>[ 240 | 'cost'=>12, //cost of password_hash 241 | 'algo'=>PASSWORD_DEFAULT //password algo. see PHP Manual entry for password_hash() for more info. 242 | ], 243 | 'locale'=>'\\RedBeanFVM\\Locale\\US', //default locale is this. 244 | 'user_filters'=>'',//load a custom class of filters. filter methods must be public. 245 | 'optional_defaults'=>false //require that optional parameters are provided a default. see example 13 for more info. 246 | ]); 247 | ``` 248 | 249 | 14. optional defaults explained. 250 | 251 | ```php 252 | //say you want a field to be optional, but it needs a default value if not specified right? 253 | //FVM will expect that you provide an array of rules, with the last rule being the default value like so 254 | $optional = [ 255 | 'about_me'=>['paragraph','no info provided'], 256 | 'phone_number'=>['us_phone','not provided'] 257 | ]; 258 | //as you can see its pretty convenient to type and should be simple to get the hang of. 259 | //if users don't like this, please open an issue and we can think of an alternative way to handle it. 260 | //this option is disabled by default. 261 | ``` 262 | 263 | ### Requirements: 264 | 265 | RedBean, obviously `http://www.redbeanphp.com/` 266 | 267 | ### Development 268 | 269 | Want to contribute? Great! Pull Requests welcomed! 270 | 271 | ### Todo's 272 | 273 | - Write Tests 274 | - Language Packs (Locale's) 275 | - Expand Features. 276 | 277 | 278 | -------------------------------------------------------------------------------- /RedBeanFVM/RedBeanFVM.php: -------------------------------------------------------------------------------- 1 | 7 | * @copyright 2015 Garrett R. Morris 8 | * @link https://github.com/r3wt/RedBeanFVM 9 | * @license http://www.opensource.org/licenses/mit-license.php 10 | * @version 1.0.5 11 | * @package redbean-fvm 12 | * 13 | * MIT LICENSE 14 | * 15 | * Permission is hereby granted, free of charge, to any person obtaining 16 | * a copy of this software and associated documentation files (the 17 | * "Software"), to deal in the Software without restriction, including 18 | * without limitation the rights to use, copy, modify, merge, publish, 19 | * distribute, sublicense, and/or sell copies of the Software, and to 20 | * permit persons to whom the Software is furnished to do so, subject to 21 | * the following conditions: 22 | * 23 | * The above copyright notice and this permission notice shall be 24 | * included in all copies or substantial portions of the Software. 25 | * 26 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 27 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 28 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 29 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 30 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 31 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 32 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 33 | */ 34 | namespace RedBeanFVM; 35 | 36 | class RedBeanFVM 37 | { 38 | private static $instance = null; 39 | 40 | private static $config = [ 41 | 'password'=>[ 42 | 'cost'=>12, //cost of password_hash 43 | 'algo'=>PASSWORD_DEFAULT //password algo. see PHP Manual entry for password_hash() for more info. 44 | ], 45 | 'locale'=>'\\RedBeanFVM\\Locale\\US', //default locale is this. 46 | 'user_filters'=>'', 47 | 'optional_defaults'=>false 48 | ]; 49 | 50 | private static $locale_filters = null; //load a locale filter set. 51 | private static $custom_filters = []; //allow users to register custom filters. 52 | private static $user_filters = null; //load a class of custom filters. 53 | 54 | /** 55 | * Protected Ctor 56 | */ 57 | protected function __construct() 58 | { 59 | $c = self::$config; 60 | if(!empty($c['locale']) && class_exists($c['locale'],true)){ 61 | self::$locale_filters = new $c['locale']();//instantiate the set locale class. 62 | } 63 | 64 | if(!empty($c['user_filters']) && class_exists($c['user_filters'],true)){ 65 | self::$user_filters = new $c['user_filters']();//instantiate the set locale class. 66 | } 67 | } 68 | 69 | /** 70 | * configure multiple configuration settings 71 | * @param Array $c 72 | */ 73 | public static function configure($c) 74 | { 75 | if(!is_array($c)){ 76 | throw new \exception('RedBeanFVM :: configureAll() expects an array! `'.gettype($c).'` given.'); 77 | } 78 | foreach($c as $k => $v){ 79 | if(isset(self::$config[$k])){ 80 | self::$config[$k] = $v; 81 | }else{ 82 | throw new \exception('RedBeanFVM :: configure() `'.$k.'` is not a valid configuration option.'); 83 | } 84 | } 85 | //if settings changed on an instantiated instance, we must reinstantiate. 86 | if(!is_null(self::$instance)){ 87 | self::destroyInstance(); 88 | self::getInstance(); 89 | } 90 | } 91 | 92 | /** 93 | * Destroy the Singleton instance of RedBeanFVM 94 | * @return void 95 | */ 96 | private static function destroyInstance() 97 | { 98 | self::$instance === null; 99 | } 100 | 101 | /** 102 | * Retrieve the Singleton instance of RedBeanFVM 103 | * @return RedBeanFVM 104 | */ 105 | public static function getInstance() 106 | { 107 | return (is_null(self::$instance) ? self::$instance = new self : self::$instance); 108 | } 109 | 110 | /** 111 | * This magic method searches the list of user defined filters for a match. if none is found, an exception is raised. 112 | * @param callable $function 113 | * @param mixed $args 114 | * @return mixed 115 | */ 116 | public function __call($function,$args = false) 117 | { 118 | if($this->custom_filter_exists($function)){ 119 | return $this->custom_filter_exec($function,$args); 120 | } 121 | if($this->user_filter_exists($function)){ 122 | return $this->user_filter_exec($function,$args); 123 | } 124 | if($this->locale_filter_exists($function)){ 125 | return $this->locale_filter_exec($function,$args); 126 | } 127 | throw new \exception('RedbeanFVM :: Method `'.$function.'` doesn\'t exist!'); 128 | } 129 | 130 | /** 131 | * Autoloader 132 | * @param $class 133 | * @return void 134 | */ 135 | public static function autoload($class) 136 | { 137 | $file = __DIR__ . str_replace('\\','/', preg_replace('/'. __NAMESPACE__ .'/','',$class,1)) . '.php'; 138 | if(file_exists($file)){ 139 | include $file; 140 | } 141 | } 142 | 143 | /** 144 | * Register the autoloader for people who arent using composer. 145 | * @return void 146 | */ 147 | public static function registerAutoloader() 148 | { 149 | spl_autoload_register('\\RedBeanFVM\\RedBeanFVM::autoload'); 150 | } 151 | 152 | /** 153 | * Convert a key to snake casing before setting it to the bean. 154 | * @param string $property the key. 155 | * @return string 156 | */ 157 | private function snake_case($key) 158 | { 159 | return strtolower(trim(preg_replace("/(_)\\1+/", "$1",preg_replace('/([^a-zA-Z0-9_])/','_',$key)),'_')); 160 | } 161 | 162 | /** 163 | * Generate a RedBean Model 164 | * @param RedBean_SimpleModel &$bean An instance of RedBean_SimpleModel 165 | * @param Array $required A list of required keys and their rules (key=>rule) OR (key=>[rule1,rule2, etc.]) 166 | * @param Array $optional A list of optional keys. Exceptions are not thrown for optional keys. default empty array. 167 | * @param Array $source An array of data where to look for the keys. Default is post 168 | * @return void 169 | */ 170 | public function generate_model( &$bean, $required, $optional = [], $source=false) 171 | { 172 | if(!isset($source)){ 173 | $source = $_POST; 174 | } 175 | $this->required($bean,$required,$source); 176 | $this->optional($bean,$optional,$source); 177 | } 178 | 179 | public function required( &$bean, $required, $source=false) 180 | { 181 | if(!isset($source)){ 182 | $source = $_POST; 183 | } 184 | foreach($required as $k => $v){ 185 | if(!isset($source[$k])){ 186 | throw new \exception('Missing form value: '.ucFirst($k)); 187 | } 188 | if(is_array($v)){ 189 | $bean->{ $this->snake_case($k) } = $this->chain($v,$source[$k]); 190 | }else{ 191 | $bean->{ $this->snake_case($k) } = $this->{$v}($source[$k]); 192 | } 193 | } 194 | } 195 | 196 | public function optional( &$bean, $optional = [], $source=false) 197 | { 198 | if(!isset($source)){ 199 | $source = $_POST; 200 | } 201 | foreach($optional as $k => $v){ 202 | $default = false; 203 | if(self::$config['optional_defaults']){ 204 | if(!is_array($v)){ 205 | throw new \exception('optional expects an array of rules when `optional_defaults` is true. the last element in the array will be used as the default value.'); 206 | } 207 | $default = array_pop($v); 208 | } 209 | if(isset($source[$k])){ 210 | if(empty($source[$k])){ 211 | $bean{ $this->snake_case($k) } = isset($default) && $default != false ? $default : ''; 212 | }else{ 213 | if(is_array($v)){ 214 | $bean->{ $this->snake_case($k) } = $this->chain($v,$source[$k]); 215 | }else{ 216 | $bean->{ $this->snake_case($k) } = $this->{$v}($source[$k]); 217 | } 218 | } 219 | }else{ 220 | if($default){ 221 | $bean->{ $this->snake_case($k) } = $default; 222 | } 223 | } 224 | } 225 | } 226 | 227 | public function checkbox( &$bean, $checkboxes, $source=false) 228 | { 229 | if(!isset($source)){ 230 | $source = $_POST; 231 | } 232 | foreach($checkboxes as $k=>$v) 233 | { 234 | $bean->{ $this->snake_case($k) } = (int) (isset($source[$k]) && $source[$k] == $v); 235 | } 236 | } 237 | 238 | public function files( &$bean, $required, $optional=[], $source=false) 239 | { 240 | if(!isset($source)){ 241 | $source = $_FILES; 242 | } 243 | throw new \exception('file uploads not yet implemented'); 244 | } 245 | 246 | /** 247 | * checks $custom_filters for the presence of the named callable 248 | * @param string $function the named callable 249 | * @return bool 250 | */ 251 | private function custom_filter_exists($function) 252 | { 253 | return isset(self::$custom_filters[$function]); 254 | } 255 | 256 | /** 257 | * executes the custom filtering functions and returns the output 258 | * @param string $function the named callable 259 | * @param mixed $input the data to be filtered by the function. 260 | * @return mixed 261 | */ 262 | private function custom_filter_exec($function,$args) 263 | { 264 | $method = self::$custom_filters[$function]; 265 | return call_user_func_array($method,$args); 266 | } 267 | 268 | /** 269 | * checks $locale_filters for the presence of the named callable 270 | * @param string $function the named callable 271 | * @return bool 272 | */ 273 | private function locale_filter_exists($function) 274 | { 275 | return is_object(self::$locale_filters) && method_exists(self::$locale_filters, $function); 276 | } 277 | 278 | /** 279 | * executes the custom filtering functions and returns the output 280 | * @param string $function the named callable 281 | * @param mixed $input the data to be filtered by the function. 282 | * @return mixed 283 | */ 284 | private function locale_filter_exec($function,$args) 285 | { 286 | return call_user_func_array([self::$locale_filters,$function],$args); 287 | } 288 | 289 | /** 290 | * checks $user_filters for the presence of the named callable 291 | * @param string $function the named callable 292 | * @return bool 293 | */ 294 | private function user_filter_exists($function) 295 | { 296 | return is_object(self::$user_filters) && method_exists(self::$user_filters, $function); 297 | } 298 | 299 | /** 300 | * executes the custom filtering functions and returns the output 301 | * @param string $function the named callable 302 | * @param mixed $input the data to be filtered by the function. 303 | * @return mixed 304 | */ 305 | private function user_filter_exec($function,$args) 306 | { 307 | return call_user_func_array([self::$user_filters,$function],$args); 308 | } 309 | 310 | /** 311 | * creates a named callable and adds it to $custom_filters array. useful for creating custom filters. 312 | * @param string $name the name to assign to the callable. 313 | * @param closure $callable the callback function. 314 | */ 315 | public function custom_filter($name,$callable) 316 | { 317 | if(empty($name)){ 318 | throw new \exception('RedBeanFVM :: custom_filter() An Invalid Name was declared.'); 319 | } 320 | if(method_exists($this,$name)){ 321 | throw new \exception('RedBeanFVM :: custom_filter() `'.$name.'()` is a built in method of RedBeanFVM and a custom filter of that name may not be declared.'); 322 | } 323 | if(!is_callable($callable)){ 324 | throw new \exception('RedBeanFVM :: custom_filter() Method `'.$name.'` isn\'t a valid callable!'); 325 | } 326 | $info = new \ReflectionFunction($callable); 327 | if( $info->getNumberOfParameters() !== 1 || $info->getNumberOfRequiredParameters() !== 1 ){ 328 | throw new \exception('RedbeanFVM :: custom_filter() Method`'.$name.'` declares an invalid number of arguments! only one argument is allowed!' ); 329 | } 330 | self::$custom_filters[$name] = $callable; 331 | } 332 | 333 | /** 334 | * executes an array of filters on an input and returns the output. 335 | * @param Array $functions 336 | * @param mixed $input 337 | * @return string 338 | */ 339 | public function chain($functions,$input) 340 | { 341 | foreach($functions as $callable){ 342 | $input = $this->{$callable}($input); 343 | } 344 | return $input; 345 | } 346 | 347 | 348 | // only a-z 349 | public function az($input) 350 | { 351 | return preg_replace( '/[^a-zA-Z]/','', trim($input) ); 352 | } 353 | 354 | // self explanatory 355 | public function az_upper($input) 356 | { 357 | return strtoupper($this->az($input)); 358 | } 359 | 360 | // self explanatory 361 | public function az_lower($input) 362 | { 363 | return strtolower($this->az($input)); 364 | } 365 | 366 | //remove all but typically allowed charachters in a business entity name, Eg: #1 Plumbing-Contractors & Associates, Ltd. 367 | public function business_name($input) 368 | { 369 | return preg_replace( '/[^A-Za-z\,\.\-\&\# ]+/','', trim($input) ); 370 | } 371 | 372 | //cast to int 373 | public function cast_int($input) 374 | { 375 | return ((int) $input); 376 | } 377 | 378 | //validate an email 379 | public function email($input) 380 | { 381 | if(!filter_var($input,FILTER_VALIDATE_EMAIL)){ 382 | throw new \exception('invalid email address'); 383 | } 384 | return $input; 385 | } 386 | 387 | //the minimalist filter 388 | public function min($input) 389 | { 390 | return stripslashes(strip_tags(trim($input))); 391 | } 392 | 393 | //remove all non word charachters with safeguard. 394 | public function name($input, $min = 2,$max = 30) 395 | { 396 | $input = preg_replace( '/[^ \w]+/','', trim($input) ); 397 | if(!preg_match('/^[a-zA-Z ]{'.$min.','.$max.'}$/', $input)){ 398 | throw new \exception('Please Enter an alphabetic name between 2 and 30 charachters. Spaces are allowed.'); 399 | } 400 | return $input; 401 | } 402 | 403 | //normalize the date format from html5 date inputs. Probably needs work in the future. 404 | public function normalize_date($input) 405 | { 406 | if(strpos($input,'-') !== false){ 407 | $seperator = '-'; 408 | } 409 | else if(strpos($input,'/') !== false){ 410 | $seperator = '/'; 411 | } 412 | else if(strpos($input,',') !== false){ 413 | $seperator = ','; 414 | }else{ 415 | throw new \exception('Invalid separator used. Use - OR / OR , to separate the date.'); 416 | } 417 | if(substr_count($input,$seperator) !== 2){ 418 | throw new \exception('Malformed Date given in form.'); 419 | } 420 | $t = explode($seperator,$input); 421 | if(count($t) !== 3){ 422 | throw new \exception("Invalid date"); 423 | } 424 | return implode('-',$t); 425 | } 426 | 427 | public function password_hash($input) 428 | { 429 | return password_hash($input, self::$config['password']['algo'], ['cost'=>self::$config['password']['cost']]); 430 | } 431 | 432 | //format a paragraph. good for textarea inputs. 433 | public function paragraph($input) 434 | { 435 | return str_replace(['\r\n','\n'],'
',strip_tags($input)); 436 | } 437 | 438 | //remove line feeds. 439 | public function rmnl($input) 440 | { 441 | return preg_replace('/\s+/', ' ', trim($input)); 442 | } 443 | } --------------------------------------------------------------------------------