Pull to refresh

Типизация и PHP

Reading time 3 min
Views 2.3K
Не знаю как вам, но мне не редко случается в ходе отладки приложения долго ковыряться в ядре и библиотеках, только для того чтобы выяснить что ошибка была на самой поверхности, скажем в неверно переданном параметре. И тогда думаешь «кабы не поленился добавить валидацию на входе функции, столько бы времени своего сэкономил!». Сделать то это не сложно, да не хочется портить прозрачность бизнес-логики кучей второстепенных условий. По сути это сквозная функциональность и здесь бы пригодился АОП-подход, но официально PHP по прежнему не поддерживает АОП. Что делать? Мне ныне видится следующее решение.



// libs/Validate.php
final class Validate {
const Integer = 1;
const Title = 2;
const Variable = 3;
const TemplateFileName = 4;
const Boolean = 5;
const UserFileSection = 6;
const Gender = 7;
const Age = 8;
const Email = 9;

static function _checkStrLength($val, $strLen, $min=false, $max=false) {
if($min AND $strLen<$min) throw new Exception ("'{$val}' is less than {$min} characters long");
if($max AND $strLen>$max) throw new Exception ("'{$val}' is greater than {$max} characters long");
}

static function type($val, $type) {
switch ($type) {
case self::Integer:
if (strval(intval($val)) != $val) throw new Exception ("'{$val}' does not appear to be an integer");
break;
case self::Title:
self::_checkStrLength($val, UTF8::strlen($val), 1, 255);
break;
case self::Variable:
self::_checkStrLength($val, UTF8::strlen($val), false, 32);
if(!preg_match("/^[a-zA-Z][a-zA-Z0-9_]*$/is", $val)) throw new Exception ("'{$val}' contains invalid characters for a variable name");
break;
case self::TemplateFileName:
if(!file_exists(BASEDIR."/custom/views/xml/{$val}")) throw new Exception ("'{$val}' file not found");
break;
case self::Boolean:
if(!is_bool($val)) throw new Exception ("'{$val}' does not appear to be an boolean");
break;
case self::UserFileSection:
if($val!="avatar" AND $val!="file" AND $val!="marketplace" AND $val!="image" ) throw new Exception ("'{$val}' user file section is not valid");
break;
case self::Gender:
if($val!="male" AND $val!="female" AND $val!="undefined" ) throw new Exception ("'{$val}' gender is not valid");
break;
case self::Age:
if(($val<4 AND $val>99) OR $val==69 ) throw new Exception ("'{$val}' age is not valid");
break;
case self::Email:
if (!eregi("^[_a-z0-9-]+(\.[_a-z0-9-]+)*@[a-z0-9-]+(\.[a-z0-9-]+)*(\.[a-z]{2,3})$", $val)) throw new Exception ("'{$val}' emaiol address is not valid");
break;
}
}
}


// Example.php

Class Page {
function add($parent_id, $title, $var, $template, array $permissions, $site_id, $activity_flag)
{
try {
Validate::type($parent_id, Validate::Integer);
Validate::type($site_id, Validate::Integer);
Validate::type($title, Validate::Title);
Validate::type($var, Validate::Variable );
Validate::type($template, Validate::TemplateFileName );
Validate::type($activity_flag, Validate::Boolean);
} catch (Exception $e) {
throw new Exception($e->getMessage());
return false;
}
}
//...
}

$p = new Page();

try {
$p->add(10,"Title", "var", "test.xml", array(), 1, 1);
} catch (Exception $e) {
echo 'Caught exception: ', $e->getMessage(), "\n";
}


Заводим класс ввалидатора и определяем в нем типы для переменных и их свойства. Теперь на входе функции достаточно сверить переданные параметры с соответствующими типами.
Например:
try {
Validate::type($login, Validate::Variable);
Validate::type($real_name, Validate::Title);
Validate::type($real_email, Validate::Email);
Validate::type($real_gender, Validate::Gender);
Validate::type($real_age, Validate::Age);
} catch (Exception $e) {
throw new Exception($e->getMessage());
return false;
}

Я не претендую с этим решением на паттерн, но возможно кому-то оно пригодится.

Updated 28.09.08
Tags:
Hubs:
-3
Comments 20
Comments Comments 20

Articles