Working basic validations with tests

This commit is contained in:
Vance Lucas 2013-01-26 21:30:55 -06:00
commit ef40f0e187
9 changed files with 489 additions and 0 deletions

3
.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
.DS_Store
composer.phar
vendor

13
.travis.yml Normal file
View File

@ -0,0 +1,13 @@
# see http://about.travis-ci.org/docs/user/languages/php/ for more hints
language: php
# list any PHP version you want to test against
php:
- 5.3
- 5.4
before_script:
- composer install
# Script to run tests
script: phpunit

32
LICENSE.txt Normal file
View File

@ -0,0 +1,32 @@
The BSD 3-Clause License
http://opensource.org/licenses/BSD-3-Clause
Copyright (c) 2013, Vance Lucas
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the Vance Lucas nor the names of its contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

22
README.md Normal file
View File

@ -0,0 +1,22 @@
## Valitron: Easy Validation That Doesn't Suck
Valitron is a simple, minimal and elegant stand-alone validation library
with NO dependencies. Valitron uses simple, straightforward validation
methods with a focus on readable and concise syntax. Valitron is the
simple and pragmatic validation library you've been loking for.
[![Build
Status](https://secure.travis-ci.org/vlucas/valitron.png)](http://travis-ci.org/vlucas/valitron)
Contributing
------------
1. Fork it
2. Create your feature branch (`git checkout -b my-new-feature`)
3. Make your changes
4. Run the tests, adding new ones for your own code if necessary (`phpunit`)
5. Commit your changes (`git commit -am 'Added some feature'`)
6. Push to the branch (`git push origin my-new-feature`)
7. Create new Pull Request

24
composer.json Normal file
View File

@ -0,0 +1,24 @@
{
"name": "vlucas/valitron",
"version": "1.0.0",
"type": "library",
"description": "Simple, elegant, stand-alone validation library with NO dependencies",
"keywords": ["validation", "validator", "valid"],
"homepage": "http://github.com/vlucas/valitron",
"license" : "BSD",
"authors" : [
{
"name": "Vance Lucas",
"email": "vance@vancelucas.com",
"homepage": "http://www.vancelucas.com"
}
],
"require": {
"php": ">=5.3.2"
},
"autoload": {
"psr-0": {
"Valitron": "src/"
}
}
}

18
phpunit.xml Normal file
View File

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<phpunit backupGlobals="false"
backupStaticAttributes="false"
colors="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
processIsolation="false"
stopOnFailure="false"
syntaxCheck="false"
bootstrap="tests/bootstrap.php"
>
<testsuites>
<testsuite name="Valitron Test Suite">
<directory suffix=".php">tests/Valitron</directory>
</testsuite>
</testsuites>
</phpunit>

298
src/Valitron/Validator.php Normal file
View File

@ -0,0 +1,298 @@
<?php
namespace Valitron;
/**
* Validation Class
*
* Validates input against certian criteria
*
* @package Valitron
* @author Vance Lucas <vance@vancelucas.com>
* @link http://www.vancelucas.com/
*/
class Validator
{
protected $_fields = array();
protected $_errors = array();
protected $_validations = array();
protected static $_rules = array();
/**
* Setup validation
*/
public function __construct($data, $fields = array())
{
// Allows filtering of used input fields against optional second array of field names allowed
// This is useful for limiting raw $_POST or $_GET data to only known fields
foreach($data as $field => $value) {
if(empty($fields) || (!empty($fields) && in_array($field, $fields))) {
$this->_fields[$field] = $value;
}
}
}
/**
* Register new validation rule callback
*/
public static function addRule($name, $callback)
{
if(!is_callable($callback)) {
throw new \InvalidArgumentException("Second argument must be a valid callback. Given argument was not callable.");
}
static::$_rules[$name] = $callback;
}
/**
* Convenience method to add validation rules
*/
public function rule($rule, $fields, $message = null)
{
if(!isset(static::$_rules[$rule])) {
throw new \InvalidArgumentException("Rule '" . $rule . "' has not been registered with " . __CLASS__ . "::addRule().");
}
$this->_validations[] = array(
'rule' => $rule,
'fields' => (array) $fields,
'message' => $message
);
return $this;
}
/**
* Convenience method to add validation rules
*/
public function __call($rule, array $args)
{
if(!isset(static::$_rules[$rule])) {
throw new \InvalidArgumentException("Method '" . $rule . "' does not exist, or rule '" . $rule . "' has not been registered with " . __CLASS__ . "::addRule().");
}
array_unshift($args, $rule);
call_user_func_array(array($this, 'rule'), $args);
return $this;
}
/**
* Required field validator
*/
public function validateRequired($field, $value, array $params = array())
{
if(is_null($value)) {
return false;
} elseif(is_string($value) and trim($value) === '') {
return false;
}
return true;
}
/**
* Validate that an attribute is a valid IP address
*
* @param string $field
* @param mixed $value
* @return bool
*/
public static function validateIp($field, $value)
{
return filter_var($value, FILTER_VALIDATE_IP) !== false;
}
/**
* Validate that an attribute is a valid e-mail address
*
* @param string $field
* @param mixed $value
* @return bool
*/
public static function validateEmail($field, $value)
{
return filter_var($value, FILTER_VALIDATE_EMAIL) !== false;
}
/**
* Validate that an attribute is a valid URL by syntax
*
* @param string $field
* @param mixed $value
* @return bool
*/
public static function validateUrl($field, $value)
{
return filter_var($value, FILTER_VALIDATE_URL) !== false;
}
/**
* Validate that an attribute is an active URL by verifying DNS record
*
* @param string $field
* @param mixed $value
* @return bool
*/
public static function validateActiveUrl($field, $value)
{
$url = str_replace(array('http://', 'https://', 'ftp://'), '', strtolower($value));
return checkdnsrr($url);
}
/**
* Validate that an attribute contains only alphabetic characters
*
* @param string $field
* @param mixed $value
* @return bool
*/
public static function validateAlpha($field, $value)
{
return preg_match('/^([a-z])+$/i', $value);
}
/**
* Validate that an attribute contains only alpha-numeric characters.
*
* @param string $field
* @param mixed $value
* @return bool
*/
public static function validateAlphaNum($field, $value)
{
return preg_match('/^([a-z0-9])+$/i', $value);
}
/**
* Validate that an attribute contains only alpha-numeric characters, dashes, and underscores.
*
* @param string $field
* @param mixed $value
* @return bool
*/
public static function validateAlphaDash($field, $value)
{
return preg_match('/^([-a-z0-9_-])+$/i', $value);
}
/**
* Validate that an attribute passes a regular expression check.
*
* @param string $field
* @param mixed $value
* @return bool
*/
public static function validateRegex($field, $value, $params)
{
return preg_match($params[0], $value);
}
/**
* Validate that an attribute is a valid date.
*
* @param string $field
* @param mixed $value
* @return bool
*/
public static function validateDate($field, $value)
{
return strtotime($value) !== false;
}
/**
* Validate that an attribute matches a date format.
*
* @param string $field
* @param mixed $value
* @param array $params
* @return bool
*/
public static function validateDateFormat($field, $value, $params)
{
$parsed = date_parse_from_format($params[0], $value);
return $parsed['error_count'] === 0;
}
/**
* Validate the date is before a given date.
*
* @param string $field
* @param mixed $value
* @param array $params
* @return bool
*/
public static function validateBefore($field, $value, $params)
{
return strtotime($value) < strtotime($params[0]);
}
/**
* Validate the date is after a given date.
*
* @param string $field
* @param mixed $value
* @param array $params
* @return bool
*/
public static function validateAfter($field, $value, $params)
{
return strtotime($value) > strtotime($params[0]);
}
/**
* Get array of fields and data
*/
public function data($field = null)
{
return $this->_fields;
}
/**
* Get array of error messages
*/
public function errors($field = null)
{
if($field !== null) {
return isset($this->_errors[$field]) ? $this->_errors[$field] : false;
}
return $this->_errors;
}
/**
* Add an error to error messages array
*/
public function error($field, $msg)
{
// Add to error array
$this->_errors[$field][] = sprintf($msg, $field);
}
/**
* Run validations and return boolean result
*
* @return boolean
*/
public function validate()
{
foreach($this->_validations as $v) {
foreach($v['fields'] as $field) {
$value = isset($this->_fields[$field]) ? $this->_fields[$field] : null;
$result = call_user_func(static::$_rules[$v['rule']], $field, $value);
if(!$result) {
$this->error($field, $v['message']);
}
}
}
return count($this->errors()) === 0;
}
}
// Register default validations here so they can be overridden by user after include
$class = __NAMESPACE__ . '\Validator';
$class::addRule('required', array($class, 'validateRequired'));
$class::addRule('email', array($class, 'validateEmail'));
$class::addRule('url', array($class, 'validateUrl'));

View File

@ -0,0 +1,61 @@
<?php
use Valitron\Validator;
class ValidateTest extends \PHPUnit_Framework_TestCase
{
public function testValidWithNoRules()
{
$v = new Validator(array('name' => 'Chester Tester'));
$this->assertTrue($v->validate());
}
public function testOptionalFieldFilter()
{
$v = new Validator(array('foo' => 'bar', 'bar' => 'baz'), array('foo'));
$this->assertEquals($v->data(), array('foo' => 'bar'));
}
public function testRequired()
{
$v = new Validator(array('name' => 'Chester Tester'));
$v->required('name');
$this->assertTrue($v->validate());
$this->assertSame(1, count($v->errors('name')));
}
public function testRequiredNonExistentField()
{
$v = new Validator(array('name' => 'Chester Tester'));
$v->required('nonexistent_field');
$this->assertFalse($v->validate());
}
public function testEmailValid()
{
$v = new Validator(array('name' => 'Chester Tester', 'email' => 'chester@tester.com'));
$v->email('email');
$this->assertTrue($v->validate());
}
public function testEmailInvalid()
{
$v = new Validator(array('name' => 'Chester Tester', 'email' => 'chestertesterman'));
$v->rule('email', 'email');
$this->assertFalse($v->validate());
}
public function testUrlValid()
{
$v = new Validator(array('website' => 'http://google.com'));
$v->rule('url', 'website');
$this->assertTrue($v->validate());
}
public function testUrlInvalid()
{
$v = new Validator(array('website' => 'shoobedobop'));
$v->rule('url', 'website');
$this->assertFalse($v->validate());
}
}

18
tests/bootstrap.php Normal file
View File

@ -0,0 +1,18 @@
<?php
error_reporting(-1);
date_default_timezone_set('UTC');
/**
* Path trickery ensures test suite will always run, standalone or within
* another composer package. Designed to find composer autoloader and require
*/
$vendorPos = strpos(__DIR__, 'vendor/vlucas/valitron');
if($vendorPos !== false) {
// Package has been cloned within another composer package, resolve path to autoloader
$vendorDir = substr(__DIR__, 0, $vendorPos) . 'vendor/';
$loader = require $vendorDir . 'autoload.php';
} else {
// Package itself (cloned standalone)
$loader = require __DIR__.'/../vendor/autoload.php';
}