Currently I am working on a simple MVC framework of mine. So my Core library loads the controller and method based on the URL with the given parameters. The parameters are an array after exploding the URL. This is done by using call_user_func_array([$controller, $method], $params)
. To prevent user from passing incorrect parameters in URL I came up with the following solution using Reflection to check whether correct number and types of parameters are passed.
Core
class Core {
public function __construct(){
$url = $this->getUrl();
// After loading correct Model and Controller (and called method)...
// Get params - Any values left over in url are params
$this->params = $url ? array_values($url) : [];
$this->params = $this->correctURLTypes($this->params);
$r = new ReflectionMethod($this->currentController, $this->currentMethod);
$requiredArgs = $this->numberOfRequiredArgs(); // Only Required params not optional
$totalArgs = $r->getNumberOfParameters(); // Total no. of params in method (required + optional)
$givenArgs = count($this->params);
if($givenArgs >= $requiredArgs && $givenArgs <= $totalArgs) {
// get params at index 1, 2, 3 ... $givenArgs
for ($i = 0; $i < $givenArgs; $i++) {
$param = new ReflectionParameter(array($this->currentController, $this->currentMethod), $i);
$paramType = $param->getType();
if($paramType == 'string') $this->params[$i] = (string) $this->params[$i];
// cast int/bool/float if expecting string
$givenType = Utils::typeOf($this->params[$i]); // static function to return type of variable
if(!is_null($paramType) && $paramType != $givenType) { // if type specified and dont match
die("Types dont match");
}
}
call_user_func_array([$this->currentController, $this->currentMethod], $this->params);
} else {
die("Arg count incorrect");
}
}
public function getUrl(){
if(isset($_GET['url'])){
$url = rtrim($_GET['url'], '/');
$url = filter_var($url, FILTER_SANITIZE_URL);
$url = explode('/', $url);
return $url;
}
}
public function correctURLTypes($arr) {
foreach ($arr as $key => $value) {
// string, int, float, bool ONLY
if(is_numeric($value)) {
if(ctype_digit($value)) {
$arr[$key] = (int) $value;
} else {
$val = $value+0;
if(is_float($val)) {
$arr[$key] = (float) $value;
}
}
} else {
if(in_array($value, ['true', 'false'])) {
if($value == 'true')
$arr[$key] = true;
else
$arr[$key] = false;
} else {
$arr[$key] = (string) $value;
}
}
}
return $arr;
}
public function numberOfRequiredArgs() {
$r = new ReflectionMethod($this->currentController, $this->currentMethod);
$required = 0;
foreach ($r->getParameters() as $key => $val) {
if(!$val->isDefaultValueAvailable()) $required++;
}
return $required;
}
}
So a method can be called which has required type specified and may have optional parameters like:
public function show(int $id, bool $edit = true) {
I was unsure if this is a good way of doing it and if its a hack. I wanted feedback and suggestions for loading the methods and parameters in such a way.