Validation and Messages

Input validation, error messages and exception handling the Cotonti way

This page is not translated yet. Showing original version of page. You can sign up and help with translating.

#1. Input and validation

As a PHP coder you might be used to $_GET, $_POST, $_COOKIE and such, but raw user input is not always good for your security and data integrity. That's why in Cotonti we import all data with cot_import() function:

function cot_import($name, $source, $filter, $maxlen = 0, $dieonerror = false, $buffer = false)

Parameters explained:

  • $name - the name of the input in the HTML form.
  • $source - source type of the data. Possible values are:
    • 'G' or 'GET' - for GET parameters;
    • 'P' or 'POST' - for POST data;
    • 'C' or 'COOKIE' - to import data from cookies;
    • 'R' or 'REQUEST' - to import from PHP's $_REQUEST;
    • 'D' or 'DIRECT' - used to filter data passed in the $name argument;
    • 'PUT' - to import data from HTTP PUT request (for REST services);
    • 'DELETE' - to import data from HTTP DELETE request (for REST);
    • 'PATCH' - to import data from HTTP PATCH request (for REST).
  • $filter - filter name against which the data is validated. Supported filters:
    • 'ALP' - passes digits, latin alphabet characters, dashes and underscores, useful for identifiers;
    • 'ARR' - imports an unfiltered array, each array item needs further validation after this import;
    • 'BOL' - import for booleans and checkboxes, returns TRUE if the input is checked or positive, FALSE if it is unchecked or zero and NULL if the input was not present in the form which sent the data;
    • 'HTM' - passes any text and trims whitespace from the beginning and ending of it, HTML is passed by this filter;
    • 'INT' - passes integer values only, a PHP (int) is returned or NULL if validation fails;
    • 'NOC' - no check/encoding, passes everything as is;
    • 'NUM' - passes numeric values only, PHP (float) is returned or NULL if validation fails;
    • 'PSW' - special filter for passwords, removes quotes,,> from input and limits its length to 32 characters;
    • 'TXT' - filter accepting plain text except for HTML, in HTML some characters are replaced with HTML entities on import;
  • $maxlen - limits max. lenght of the input, 0 means unlimited;
  • $dieonerror - stop script execution with a fatal error if validation fails;
  • $buffer - enables import buffer feature, used by Forms API.

The returned value is either a value with a filter applied or NULL if validation failed and a user must enter another value instead. Here are some example calls:

$title  = cot_import('title',  'P', 'TXT');
$code   = cot_import('code',   'P', 'ALP');
$count  = cot_import('count',  'P', 'INT');
$text   = cot_import('text',   'P', 'HTM');
$notify = cot_import('notify', 'P', 'BOL');

After you have imported the values, you might want to check them against some value (first of all NULL which is returned if the input was not present or validation failed against the filter) and emit an error message.

In a verbose form you could do it like this:

if (is_null($count))
{
	cot_error('Please enter a correct integer count', 'count');
}

cot_error() is a standard way to inform the system that something went wrong and to emit an error message that can be displayed later on. It has two parameters:

  • $message - message text or language string code for the message to be displayed. E.g. if there is a language string $L['my_err_msg'] in connected language files, you can just pass 'my_err_msg' and localized message will be displayed to a user.
  • $src - name of the input where wrong data was entered (optional), this is used to display the error message next to the input depending on template configuration.

There is actually a shorter (and recommended) way to combine "if" and "cot_error()" call in case of error:

cot_check(is_null($count), 'Please enter a correct integer count', 'count');

cot_check() function has parameters:

  • $condition - a boolean expression which checks for an error, if it evaluates to TRUE, then the error will be triggered;
  • $message - error message displayed if $condition is TRUE, same as in cot_error();
  • $src - input name (optional) for the source of invalid data, same as in cot_error().

You should check whether any errors have been found in the system before proceeding to action such as saving data in database using cot_error_found() function:

if (!cot_error_found())
{
	// OK, we can save our data here because validation passed
}

This is it, now you know the basics of input validation in Cotonti.

#2. Emitting messages and using other functions

Not all messages are error messages. You might as well inform the user that some operation ended successfully or warn him about something non-critical. Cotonti's messages API covers those cases too.

Here is the list of other Errors and Messages API functions:

Most of these functions are rather advanced and you will never have to use it in your modules or plugins. Let's have a look at the most common ones.

cot_message() function is similar to cot_error() but it is more generic. Here is the list of its parameters:

  • $text - message text or language string code;
  • $class - type or CSS class of the message, common values are 'error', 'success', warning', 'ok';
  • $src - source input name where the message was caused, used to display the message next to it in some configurations.

Use this function to inform the user about operation status when handling forms and returning back to the normal page:

cot_check($some_input != '', 'some_err_msg', 'some_input');
if (!cot_error_found())
{
	// OK
	// Save the data...
	// ...
	// Emit the message
	cot_message('New item added successfully');
	// Return to the interface
	cot_redirect(cot_url('mymod', 'm=edit'));
}

Use cot_die() if something went wrong and you want to prevent further script execution:

if ($something_really_wrong)
{
	// How did the user get here?
	cot_die();
}

If that "something went wrong" is a standard situation that can be represented with a common HTTP error code and message, then use cot_die_message() function providing an error code:

if ($page_not_found)
{
	// Print a standard 404 page with a correct HTTP code
	cot_die_message(404);
}

You can also use cot_die_message() to handle non-standard errors and pass error page title and contents as 3rd and 4th arguments.

#3. Displaying messages

Now you can validate data and emit messages but how do you display them? Normally you need to add just two lines to add errors and messages display to your module or plugin.

The first one is in your template, add a line that includes standard template for messages output:

{FILE "{PHP.cfg.themes_dir}/{PHP.cfg.defaulttheme}/warnings.tpl"}

And the second one should be put in your PHP code somewhere before the MAIN is parsed, e.g.:

cot_display_messages($t);

The above example assumes that:

  • $t is the current XTemplate object;
  • You have put the line connecting the standard warnings.tpl template in the outer block called MAIN.

This assumption may be incorrect, so here is the explanation of  cot_display_messages($tpl, $block = 'MAIN') parameters:

  • $tpl - XTemplate object where messages should be displayed;
  • $block - full block name where messages template is included. For example, if you have put it in the RESULTS block contained within MAIN block, then the value of this argument should be 'MAIN.RESULTS'.

#4. All together now

Now that you know the pieces of the puzzle, here is a complete picture of a real application for you:

// Connect language file with messages
require_once cot_langfile('mailout', 'plug');

if ($a == 'send' && $_SERVER['REQUEST_METHOD'] == 'POST')
{
	// Handle form submission
	$subject = cot_import('subject', 'P', 'TXT');
	$limit   = cot_import('limit',   'P', 'INT');
	$content = cot_import('content', 'P', 'HTM');

	// Input validation
	cot_check(empty($subject), 'mailout_err_subject_empty', 'subject');
	cot_check(empty($content), 'mailout_err_content_empty', 'content');
	cot_check($limit <= 0,     'mailout_err_invalid_limit', 'limit');

	if (!cot_error_found())
	{
		// Passed, save changes
		$sql_limit = $limit > 0 ? "LIMIT $limit" : '';
		$res = $db->query("SELECT user_name, user_email FROM $db_users $sql_limit");
		$counter = 0;
		foreach ($res->fetchAll() as $row)
		{
			$to = '"' . addslashes($row['user_name']) . '" <' . $row['user_email'] . '>';
			cot_mail($to, $subject, $content, '', false, null, true);
			$counter++;
		}
		// Emit success message
		cot_message("$counter messages sent.");
	}

	// Redirect back to the original form and show messages there
	cot_redirect(cot_url('admin', 'm=other&p=mailout', '', true));
}

// Display the user interface with the form
$t = new XTemplate(cot_tplfile('mailout.tools', 'plug'));

// Work with the template, assign tags and render blocks

// Don't forget to display the messages if they are present
cot_display_messages($t);

 

#5. Custom filters for data input

In the begining of this page we learn how to use built-in filters for user data input. Besides it developer can add custom filters for own need.

Shows it as example. Let's say we have a form with users mobile number input and we mut check it to match some basic rules.

We can do it in 3 simple steps:

  1. Creating filtration function, assume it would be a cell-phone number (we need mobile operator code and a phone number itself):

  1. function mobilenum_filter($input_value, $name)
    {
       // clear all except digits
       $filtered = preg_replace("/[^\d]/", '', $input_value);
       // check for number length
       if (preg_match('/\d{10,12}/', $filtered)){
           // if length is in range from 10 to 12 — assume it as a valid number
           // then returns last 10 symbols (3 for operator code and 10 for number)
           return substr($filtered, -10);
       } else {
           // if it too short or too long mark it as had not passed filtration — returning NULL
           return NULL;
       }
    }

    As a $input_value argument function gets «raw» user inputed data. With $name we get name of input field.

  2. After filtration function had been created we need to set it in «filter registry»:

    $cot_import_filters['MOB'][] = 'mobilenum_filter'; 
    • Array $cot_import_filters is a filter registry. It's a global variable, so useglobalkeyword to use it inside any function.
    • 'MOB' — name of the filter to be used in cot_import() function. It can be user custom if we define a new one, or as name of one of built-in if we plan to redifine system filter.
    • 'mobilenum_filter' — name of filtration function defined in first step.
  3. Now we can use it to filter some user data:

    $mob_number = cot_import('mobnum', 'P', 'MOB'); 

    where 'mobnum' — is name of form input field and 'MOB' is a name of our custom filter.

Also we can define several filtration function for one filter (one filter name). It would be called one be one. And input value consider valid only if all filtration  steps is passed.


1. Dr2005alex  2016-03-20 10:22

Респект!!! я оказывается не все видел)))) 

2. Roffun  2016-03-20 11:50

Очень полезная информация, чем больше таких статей в документации тем активнее будет приток новых пользователей.

p.s. В последнем примере описано:

"Для добавления параметра безопасности в ссылку используется функция" cot_xp() 

а в примере кода cot_xg()

'USERS_EDIT_SEND' => cot_url('users','m=edit&a=update&'.cot_xg().'&id='.$urr['user_id'])
3. Dr2005alex  2016-03-20 21:08

Да все-же опечатка, cot_xg() для добавления в url, а cot_xp() для добавления скрытого input со значением x в форму.

4. Macik  2016-03-22 23:43

Исправил. спасибо, что читаете доки!

5. CrazyFreeMan  2016-07-21 19:52

Хорошо бы расписать 

  • $src - (опционально) имя поля ввода в котором мы получили некорректные данные. Нужно указывать для отображения сообщений о ошибках рядом с полем ввода (зависит от настроек шаблонов и системной опции "Показывать сообщения отдельно для каждого источника").

Что именно должно быть в шаблоне, что это относится к ресурсам и что нужно добавить параметр $error а то важная мелочь :) 

6. Macik  2016-09-10 20:42

 CrazyFreeManсейчас в ядре этого нет. Т.е. сделать такой вывод можно только написав свой обработчик. `$src` используется системой для разделения ошибок внутри соответствующего массива. 

Alleen geregistreerde gebruikers kunnen reacties plaatsen.