initial commit
authorMichael Wallner <mike@php.net>
Tue, 19 Feb 2013 12:35:01 +0000 (13:35 +0100)
committerMichael Wallner <mike@php.net>
Tue, 19 Feb 2013 12:35:01 +0000 (13:35 +0100)
.gitignore [new file with mode: 0644]
LICENSE [new file with mode: 0644]
src/Assert.php [new file with mode: 0644]
src/Testable.php [new file with mode: 0644]
src/Validator.php [new file with mode: 0644]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..14bc68c
--- /dev/null
@@ -0,0 +1 @@
+/nbproject/private/
\ No newline at end of file
diff --git a/LICENSE b/LICENSE
new file mode 100644 (file)
index 0000000..c26019a
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,22 @@
+Copyright (c) 2013, Michael Wallner <mike@php.net>.
+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 (file)
index 0000000..9630fa2
--- /dev/null
@@ -0,0 +1,241 @@
+<?php
+
+namespace ascertain;
+
+/**
+ * Be sure to always pass an error string as last argument to the assertion:
+ * <code>
+ *     $testable->assert()
+ *             ->that("name")->isLen(3, 40, "must be between 3 and 40 characters long")
+ *             ->that("email")->isEmail("is not valid");
+ * </code>
+ */
+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 (file)
index 0000000..869d468
--- /dev/null
@@ -0,0 +1,13 @@
+<?php
+
+namespace ascertain;
+
+/**
+ * Implement this interface to use the \ascertain\Validator trait
+ */
+interface Testable {
+       /**
+        * @returns array of properties
+        */
+       function export();
+}
diff --git a/src/Validator.php b/src/Validator.php
new file mode 100644 (file)
index 0000000..68eb9a7
--- /dev/null
@@ -0,0 +1,16 @@
+<?php
+
+namespace ascertain;
+
+/**
+ * Use this trait in an \ascertain\Testable class
+ */
+trait Validator {
+       /**
+        * @param string $e Exception class name
+        * @return \ascertain\Assert
+        */
+       function assert($e = "\\InvalidArgumentException") {
+               return new Assert($this, $e);
+       }
+}