Last active
December 15, 2015 02:39
-
-
Save wstucco/5189136 to your computer and use it in GitHub Desktop.
three different versions of a function handling parameters validation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
// Takes the parameter value and validate it against a | |
// list of known validators and throws an exception if it doesn't | |
// | |
// params: | |
// mixed $value | |
// string $validator | |
// | |
function require_valid_parameter($value, $validator) { | |
$valid_validators = array("int", "positive-int", "negative-int" /* more, you get it */ ); | |
// what we do here? | |
// PHPers would randomly return false, -1, NULL, ignore the error, you name it | |
// In my opinion the best option is to throw an exception, specifically NotImplementedException, | |
// but since it doesn't exists in PHP built in exceptions, we can throw | |
// InvalidArgumentException or DomainException, with some extents even OutOfRangeException could | |
// be fine | |
if(false === in_array($validators, $valid_validator, true)) // strict comparision | |
throw new DomainException("blablabla error message"); | |
switch($validator) { | |
case "int": | |
/* do something */ | |
break; | |
case "positive-int": | |
/* do something */ | |
break; | |
/* | |
. | |
. | |
. | |
*/ | |
} | |
// if we are here the parameter value didn't validate | |
throw new InvalidArgumentException("another differente error message"); | |
} | |
// how long will it take to this function to become thousands line long, with every kind | |
// of strange validators, even the most useless, or that everyone will "extend" it by hacking | |
// the code to add their own? | |
// I mean if you see code like this one and assume is the right way to do, why should you | |
// think about writing something better? | |
// Somebody should tell you about function pointers | |
// let's have a look at version 2 | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
// Takes the parameter value and validate it against a | |
// list of known validators and throws an exception if it doesn't | |
// | |
// version 2, function pointers | |
// | |
// params: | |
// mixed $value | |
// string $validator | |
// | |
function require_valid_parameter($value, $validator) { | |
// since we are basically building a pattern matching algorithm, let's use it | |
// to point us directly to the solution, uscing function pointers to anonymous functions | |
$valid_validators = array( | |
"int" => function() use($value) { return(is_int($value)); }, | |
"positive-int" => function() use($value) { return(is_int($value) && $value > 0); }, | |
"negative-int" => function() use($value) { return(is_int($value) && $value < 0); }, | |
/* more, you get it */ | |
); | |
if(in_array($validators, $valid_validator) === false) | |
throw new DomainException("blablabla error message"); | |
// we call directly the validator function knowing that it exists and | |
// knows how to validate the value.See? much more compact.Can you see a pattern emerging? | |
if(!call_user_func($valid_validators[$validator], $value)) | |
throw new InvalidTypeException("another differente error message"); // this exception does not exist | |
// in built in exceptions | |
} | |
// definitely better. | |
// code is more concise, we just jump to the solution without having to go through a 10 page | |
// long switch/case. | |
// but is it realy better? we still have to compile a long list of predefined validators that our | |
// function knows and accept as valid. | |
// This is the moment when you start thinking: well, let's add a third parameter, with a custom | |
// list of validators that can be applied (either 'instead of' or 'together with' the old ones) | |
// But wait... is the re a better solution? something more clever? | |
// I think there is |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
// Takes the parameter value and validate it against a validaotr function | |
// | |
// version 3, function composition, a little bit of functional programming, just a little | |
// $validator is now a function | |
// | |
// params: | |
// mixed $value | |
// function $validator | |
function require_valid_parameter($value, $validator) { | |
// we check if we have something callable here, before starting the process | |
if(!is_callable($validator)) | |
throw new InvalidArgumentException("meaningful error message"); | |
// we check the return value against true to avoid that custom functions returning values | |
// different from booleans can evaluate to true. | |
// callback functions should only return true or false but we can't be sure | |
// the int 5, even if probably wrong, evaluates to true, so unless we are absolutely sure that the | |
// validator returned `true`, we assume that the validation failed | |
// soooo compact and clear! | |
if(!(true === call_user_func($validator, $value))) | |
throw new InvalidTypeException("another differente error message"); | |
} | |
// where can we go from here? | |
// well for example we have a building block to create helpers functions. | |
// code is terse and almost self documenting and, most of all, extensibility is | |
// virtually unlimited and you don't need access to the source code of | |
// require_valid_argument to add your own validators to the list | |
function require_int($a) { | |
require_valid_argument($a, function() use($a) { | |
return(is_int($a)); | |
}); | |
} | |
function require_positive_int($a) { | |
require_int($a); // validate the parameter's value | |
require_valid_argument($a, function() use($a) { | |
return($a > 0); // we already know that $a is int, we only have to check if it's positive | |
}); | |
} | |
// require_negative_int is left as an excercise for the reader | |
// we can have fun and write whatever kind of validator function we can think of | |
function require_int_between_3_and_7($a) { | |
require_int($a); | |
require_valid_argument($a, function() use($a) { | |
return($a >= 3 && $a <= 7); | |
}); | |
} | |
// even the most stupid ones | |
function require_int_between_3_and_7_excluded($a) { | |
require_int($a); | |
require_valid_argument($a, function() use($a) { | |
return($a > 3 && $a < 7); | |
}); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment