diff --git a/lang/en_utf8/debug.php b/lang/en_utf8/debug.php
index 43f630bb504..3f7bf2b6f3c 100644
--- a/lang/en_utf8/debug.php
+++ b/lang/en_utf8/debug.php
@@ -15,6 +15,7 @@ $string['erroroccur'] = 'An error has occurred during this process';
 $string['fixsetting'] = 'Please fix your settings in config.php: <p>You have:</p> <p>\$CFG->dirroot = \'$a->current\';</p> <p>but it should be:</p> <p>\$CFG->dirroot = \'$a->found\';</p>';
 $string['invalideventdata'] = 'Incorrect eventadata submitted: $a';
 $string['invalidarraysize'] = 'Incorrect size of arrays in params of $a';
+$string['invalidparameter'] = 'Invalid parameter value detected, execution can not continue.';
 $string['missingconfigversion'] = 'Config table does not contain version, can not continue, sorry.';
 $string['mustbeoveride'] = 'Abstract $a method must be overriden.';
 $string['morethanonerecordinfetch'] = 'Found more than one record in fetch() !';
diff --git a/lib/moodlelib.php b/lib/moodlelib.php
index edf5400bbcc..ec7d6dc8771 100644
--- a/lib/moodlelib.php
+++ b/lib/moodlelib.php
@@ -374,6 +374,40 @@ function optional_param($parname, $default=NULL, $type=PARAM_CLEAN) {
     return clean_param($param, $type);
 }
 
+/**
+ * Strict validation of parameter values, the values are only converted
+ * to requested PHP type. Internally it is using clean_param, the values
+ * before and after cleaning must be equal - otherwise
+ * an invalid_parameter_exception is thrown.
+ * Onjects and classes are not accepted.
+ *
+ * @param mixed $param
+ * @param int $type PARAM_ constant
+ * @param bool $allownull are nulls valid value?
+ * @param string $debuginfo optional debug information
+ * @return mixed the $param value converted to PHP type or invalid_parameter_exception
+ */
+function validate_param($param, $type, $allownull=true, $debuginfo='') {
+    if (is_null($param)) {
+        if ($allownull) {
+            return null;
+        } else {
+            throw new invalid_parameter_exception($debuginfo);
+        }
+    }
+    if (is_array($param) or is_object($param)) {
+        throw new invalid_parameter_exception($debuginfo);
+    }
+
+    $cleaned = clean_param($param, $type);
+    if ((string)$param !== (string)$cleaned) {
+        // conversion to string is usually lossless
+        throw new invalid_parameter_exception($debuginfo);
+    }
+
+    return $cleaned;
+}
+
 /**
  * Used by {@link optional_param()} and {@link required_param()} to
  * clean the variables and/or cast to specific types, based on
diff --git a/lib/setuplib.php b/lib/setuplib.php
index 791f3f28b9f..5f3a5db3ec9 100644
--- a/lib/setuplib.php
+++ b/lib/setuplib.php
@@ -121,6 +121,22 @@ class coding_exception extends moodle_exception {
     }
 }
 
+/**
+ * Exception indicating malformed parameter problem.
+ * This exception is not supposed to be thrown when processing
+ * user submitted data in forms. It is more suitable
+ * for WS and other low level stuff.
+ */
+class invalid_parameter_exception extends moodle_exception {
+    /**
+     * Constructor
+     * @param string $debuginfo some detailed information
+     */
+    function __construct($debuginfo=null) {
+        parent::__construct('invalidparameter', 'debug', '', null, $debuginfo);
+    }
+}
+
 /**
  * An exception that indicates something really weird happended. For example,
  * if you do switch ($context->contextlevel), and have one case for each
diff --git a/lib/simpletest/testmoodlelib.php b/lib/simpletest/testmoodlelib.php
index 76a7717cd98..af3384d8779 100644
--- a/lib/simpletest/testmoodlelib.php
+++ b/lib/simpletest/testmoodlelib.php
@@ -228,8 +228,7 @@ class moodlelib_test extends UnitTestCase {
         $this->assertEqual(array('gecko', 'gecko19'), get_browser_version_classes());
     }
 
-    function test_optional_param()
-    {
+    function test_optional_param() {
         $_POST['username'] = 'post_user';
         $_GET['username'] = 'get_user';
         $this->assertEqual(optional_param('username', 'default_user', PARAM_CLEAN), 'post_user');
@@ -271,8 +270,7 @@ class moodlelib_test extends UnitTestCase {
      * @param int $type expected format of param after cleaning.
      * @return mixed
      */
-    function test_clean_param()
-    {
+    function test_clean_param() {
         global $CFG;
         // Test unknown parameter type
 
@@ -299,6 +297,45 @@ class moodlelib_test extends UnitTestCase {
         $this->assertEqual(clean_param('course/view.php?id=3', PARAM_LOCALURL), 'course/view.php?id=3');
     }
 
+    function test_validate_param() {
+        try {
+            $param = validate_param('11a', PARAM_INT);
+            $this->fail('invalid_parameter_exception expected');
+        } catch (invalid_parameter_exception $ex) {
+            $this->assertTrue(true);
+        }
+        try {
+            $param = validate_param('11', PARAM_INT);
+            $this->assertEqual($param, 11);
+        } catch (invalid_parameter_exception $ex) {
+            $this->fail('invalid_parameter_exception not expected');
+        }
+        try {
+            $param = validate_param(null, PARAM_INT, false);
+            $this->fail('invalid_parameter_exception expected');
+        } catch (invalid_parameter_exception $ex) {
+            $this->assertTrue(true);
+        }
+        try {
+            $param = validate_param(null, PARAM_INT, true);
+            $this->assertTrue($param===null);
+        } catch (invalid_parameter_exception $ex) {
+            $this->fail('invalid_parameter_exception expected');
+        }
+        try {
+            $param = validate_param(array(), PARAM_INT);
+            $this->fail('invalid_parameter_exception expected');
+        } catch (invalid_parameter_exception $ex) {
+            $this->assertTrue(true);
+        }
+        try {
+            $param = validate_param(new stdClass, PARAM_INT);
+            $this->fail('invalid_parameter_exception expected');
+        } catch (invalid_parameter_exception $ex) {
+            $this->assertTrue(true);
+        }
+    }
+
     function test_make_user_directory() {
         global $CFG;