在php中轻松进行参数验证

Every time you write some php functions or class methods, it's good practice to check input arguments and throw an exceptions, or trigger errors or warnings etc...

For example

<?php

function send_email($email, $subject, $body)
{
    if (empty($email)) {
        throw new InvalidArgumentException('Email should not be empty');
    }
    if (!is_string($email) || filter_var($email, FILTER_VALIDATE_EMAIL) === false) {
        throw new InvalidArgumentException('Email format is invalid');
    }
    if (empty($subject)) {
        throw new InvalidArgumentException('Subject should not be empty');
    }
    if (!is_string($subject)) {
        throw new InvalidArgumentException('Subject must be a string');
    }
    if (empty($body)) {
        throw new InvalidArgumentException('Body should not be empty');
    }
    if (!is_string($body)) {
        throw new InvalidArgumentException('Body must be a string');
    }

    return mail($email, $subject, $body);
}

As you can see most of this example contains validation code, whereas only one helpful line which do the job. You actually need to write a lot of code if you want to reliably protect your functions. And this is tediously.

My question is - does anybody know some good way to validate code easily? Is there any libraries that validate depends on PHP-DOC? For example:

<?php

/**
 * @param email $email
 * @param string $subject
 * @param string $body
 */
function send_email($email, $subject, $body)
{
    return mail($email, $subject, $body);
}

Thank you.

The easiest way in pure PHP is to simplify the check by using assertions:

class Assert {

    public static function isString($var) {
        if (!is_string($var)) {
            throw new InvalidArgumentException('Argument is not a string');
        }
    }

}

function foo($string) {
    Assert::isString($string);
    ...
}

You can spruce this up with introspection and/or debug backtraces to include more information in the thrown exception.

I don't think there are libraries that do this, but if you really want to go this way:

/**
 * @param email $email
 * @param string $subject
 * @param string $body
 */
function send_email($email, $subject, $body)
{
    check_arguments(__FUNCTION__, func_get_args());
    return mail($email, $subject, $body);
}

And all the fun goes inside check_arguments() which would parse the DocComment and match the declared types with the actual argument types:

function check_arguments($funcName, array $args){

  $func = new \ReflectionFunction($funcName);
  $docComment = $func->getDocComment();

  // parse the comment here into a data structure,
  // compare types and throw Exceptions on failure...
}

Have a look at Non-standard PHP library (NSPL). With its args module you can do the following:

use const 
spl\args
otEmpty;
use const 
spl\args\string;
use function 
spl\args\expects;
use function 
spl\args\expectsAll;

function validEmail($email)
{
    return filter_var($email, FILTER_VALIDATE_EMAIL) === false
}

function send_email($email, $subject, $body)
{
    expectsAll([nonEmpty, string], [$email, $subject, $body]);
    expects('validEmail', $email);

    return mail($email, $subject, $body);
}