From 4f35f96e540e8990a7ab6078d677e072e9c4f7e8 Mon Sep 17 00:00:00 2001 From: Michael Wallner Date: Tue, 19 Feb 2013 13:35:01 +0100 Subject: [PATCH 1/1] initial commit --- .gitignore | 1 + LICENSE | 22 +++++ src/Assert.php | 241 ++++++++++++++++++++++++++++++++++++++++++++++ src/Testable.php | 13 +++ src/Validator.php | 16 +++ 5 files changed, 293 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 src/Assert.php create mode 100644 src/Testable.php create mode 100644 src/Validator.php diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..14bc68c --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/nbproject/private/ \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..c26019a --- /dev/null +++ b/LICENSE @@ -0,0 +1,22 @@ +Copyright (c) 2013, Michael Wallner . +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. + +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 OWNER 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. diff --git a/src/Assert.php b/src/Assert.php new file mode 100644 index 0000000..9630fa2 --- /dev/null +++ b/src/Assert.php @@ -0,0 +1,241 @@ + + * $testable->assert() + * ->that("name")->isLen(3, 40, "must be between 3 and 40 characters long") + * ->that("email")->isEmail("is not valid"); + * + */ +class Assert +{ + /** + * Testable properties + * @var array + */ + private $properties; + + /** + * The name of the currently inspected property + * @var string + */ + private $inspectedProperty; + + /** + * Whether the argument to when() was true + * @var bool + */ + private $inspectCondition = true; + + /** + * Whether assertions cause exceptions of this type + * @var string + */ + private $exceptionClass; + + /** + * Failed assertions + * @var array + */ + private $validationErrors = array(); + + /** + * @param \ascertain\Testable $testable + * @param string $exceptionClass + */ + function __construct(Testable $testable, $exceptionClass = null) { + $this->properties = $testable->export(); + $this->exceptionClass = $exceptionClass; + } + + /** + * @return int count of failed assertions + */ + function hasErrors() { + return count($this->validationErrors); + } + + /** + * @return array of failed assertions + */ + function getErrors() { + return $this->validationErrors; + } + + /** + * Reset failed assertions + * @return \ascertain\Assert + */ + function resetErrors() { + $this->validationErrors = array(); + return $this; + } + + /** + * Set the currently inspected property + * @param string $property + * @return \ascertain\Assert + */ + function that($property) { + $this->inspectCondition = true; + $this->inspectedProperty = $property; + return $this; + } + + /** + * The following assertions are only tested if the argument is true + * @param bool $condition + * @return \ascertain\Assert + */ + function when($condition) { + $this->inspectCondition = $condition; + return $this; + } + + /** + * !strcmp($v, $c) + * @param string $v + * @param string $c + * @return bool + */ + function test($v, $c) { + return !strcmp($v, $c); + } + + /** + * !strlen($v) + * @param string $v + * @return bool + */ + function testNothing($v) { + return !strlen($v); + } + + /** + * is_numeric($v) + * @param string $v + * @return bool + */ + function testNumeric($v) { + return is_numeric($v); + } + + /** + * Test wheter string representations of original and the int-cast equal + * @param mixed $v + * @return bool + */ + function testInteger($v) { + return ((string) $v) === ((string)(int) $v); + } + + /** + * Test whether the argument is scalar + * @param mixed $v + * @param bool $strictNulls + * @return bool + */ + function testScalar($v, $strictNulls = false) { + return is_scalar($v) && (!$strictNulls || !isset($v)); + } + + /** + * Test wheter the argument constists only of printable characters + * @param string $v + * @return bool + */ + function testPrintable($v) { + return preg_match("/^[[:print:]\\P{Cc}]*\$/u", $v) > 0; + } + + /** + * Test wheter the string length is between a certain range + * @param string $v + * @param int $min + * @param int $max + * @return bool + */ + function testLen($v, $min, $max) { + return $this->testRange(function_exists("mb_strlen") ? mb_strlen($v) : strlen($v), $min, $max); + } + + /** + * Test wheter a value is between a certain range + * @param mixed $v + * @param mixed $min + * @param mixed $max + * @return bool + */ + function testRange($v, $min, $max) { + return $v >= $min && $v <= $max; + } + + /** + * Test for a valid email address with FILTER_VALIDATE_EMAIL + * @param stting $v + * @param int $options + * @return bool + */ + function testEmail($v, $options = null) { + return filter_var($v, FILTER_VALIDATE_EMAIL, $options) !== false; + } + + /** + * Test for a valid URL with FILTER_VALIDATE_URL + * @param string $v + * @param int $options + * @return bool + */ + function testUrl($v, $options = null) { + return filter_var($v, FILTER_VALIDATE_URL, $options) !== false; + } + + /** + * Test whether a string contains another string + * @param type $v haystack + * @param type $n needle + * @param bool $ci case-sensitive + * @return bool + */ + function testContains($v, $n, $ci = true) { + return ($ci ? strpos($v, $n) : stripos($v, $n)) !== false; + } + + /** + * Thest if a regular expression matches + * @param string $v + * @param stirng $regex + * @param int $flags + * @return int + */ + function testRegex($v, $regex, $flags = 0) { + return preg_match($regex, $v, null, $flags); + } + + /** + * Semantic is(Not) wrapper to the assertions + * @param string $method + * @param array $args + * @return \ascertain\Assert + * @throws InvalidArgumentException (or rahter the configured exception) + */ + function __call($method, $args) { + if ($this->inspectCondition && preg_match("/^is(Not)?(.*)\$/i", $method, $match)) { + list(, $not, $test) = $match; + + $error = array_pop($args); + array_unshift($args, $this->properties[$this->inspectedProperty]); + if (call_user_func_array(array($this, "test$test"), $args) == !!$not) { + if (($exception = $this->exceptionClass)) { + throw new $exception("$this->inspectedProperty $error"); + } + $this->validationErrors[$this->inspectedProperty][] = $error; + } + } + return $this; + } + +} diff --git a/src/Testable.php b/src/Testable.php new file mode 100644 index 0000000..869d468 --- /dev/null +++ b/src/Testable.php @@ -0,0 +1,13 @@ +