INFRA-132 - phpcbf Drupal.WhiteSpace.ScopeIndent.IncorrectExact
[civicrm-core.git] / api / v3 / utils.php
index b16b49cb078e0fe119c1b4ce1f059266b1b97d07..c2ada33f336073e367ef3603ec0bc87a0fb7f28a 100644 (file)
@@ -23,7 +23,7 @@
   | GNU Affero General Public License or the licensing of CiviCRM,     |
   | see the CiviCRM license FAQ at http://civicrm.org/licensing        |
   +--------------------------------------------------------------------+
-*/
+ */
 
 /**
  * File for CiviCRM APIv3 utilitity functions
@@ -54,9 +54,6 @@ function _civicrm_api3_initialize() {
  *   String DAO to check for required fields (create functions only).
  * @param array $keyoptions
  *   List of required fields options. One of the options is required.
- *
- * @return null or throws error if there the required fields not present
- * @
  */
 function civicrm_api3_verify_one_mandatory($params, $daoName = NULL, $keyoptions = array()) {
   $keys = array(array());
@@ -76,11 +73,6 @@ function civicrm_api3_verify_one_mandatory($params, $daoName = NULL, $keyoptions
  * @param array $keys
  *   List of required fields. A value can be an array denoting that either this or that is required.
  * @param bool $verifyDAO
- *
- * @throws API_Exception
- * @return null or throws error if there the required fields not present
- *
- * @todo see notes on _civicrm_api3_check_required_fields regarding removing $daoName param
  */
 function civicrm_api3_verify_mandatory($params, $daoName = NULL, $keys = array(), $verifyDAO = TRUE) {
 
@@ -133,11 +125,9 @@ function civicrm_api3_verify_mandatory($params, $daoName = NULL, $keys = array()
 
 /**
  *
- * @param <type> $data
+ * @param $msg
  * @param array $data
- *
- * @throws API_Exception
- * @return array <type>
+ * @return array
  */
 function civicrm_api3_create_error($msg, $data = array()) {
   $data['is_error'] = 1;
@@ -169,7 +159,7 @@ function civicrm_api3_create_error($msg, $data = array()) {
  *   Additional values to be added to top level of result array(.
  *   - this param is currently used for legacy behaviour support
  *
- * @return array $result
+ * @return array
  */
 function civicrm_api3_create_success($values = 1, $params = array(), $entity = NULL, $action = NULL, &$dao = NULL, $extraReturnValues = array()) {
   $result = array();
@@ -270,6 +260,8 @@ function civicrm_api3_create_success($values = 1, $params = array(), $entity = N
 
 /**
  * Load the DAO of the entity
+ * @param $entity
+ * @return bool
  */
 function _civicrm_api3_load_DAO($entity) {
   $dao = _civicrm_api3_get_DAO($entity);
@@ -284,8 +276,8 @@ function _civicrm_api3_load_DAO($entity) {
  * return the DAO of the function or Entity
  * @param string $name
  *   Either a function of the api (civicrm_{entity}_create or the entity name.
- * return the DAO name to manipulate this function
- * eg. "civicrm_api3_contact_create" or "Contact" will return "CRM_Contact_BAO_Contact"
+ *   return the DAO name to manipulate this function
+ *   eg. "civicrm_api3_contact_create" or "Contact" will return "CRM_Contact_BAO_Contact"
  * @return mixed|string
  */
 function _civicrm_api3_get_DAO($name) {
@@ -352,8 +344,8 @@ function _civicrm_api3_get_DAO($name) {
  * return the DAO of the function or Entity
  * @param string $name
  *   Is either a function of the api (civicrm_{entity}_create or the entity name.
- * return the DAO name to manipulate this function
- * eg. "civicrm_contact_create" or "Contact" will return "CRM_Contact_BAO_Contact"
+ *   return the DAO name to manipulate this function
+ *   eg. "civicrm_contact_create" or "Contact" will return "CRM_Contact_BAO_Contact"
  * @return mixed
  */
 function _civicrm_api3_get_BAO($name) {
@@ -373,7 +365,7 @@ function _civicrm_api3_get_BAO($name) {
 
 /**
  *  Recursive function to explode value-separated strings into arrays
- *
+ * @param $values
  */
 function _civicrm_api3_separate_values(&$values) {
   $sp = CRM_Core_DAO::VALUE_SEPARATOR;
@@ -382,7 +374,7 @@ function _civicrm_api3_separate_values(&$values) {
       _civicrm_api3_separate_values($value);
     }
     elseif (is_string($value)) {
-      if ($key == 'case_type_id'){// this is to honor the way case API was originally written
+      if ($key == 'case_type_id') {// this is to honor the way case API was originally written
         $value = trim(str_replace($sp, ',', $value), ',');
       }
       elseif (strpos($value, $sp) !== FALSE) {
@@ -414,7 +406,7 @@ function _civicrm_api3_filter_fields_for_bao($entity, &$params, &$values) {
  * @param array $params
  * @param array $values
  *
- * @return Bool $valueFound
+ * @return Bool
  */
 function _civicrm_api3_store_values(&$fields, &$params, &$values) {
   $valueFound = FALSE;
@@ -437,7 +429,7 @@ function _civicrm_api3_store_values(&$fields, &$params, &$values) {
  *  others that use the query object. Note that this function passes permission information in.
  *  The others don't
  *
- * Ideally this would be merged with _civicrm_get_query_object but we need to resolve differences in what the
+ * Ideally this would be merged with _civicrm_get_query_object but we need to resolve differences in what the
  * 2 variants call
  * @param $entity
  * @param array $params
@@ -447,7 +439,7 @@ function _civicrm_api3_store_values(&$fields, &$params, &$values) {
  * @param bool $getCount
  *   Are we just after the count.
  *
- * @return
+ * @return array
  */
 function _civicrm_api3_get_using_query_object($entity, $params, $additional_options = array(), $getCount = NULL) {
 
@@ -525,7 +517,8 @@ function _civicrm_api3_get_using_query_object($entity, $params, $additional_opti
     $getCount,
     $skipPermissions
   );
-  if ($getCount) { // only return the count of contacts
+  if ($getCount) {
+    // only return the count of contacts
     return $entities;
   }
 
@@ -540,20 +533,21 @@ function _civicrm_api3_get_using_query_object($entity, $params, $additional_opti
  * @param array $params
  * @param string $mode
  * @param string $entity
- * @return CRM_Core_DAO query object
+ * @return array
+ *   [CRM_Core_DAO|CRM_Contact_BAO_Query]
  */
 function _civicrm_api3_get_query_object($params, $mode, $entity) {
-  $options          = _civicrm_api3_get_options_from_params($params, TRUE, $entity, 'get');
-  $sort             = CRM_Utils_Array::value('sort', $options, NULL);
-  $offset           = CRM_Utils_Array::value('offset', $options);
-  $rowCount         = CRM_Utils_Array::value('limit', $options);
-  $inputParams      = CRM_Utils_Array::value('input_params', $options, array());
+  $options = _civicrm_api3_get_options_from_params($params, TRUE, $entity, 'get');
+  $sort = CRM_Utils_Array::value('sort', $options, NULL);
+  $offset = CRM_Utils_Array::value('offset', $options);
+  $rowCount = CRM_Utils_Array::value('limit', $options);
+  $inputParams = CRM_Utils_Array::value('input_params', $options, array());
   $returnProperties = CRM_Utils_Array::value('return', $options, NULL);
   if (empty($returnProperties)) {
     $returnProperties = CRM_Contribute_BAO_Query::defaultReturnProperties($mode);
   }
 
-  $newParams = CRM_Contact_BAO_Query::convertFormValues($inputParams);
+  $newParams = CRM_Contact_BAO_Query::convertFormValues($inputParams, 0, FALSE, $entity);
   $query = new CRM_Contact_BAO_Query($newParams, $returnProperties, NULL,
     FALSE, FALSE, $mode,
     empty($params['check_permissions'])
@@ -698,13 +692,14 @@ function _civicrm_api3_apply_filters_to_dao($filterField, $filterValue, &$dao) {
  *   Params array as passed into civicrm_api.
  * @param bool $queryObject
  *   Is this supporting a queryobject api (e.g contact) - if so we support more options.
- * for legacy report & return a unique fields array
+ *   for legacy report & return a unique fields array
  *
  * @param string $entity
  * @param string $action
  *
  * @throws API_Exception
- * @return array $options options extracted from params
+ * @return array
+ *   options extracted from params
  */
 function _civicrm_api3_get_options_from_params(&$params, $queryObject = FALSE, $entity = '', $action = '') {
   $is_count = FALSE;
@@ -780,7 +775,7 @@ function _civicrm_api3_get_options_from_params(&$params, $queryObject = FALSE, $
       $legacyreturnProperties[substr($n, 7)] = $v;
     }
     elseif ($n == 'id') {
-      $inputParams[$entity .  '_id'] = $v;
+      $inputParams[$entity . '_id'] = $v;
     }
     elseif (in_array($n, $otherVars)) {
     }
@@ -821,6 +816,11 @@ function _civicrm_api3_apply_options_to_dao(&$params, &$dao, $entity) {
 /**
  * build fields array. This is the array of fields as it relates to the given DAO
  * returns unique fields as keys by default but if set but can return by DB fields
+ *
+ * @param CRM_Core_DAO $bao
+ * @param bool $unique
+ *
+ * @return array
  */
 function _civicrm_api3_build_fields_array(&$bao, $unique = TRUE) {
   $fields = $bao->fields();
@@ -842,9 +842,10 @@ function _civicrm_api3_build_fields_array(&$bao, $unique = TRUE) {
 /**
  * build fields array. This is the array of fields as it relates to the given DAO
  * returns unique fields as keys by default but if set but can return by DB fields
- * @param CRM_Core_BAO $bao
  *
- * @return mixed
+ * @param CRM_Core_DAO $bao
+ *
+ * @return array
  */
 function _civicrm_api3_get_unique_name_array(&$bao) {
   $fields = $bao->fields();
@@ -865,9 +866,6 @@ function _civicrm_api3_get_unique_name_array(&$bao) {
  * @param bool $autoFind
  *
  * @return array
- *
- * @static void
- * @access public
  */
 function _civicrm_api3_dao_to_array($dao, $params = NULL, $uniqueFields = TRUE, $entity = "", $autoFind = TRUE) {
   $result = array();
@@ -912,9 +910,9 @@ function _civicrm_api3_dao_to_array($dao, $params = NULL, $uniqueFields = TRUE,
  * && it can take custom fields & there is the string 'custom' in their return request we get them all, they are filtered on the way out
  * @todo filter so only required fields are queried
  *
- * @param array $params
  * @param string $entity
  *   Entity name in CamelCase.
+ * @param array $params
  *
  * @return bool
  */
@@ -937,10 +935,6 @@ function _civicrm_api3_custom_fields_are_required($entity, $params) {
  * @param array $values
  *   (reference) array.
  * @param array|bool $uniqueFields
- *
- * @return array
- * @static void
- * @access public
  */
 function _civicrm_api3_object_to_array(&$dao, &$values, $uniqueFields = FALSE) {
 
@@ -954,6 +948,9 @@ function _civicrm_api3_object_to_array(&$dao, &$values, $uniqueFields = FALSE) {
 
 /**
  * Wrapper for _civicrm_object_to_array when api supports unique fields
+ * @param $dao
+ * @param $values
+ * @return array
  */
 function _civicrm_api3_object_to_array_unique_fields(&$dao, &$values) {
   return _civicrm_api3_object_to_array($dao, $values, TRUE);
@@ -1126,10 +1123,9 @@ function formatCheckBoxField(&$checkboxFieldValue, $customFieldLabel, $entity) {
  *
  * @daoName string DAO to check params agains
  *
- * @return bool should the missing fields be returned as an array (core error created as default)
- *
- * @return bool true if all fields present, depending on $result a core error is created of an array of missing fields is returned
- * @access public
+ * @return bool
+ *   Sshould the missing fields be returned as an array (core error created as default)
+ *   true if all fields present, depending on $result a core error is created of an array of missing fields is returned
  */
 function _civicrm_api3_check_required_fields($params, $daoName, $return = FALSE) {
   //@deprecated - see notes
@@ -1296,7 +1292,8 @@ function _civicrm_api3_basic_create_fallback($bao_name, &$params) {
  * @param string $bao_name
  * @param array $params
  *
- * @return array API result array
+ * @return array
+ *   API result array
  * @throws API_Exception
  */
 function _civicrm_api3_basic_delete($bao_name, &$params) {
@@ -1408,7 +1405,8 @@ function _civicrm_api3_validate_fields($entity, $action, &$params, $fields, $err
         _civicrm_api3_validate_date($params, $fieldName, $fieldInfo);
         break;
 
-      case 32://blob
+      case 32:
+        //blob
         _civicrm_api3_validate_html($params, $fieldName, $fieldInfo);
         break;
 
@@ -1417,15 +1415,22 @@ function _civicrm_api3_validate_fields($entity, $action, &$params, $fields, $err
         break;
 
       case CRM_Utils_Type::T_MONEY:
-        if (!CRM_Utils_Rule::money($params[$fieldName]) && !empty($params[$fieldName])) {
-          throw new Exception($fieldName . " is  not a valid amount: " . $params[$fieldName]);
+        list($fieldValue, $op) = _civicrm_api3_field_value_check($params, $fieldName);
+        if (strpos($op, 'NULL') !== FALSE || strpos($op, 'EMPTY') !== FALSE) {
+          break;
+        }
+        foreach ((array) $fieldValue as $fieldvalue) {
+          if (!CRM_Utils_Rule::money($fieldvalue) && !empty($fieldvalue)) {
+            throw new Exception($fieldName . " is  not a valid amount: " . $params[$fieldName]);
+          }
         }
+        break;
     }
 
     // intensive checks - usually only called after DB level fail
     if (!empty($errorMode) && strtolower($action) == 'create') {
       if (!empty($fieldInfo['FKClassName'])) {
-        if (!empty($params[$fieldName])) {
+        if (!empty($fieldValue)) {
           _civicrm_api3_validate_constraint($params, $fieldName, $fieldInfo);
         }
         elseif (!empty($fieldInfo['required'])) {
@@ -1434,7 +1439,7 @@ function _civicrm_api3_validate_fields($entity, $action, &$params, $fields, $err
       }
       if (!empty($fieldInfo['api.unique'])) {
         $params['entity'] = $entity;
-        _civicrm_api3_validate_uniquekey($params, $fieldName, $fieldInfo);
+        _civicrm_api3_validate_unique_key($params, $fieldName);
       }
     }
   }
@@ -1459,12 +1464,23 @@ function _civicrm_api3_validate_fields($entity, $action, &$params, $fields, $err
  * @throws Exception
  */
 function _civicrm_api3_validate_date(&$params, &$fieldName, &$fieldInfo) {
+  list($fieldValue, $op) = _civicrm_api3_field_value_check($params, $fieldName);
+  if (strpos($op, 'NULL') !== FALSE || strpos($op, 'EMPTY') !== FALSE) {
+    return;
+  }
   //should we check first to prevent it from being copied if they have passed in sql friendly format?
   if (!empty($params[$fieldInfo['name']])) {
-    $params[$fieldInfo['name']] = _civicrm_api3_getValidDate($params[$fieldInfo['name']], $fieldInfo['name'], $fieldInfo['type']);
+    $fieldValue = _civicrm_api3_getValidDate($fieldValue, $fieldInfo['name'], $fieldInfo['type']);
   }
-  if ((CRM_Utils_Array::value('name', $fieldInfo) != $fieldName) && !empty($params[$fieldName])) {
-    $params[$fieldName] = _civicrm_api3_getValidDate($params[$fieldName], $fieldName, $fieldInfo['type']);
+  if ((CRM_Utils_Array::value('name', $fieldInfo) != $fieldName) && !empty($fieldValue)) {
+    $fieldValue = _civicrm_api3_getValidDate($fieldValue, $fieldName, $fieldInfo['type']);
+  }
+
+  if (!empty($op)) {
+    $params[$fieldName][$op] = $fieldValue;
+  }
+  else {
+    $params[$fieldName] = $fieldValue;
   }
 }
 
@@ -1496,21 +1512,21 @@ function _civicrm_api3_getValidDate($dateValue, $fieldName, $fieldType) {
 /**
  * Validate foreign constraint fields being passed into API.
  *
- * @param array $params
- *   Params from civicrm_api.
+ * @param mixed $fieldValue
  * @param string $fieldName
  *   Uniquename of field being checked.
  * @param array $fieldInfo
  *   Array of fields from getfields function.
- * @throws Exception
+ * @throws \API_Exception
  */
-function _civicrm_api3_validate_constraint(&$params, &$fieldName, &$fieldInfo) {
-  $dao = new $fieldInfo['FKClassName'];
-  $dao->id = $params[$fieldName];
+function _civicrm_api3_validate_constraint(&$fieldValue, &$fieldName, &$fieldInfo) {
+  $daoName = $fieldInfo['FKClassName'];
+  $dao = new $daoName();
+  $dao->id = $fieldValue;
   $dao->selectAdd();
   $dao->selectAdd('id');
   if (!$dao->find()) {
-    throw new Exception("$fieldName is not valid : " . $params[$fieldName]);
+    throw new API_Exception("$fieldName is not valid : " . $fieldValue);
   }
 }
 
@@ -1521,14 +1537,16 @@ function _civicrm_api3_validate_constraint(&$params, &$fieldName, &$fieldInfo) {
  *   Params from civicrm_api.
  * @param string $fieldName
  *   Uniquename of field being checked.
- * @param $fieldInfo
- *   Array of fields from getfields function.
  * @throws Exception
  */
-function _civicrm_api3_validate_uniquekey(&$params, &$fieldName, &$fieldInfo) {
+function _civicrm_api3_validate_unique_key(&$params, &$fieldName) {
+  list($fieldValue, $op) = _civicrm_api3_field_value_check($params, $fieldName);
+  if (strpos($op, 'NULL') !== FALSE || strpos($op, 'EMPTY') !== FALSE) {
+    return;
+  }
   $existing = civicrm_api($params['entity'], 'get', array(
       'version' => $params['version'],
-      $fieldName => $params[$fieldName],
+      $fieldName => $fieldValue,
     ));
   // an entry already exists for this unique field
   if ($existing['count'] == 1) {
@@ -1686,6 +1704,9 @@ function _civicrm_api_get_fields($entity, $unique = FALSE, &$params = array()) {
 /**
  * Return an array of fields for a given entity - this is the same as the BAO function but
  * fields are prefixed with 'custom_' to represent api params
+ * @param $entity
+ * @param array $params
+ * @return array
  */
 function _civicrm_api_get_custom_fields($entity, &$params) {
   $entity = _civicrm_api_get_camel_name($entity);
@@ -1715,8 +1736,11 @@ function _civicrm_api_get_custom_fields($entity, &$params) {
   }
   return $ret;
 }
+
 /**
  * Translate the custom field data_type attribute into a std 'type'
+ * @param $dataType
+ * @return int
  */
 function _getStandardTypeFromCustomDataType($dataType) {
   $mapping = array(
@@ -1742,6 +1766,8 @@ function _getStandardTypeFromCustomDataType($dataType) {
  * If multiple aliases the last takes precedence
  *
  * Function also swaps unique fields for non-unique fields & vice versa.
+ * @param $apiRequest
+ * @param $fields
  */
 function _civicrm_api3_swap_out_aliases(&$apiRequest, $fields) {
   foreach ($fields as $field => $values) {
@@ -1769,8 +1795,7 @@ function _civicrm_api3_swap_out_aliases(&$apiRequest, $fields) {
       && $uniqueName
       && $field != $uniqueName
       && array_key_exists($uniqueName, $apiRequest['params'])
-      )
-    {
+    ) {
       $apiRequest['params'][$field] = CRM_Utils_Array::value($values['uniqueName'], $apiRequest['params']);
       // note that it would make sense to unset the original field here but tests need to be in place first
     }
@@ -1792,36 +1817,48 @@ function _civicrm_api3_swap_out_aliases(&$apiRequest, $fields) {
  * @throws API_Exception
  */
 function _civicrm_api3_validate_integer(&$params, &$fieldName, &$fieldInfo, $entity) {
-  if (!empty($params[$fieldName])) {
+  list($fieldValue, $op) = _civicrm_api3_field_value_check($params, $fieldName);
+  if (strpos($op, 'NULL') !== FALSE || strpos($op, 'EMPTY') !== FALSE) {
+    return;
+  }
+
+  if (!empty($fieldValue)) {
     // if value = 'user_contact_id' (or similar), replace value with contact id
-    if (!is_numeric($params[$fieldName]) && is_scalar($params[$fieldName])) {
-      $realContactId = _civicrm_api3_resolve_contactID($params[$fieldName]);
+    if (!is_numeric($fieldValue) && is_scalar($fieldValue)) {
+      $realContactId = _civicrm_api3_resolve_contactID($fieldValue);
       if ('unknown-user' === $realContactId) {
-        throw new API_Exception("\"$fieldName\" \"{$params[$fieldName]}\" cannot be resolved to a contact ID", 2002, array('error_field' => $fieldName, "type" => "integer"));
+        throw new API_Exception("\"$fieldName\" \"{$fieldValue}\" cannot be resolved to a contact ID", 2002, array('error_field' => $fieldName, "type" => "integer"));
       }
       elseif (is_numeric($realContactId)) {
-        $params[$fieldName] = $realContactId;
+        $fieldValue = $realContactId;
       }
     }
     if (!empty($fieldInfo['pseudoconstant']) || !empty($fieldInfo['options'])) {
-      _civicrm_api3_api_match_pseudoconstant($params, $entity, $fieldName, $fieldInfo);
+      _civicrm_api3_api_match_pseudoconstant($fieldValue, $entity, $fieldName, $fieldInfo);
     }
 
     // After swapping options, ensure we have an integer(s)
-    foreach ((array) ($params[$fieldName]) as $value) {
+    foreach ((array) ($fieldValue) as $value) {
       if ($value && !is_numeric($value) && $value !== 'null' && !is_array($value)) {
         throw new API_Exception("$fieldName is not a valid integer", 2001, array('error_field' => $fieldName, "type" => "integer"));
       }
     }
 
     // Check our field length
-    if (is_string($params[$fieldName]) && !empty($fieldInfo['maxlength']) && strlen($params[$fieldName]) > $fieldInfo['maxlength']
+    if (is_string($fieldValue) && !empty($fieldInfo['maxlength']) && strlen($fieldValue) > $fieldInfo['maxlength']
       ) {
-      throw new API_Exception($params[$fieldName] . " is " . strlen($params[$fieldName]) . " characters  - longer than $fieldName length" . $fieldInfo['maxlength'] . ' characters',
+      throw new API_Exception($fieldValue . " is " . strlen($fieldValue) . " characters  - longer than $fieldName length" . $fieldInfo['maxlength'] . ' characters',
         2100, array('field' => $fieldName, "max_length" => $fieldInfo['maxlength'])
       );
     }
   }
+
+  if (!empty($op)) {
+    $params[$fieldName][$op] = $fieldValue;
+  }
+  else {
+    $params[$fieldName] = $fieldValue;
+  }
 }
 
 /**
@@ -1831,7 +1868,7 @@ function _civicrm_api3_validate_integer(&$params, &$fieldName, &$fieldInfo, $ent
  *   E.g. "user_contact_id" or "@user:username".
  * @return int|NULL|'unknown-user'
  */
-function  _civicrm_api3_resolve_contactID($contactIdExpr) {
+function _civicrm_api3_resolve_contactID($contactIdExpr) {
   //if value = 'user_contact_id' replace value with logged in user id
   if ($contactIdExpr == "user_contact_id") {
     return CRM_Core_Session::getLoggedInContactID();
@@ -1863,9 +1900,13 @@ function  _civicrm_api3_resolve_contactID($contactIdExpr) {
  * @throws API_Exception
  */
 function _civicrm_api3_validate_html(&$params, &$fieldName, $fieldInfo) {
-  if ($value = CRM_Utils_Array::value($fieldName, $params)) {
-    if (!CRM_Utils_Rule::xssString($value)) {
-      throw new API_Exception(ts('Illegal characters in input (potential scripting attack)'), array("field" => $fieldName, "error_code" => "xss"));
+  list($fieldValue, $op) = _civicrm_api3_field_value_check($params, $fieldName);
+  if (strpos($op, 'NULL') || strpos($op, 'EMPTY')) {
+    return;
+  }
+  if ($fieldValue) {
+    if (!CRM_Utils_Rule::xssString($fieldValue)) {
+      throw new API_Exception('Illegal characters in input (potential scripting attack)', array("field" => $fieldName, "error_code" => "xss"));
     }
   }
 }
@@ -1883,47 +1924,62 @@ function _civicrm_api3_validate_html(&$params, &$fieldName, $fieldInfo) {
  * @throws Exception
  */
 function _civicrm_api3_validate_string(&$params, &$fieldName, &$fieldInfo, $entity) {
-  // If fieldname exists in params
-  $value = CRM_Utils_Array::value($fieldName, $params, '');
-  if (!is_array($value)) {
-    $value = (string) $value;
+  list($fieldValue, $op) = _civicrm_api3_field_value_check($params, $fieldName);
+  if (strpos($op, 'NULL') !== FALSE || strpos($op, 'EMPTY') !== FALSE || CRM_Utils_System::isNull($fieldValue)) {
+    return;
   }
-  else{
+
+  if (!is_array($fieldValue)) {
+    $fieldValue = (string) $fieldValue;
+  }
+  else {
     //@todo what do we do about passed in arrays. For many of these fields
     // the missing piece of functionality is separating them to a separated string
     // & many save incorrectly. But can we change them wholesale?
   }
-  if ($value) {
-    if (!CRM_Utils_Rule::xssString($value)) {
-      throw new Exception(ts('Illegal characters in input (potential scripting attack)'));
-    }
-    if ($fieldName == 'currency') {
-      if (!CRM_Utils_Rule::currencyCode($value)) {
-        throw new Exception("Currency not a valid code: $value");
+  if ($fieldValue) {
+    foreach ((array) $fieldValue as $value) {
+      if (!CRM_Utils_Rule::xssString($fieldValue)) {
+        throw new Exception('Illegal characters in input (potential scripting attack)');
+      }
+      if ($fieldName == 'currency') {
+        //When using IN operator $fieldValue is a array of currency codes
+        if (!CRM_Utils_Rule::currencyCode($value)) {
+          throw new Exception("Currency not a valid code: $currency");
+        }
       }
     }
-    if (!empty($fieldInfo['pseudoconstant']) || !empty($fieldInfo['options'])) {
-      _civicrm_api3_api_match_pseudoconstant($params, $entity, $fieldName, $fieldInfo);
-    }
-    // Check our field length
-    elseif (is_string($value) && !empty($fieldInfo['maxlength']) && strlen(utf8_decode($value)) > $fieldInfo['maxlength']) {
-      throw new API_Exception("Value for $fieldName is " . strlen(utf8_decode($value)) . " characters  - This field has a maxlength of {$fieldInfo['maxlength']} characters.",
-        2100, array('field' => $fieldName)
-      );
-    }
+  }
+  if (!empty($fieldInfo['pseudoconstant']) || !empty($fieldInfo['options'])) {
+    _civicrm_api3_api_match_pseudoconstant($fieldValue, $entity, $fieldName, $fieldInfo);
+  }
+  // Check our field length
+  elseif (is_string($fieldValue) && !empty($fieldInfo['maxlength']) && strlen(utf8_decode($fieldValue)) > $fieldInfo['maxlength']) {
+    throw new API_Exception("Value for $fieldName is " . strlen(utf8_decode($value)) . " characters  - This field has a maxlength of {$fieldInfo['maxlength']} characters.",
+      2100, array('field' => $fieldName)
+    );
+  }
+
+  if (!empty($op)) {
+    $params[$fieldName][$op] = $fieldValue;
+  }
+  else {
+    $params[$fieldName] = $fieldValue;
   }
 }
 
 /**
  * Validate & swap out any pseudoconstants / options
  *
- * @param array $params: api parameters
- * @param string $entity: api entity name
- * @param string $fieldName: field name used in api call (not necessarily the canonical name)
- * @param array $fieldInfo: getfields meta-data
+ * @param mixed $fieldValue
+ * @param string $entity : api entity name
+ * @param string $fieldName : field name used in api call (not necessarily the canonical name)
+ * @param array $fieldInfo : getfields meta-data
+ * @throws \API_Exception
  */
-function _civicrm_api3_api_match_pseudoconstant(&$params, $entity, $fieldName, $fieldInfo) {
+function _civicrm_api3_api_match_pseudoconstant(&$fieldValue, $entity, $fieldName, $fieldInfo) {
   $options = CRM_Utils_Array::value('options', $fieldInfo);
+
   if (!$options) {
     if (strtolower($entity) == 'profile' && !empty($fieldInfo['entity'])) {
       // we need to get the options from the entity the field relates to
@@ -1935,13 +1991,13 @@ function _civicrm_api3_api_match_pseudoconstant(&$params, $entity, $fieldName, $
 
   // If passed a value-separated string, explode to an array, then re-implode after matching values
   $implode = FALSE;
-  if (is_string($params[$fieldName]) && strpos($params[$fieldName], CRM_Core_DAO::VALUE_SEPARATOR) !== FALSE) {
-    $params[$fieldName] = CRM_Utils_Array::explodePadded($params[$fieldName]);
+  if (is_string($fieldValue) && strpos($fieldValue, CRM_Core_DAO::VALUE_SEPARATOR) !== FALSE) {
+    $fieldValue = CRM_Utils_Array::explodePadded($fieldValue);
     $implode = TRUE;
   }
   // If passed multiple options, validate each
-  if (is_array($params[$fieldName])) {
-    foreach ($params[$fieldName] as &$value) {
+  if (is_array($fieldValue)) {
+    foreach ($fieldValue as &$value) {
       if (!is_array($value)) {
         _civicrm_api3_api_match_pseudoconstant_value($value, $options, $fieldName);
       }
@@ -1950,25 +2006,26 @@ function _civicrm_api3_api_match_pseudoconstant(&$params, $entity, $fieldName, $
     // need to verify that this is safe and doesn't break anything though.
     // Better yet would be to leave it as an array and ensure that every dao/bao can handle array input
     if ($implode) {
-      CRM_Utils_Array::implodePadded($params[$fieldName]);
+      CRM_Utils_Array::implodePadded($fieldValue);
     }
   }
   else {
-    _civicrm_api3_api_match_pseudoconstant_value($params[$fieldName], $options, $fieldName);
+    _civicrm_api3_api_match_pseudoconstant_value($fieldValue, $options, $fieldName);
   }
 }
 
 /**
  * Validate & swap a single option value for a field
  *
- * @param string $value: field value
- * @param array $options: array of options for this field
- * @param string $fieldName: field name used in api call (not necessarily the canonical name)
+ * @param string $value field value
+ * @param array $options array of options for this field
+ * @param string $fieldName field name used in api call (not necessarily the canonical name)
  * @throws API_Exception
  */
 function _civicrm_api3_api_match_pseudoconstant_value(&$value, $options, $fieldName) {
   // If option is a key, no need to translate
-  if (array_key_exists($value, $options)) {
+  // or if no options are avaiable for pseudoconstant 'table' property
+  if (array_key_exists($value, $options) || !$options) {
     return;
   }
 
@@ -1992,11 +2049,12 @@ function _civicrm_api3_api_match_pseudoconstant_value(&$value, $options, $fieldN
  * Returns the canonical name of a field
  *
  * @param $entity
- *   api entity name (string should already be standardized - no camelCase).
+ *   api entity name (string should already be standardized - no camelCase).
  * @param $fieldName
- *   any variation of a field's name (name, unique_name, api.alias).
+ *   any variation of a field's name (name, unique_name, api.alias).
  *
- * @return bool|string (string|bool) fieldName or FALSE if the field does not exist
+ * @return bool|string
+ *   fieldName or FALSE if the field does not exist
  */
 function _civicrm_api3_api_resolve_alias($entity, $fieldName) {
   if (strpos($fieldName, 'custom_') === 0 && is_numeric($fieldName[7])) {
@@ -2045,3 +2103,22 @@ function _civicrm_api3_deprecation_check($entity, $result = array()) {
     }
   }
 }
+
+/**
+ * In some case $params[$fieldName] holds Array value in this format Array([operator] => [value])
+ * So this function returns the actual field value
+ *
+ * @param array $params
+ * @param string $fieldName
+ * @return mixed
+ */
+function _civicrm_api3_field_value_check(&$params, $fieldName) {
+  $fieldValue = CRM_Utils_Array::value($fieldName, $params);
+  $op = NULL;
+
+  if (!empty($fieldValue) && is_array($fieldValue) && array_search(key($fieldValue), CRM_Core_DAO::acceptedSQLOperators())) {
+    $op = key($fieldValue);
+    $fieldValue = CRM_Utils_Array::value($op, $fieldValue);
+  }
+  return array($fieldValue, $op);
+}