Skip to content

Instantly share code, notes, and snippets.

@wstucco
Last active December 15, 2015 02:39
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save wstucco/5189136 to your computer and use it in GitHub Desktop.
Save wstucco/5189136 to your computer and use it in GitHub Desktop.
three different versions of a function handling parameters validation
<?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
<?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
<?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