Porto

Filtering Data with PHP

Filtering Data with PHP

A basis for a security model

By Kevin Waterson

Contents

  1. What is a PHP Filter
  2. Getting Started
  3. Filtering Variables
  4. Validating INTEGERS
  5. Validate BOOLEAN
  6. Validate FLOAT
  7. Validate REGEX
  8. Validate a URL
  9. Validate an IP Address
  10. Validate an Email Address
  11. Sanitizing Variables
  12. Sanitize a String
  13. URL Encode
  14. Sanitize Special Chars
  15. Filter Unsafe RAW
  16. Sanitize an Email Address
  17. Sanitize a URL
  18. Sanitize an Integer
  19. Sanitize a Float
  20. Magic Quotes
  21. Callback Filter
  22. The INPUT Filter
  23. Filter an array
  24. Filter an array with callback
  25. A Real World Example
  26. Credits

What is a PHP Filter

One of the greatest strengths of PHP is its ease of use. Unfortunately this same benifit has worked against PHP as many new coders have forgotten any security measures or lack the expertise to create a class to validate their variables from end users. PHP provides an extension to help with this process. There are many validation classes out there, some better than others, with an equal number of methods for doing the same task. The PHP filter extension has many of the functions needed for checking many types of user input. Handled locally this provides a standard method of filtering data. This makes for easier to read code as we will all be using the same functions rather than having to create our own. This will bring PHP security to fore with programmers able to easily implement simple, yet robust, filtering of data. Never again do we need to see code like this below.


<?php
  
include($_GET['filename']);
?>

Or, even more common is database queries like this one...


<?php
  mysql_query
("INSERT INTO table (field_one, field_two) VALUES ({$_POST['var1']}{$_POST['var2']})";
?>

Getting Started

To get a look at what the filter extension has to offer, we can easily list all the available filters with the input_filters_list() function. Lets put it to work.


<table>
<tr><td>Filter Name</td><td>Filter ID</td></tr>
<?php
foreach(filter_list() as $id =>$filter)
        {
        echo 
'<tr><td>'.$filter.'</td><td>'.filter_id($filter).'</td></tr>'."\n";
        }
?>
</table>

The above code will produce a table like the one below.

Filter NameFilter ID
int257
boolean258
float259
validate_regexp272
validate_url273
validate_email274
validate_ip275
string513
stripped513
encoded514
special_chars515
unsafe_raw516
email517
url518
number_int519
number_float520
magic_quotes521
callback1024

This is quite an impressive list and more will be added in time. Note also that each filter has its own Filter ID, this will become useful as we progress through this tutorial. Each of these filters can be used with the filter_var() function and here we will step through each one show how it works. Note that the string and stripped have the same ID. This is because they are the same.

Filtering Variables

The actual filtering of variables is done with the filter_var() function. Lets start with a simple integer filter to see how it works.

<?php

/*** an integer to check ***/
$int 1234;
/*** validate the integer ***/
echo filter_var($intFILTER_VALIDATE_INT);

?>

The above code will echo 1234. The filter_var() function will return INT(1234) because the input has validated as an integer. Lets change the $int variable to a non int value and see the results.


<?php

/*** an integer to check ***/
$int 'abc1234';
/*** validate the integer ***/
echo filter_var($intFILTER_VALIDATE_INT);

?>

Now we see a different result. No display is made because the variable $int has failed validation and the filter_var() function has returned bool(false). Also note that if the variable is set to $int='' then it will again return bool(false). Consider this code..


<?php

/*** an integer to check ***/
$int '';
/*** validate the integer ***/
echo filter_var($intFILTER_VALIDATE_INT);

?>

As you can see if you run the above snippet of code, the result is once again a blank page. From here we can continue with INTEGER validation as the filter_var() function has some amazing properties to allow us to do many of the validation tasks we once had to write ourselves.

Validating INTEGERS

As we saw in the previous section, validating a variable as type INT is simple with the filter_var() function. But wait, theres more. FILTER_VALIDATE_INT also allows us to specify a range for our integer variable. This is most excellent when we need to check if a variable is both of type INT and between 1 and 100. Lets see some code.

<?php
/*** an integer to check ***/
$int 42;
/*** lower limit of the int ***/
$min 50;
/*** upper limit of the int ***/
$max 100;

/*** validate the integer ***/
echo filter_var($intFILTER_VALIDATE_INT, array("min_range"=>$min"max_range"=>$max));

?>

WTF!... Above we have tried to validate an interger that must be in the range of 50 and 100. The number 42 clearly is not within this range, yet it passes the validation. What has happened here is we have incorrectly specified the options for min_range and max_range. Although this looks correct, and no error is generated the filter simply falls back to being FILTER_VALIDATE_INT and the number 42 passes. Below we show how to correctly specify the options array.

<?php
/*** an integer to check ***/
$int 42;
/*** lower limit of the int ***/
$min 50;
/*** upper limit of the int ***/
$max 100;

/*** validate the integer ***/
echo filter_var($intFILTER_VALIDATE_INT, array("options" => array("min_range"=>$min"max_range"=>$max)));


?>

Now we see a different behaviour when the options array is correctly specified as above. We havetried to validate an INT(42), checking that it is both of the type INT and it is within the range of 50 and 100. The above code will return boolean FALSE as the interger 42 is not withing the range specified. Note also that this will work for negative values. Consider this next block of code.

<?php
/*** an integer to check ***/
$int = -2;

/*** lower limit of the int ***/
$min = -10;

/*** upper limit of the int ***/
$max 100;

/*** validate the integer ***/
echo filter_var($intFILTER_VALIDATE_INT, array("options" => array("min_range"=>$min"max_range"=>$max)));
?>

The above code will echo -2 as it is both of type INT and within the range of -10 to 100. We could also validate our integer by specifying only a min_range or a max_range. This can prove helpful if we need a number to be no less than 10 for example. Lets see it in action.


<?php
/*** an integer to check ***/
$int 12;

/*** lower limit of the int ***/
$min 10;

/*** validate the integer ***/
echo filter_var($intFILTER_VALIDATE_INT,array('options'=>array('min_range'=>$min)));
?>

The above code now correctly validates an integer that must not be less than, or equal to 10. Now we can move on a little. We have seen how we can validate a single INT, but we can go further and validate an array of values. To validate an array of values, in this case integers, we use the filter_var_array() function. This function alone makes the whole filter extension worth the effort. The filter_var_array() function allows us to filter or validate many different data types. But for now, lets simply stick with INTs. Lets code it up and see.

<?php

/*** an array of values to filter ***/
$arr = array(10,"109","""-1234""some text""asdf234asdfgs", array());

/*** create an array of filtered values ***/
$filtered_array filter_var_array($arrFILTER_VALIDATE_INT);

/*** print out the results ***/
foreach($filtered_array as $key=>$value)
        {
        echo 
$key.' -- '.$value.'<br />';
        }
?>

The above code will print out the array. It should look like this
0 -- 10
1 -- 109
2 --
3 -- -1234
4 --
5 --
6 -- Array
As you can see, it has printed out the values that are of type INT. We note that 4 and 5 in the list are blank as the filter_var() function has return bool(false) here. Finally we see the last type is merely an array. This type of flexibility is makes the filter extension a great improvement over having to code up all this validation yourself. There is no longer any excuse for sloppy security practices.

Moving on from validating an array of values, the FILTER_VALIDATE_INT filter allows us to also validate INTEGERS of the type OCTAL and HEX. Clever stuff, and will save a bundle of time as we no longer need to code up endless hex and octal arrays. Two flags are currently available for this type of checking. They are:

  • FILTER_FLAG_ALLOW_HEX
  • FILTER_FLAG_ALLOW_OCTAL

Once again the filter uses an array to hold the flags, be sure to correctly specify them as shown here with the flags array. Lets see how we go with a hex value.

<?php

/*** a hex value to check ***/
$hex "0xff";

/*** filter with HEX flag ***/
echo filter_var($hexFILTER_VALIDATE_INT, array("flags"=>FILTER_FLAG_ALLOW_HEX));

?>

The above code will correctly echoes 255. The filter has successfully validated the HEX value and returned that value. If validation fails with a non-hex value it will return bool(false). Of course, hex values are not case sensitive so 0Xff or 0xFF would validate also. As we mentioned earlier, we can also use an OCTAL value in the same way.

<?php

/*** an OCTAL value to check ***/
$octal 0666;

/*** validate with the OCTAL flag ***/
if(filter_var($octalFILTER_VALIDATE_INT, array("flags"=>FILTER_FLAG_ALLOW_OCTAL)) === FALSE)
        {
        echo 
"Value is not OCTAL!";
        }
else
        {
        echo 
"$octal is a valid OCTAL number";
        }

?>

Validate Boolean

Along with the amazing INTEGER validation PHP also provides a similar method of validating BOOLEAN values. Here we see how easy it is to check for a BOOLEAN value.

<?php

/*** test for a boolean value ***/
echo filter_var("true"FILTER_VALIDATE_BOOLEAN);

?>

The above code will echo 1. This is because the BOOLEAN filter has found a valid boolean value. Other acceptable boolean values that return TRUE are listed here.

  • 1
  • "1"
  • "yes"
  • "true"
  • "on"
  • TRUE

These values are not case sensitive so that true and TrUe are the same. Values that return false are listed below here.

  • 0
  • "0"
  • "no"
  • "false"
  • "off"
  • ""
  • NULL
  • FALSE

The BOOLEAN filter will also allow us to evaluate code within the filter itself to test for values. Consider the below code using the ternary operator.

<?php

/*** a simple array ***/
$array = array(1,2,3,4,5);

/*** test for a boolean value ***/
echo filter_var(in_array(3$array), FILTER_VALIDATE_BOOLEAN) ? "TRUE" "FALSE";

?>

The above code will echo TRUE as it has tested the result of the in_array() function and has found the return value to be bool(true). Not to be forgotten here is that we can use an array of values to test for boolean values also. Lets see how.

<?php

/*** a multi dimensional array ***/
$array = array(0,1,2,3,4, array(0,1,2,3,4));

/*** create the list of values ***/
$values filter_var($arrayFILTER_VALIDATE_BOOLEANFILTER_REQUIRE_ARRAY);

/*** dump the values ***/
var_dump($values);

?>

The above code will produce something like this..

  • array(6) {
  • [0]=> bool(false)
  • [1]=> bool(true)
  • [2]=> bool(false)
  • [3]=> bool(false)
  • [4]=> bool(false)
  • [5]=> array(5) {
    • [0]=> bool(false)
    • [1]=> bool(true)
    • [2]=> bool(false)
    • [3]=> bool(false)
    • [4]=> bool(false)
    • }
  • }

As you can see above, the filter has recursively iterated over the array and returned the a boolean value depending on the array members value.

Validate FLOAT

There are of course times we need to validate a floating point number and once again this is simply done as the rest.


<?php

/*** an FLOAT value to check ***/
$float 22.42;

/*** validate with the FLOAT flag ***/
if(filter_var($floatFILTER_VALIDATE_FLOAT) === false)
    {
    echo 
"$float is not valid!";
    }
else
    {
    echo 
"$float is a valid floating point number";
    }
?>

Like other validation options we can still validate an array of floats. Similar to how we validated an array of boolean values, we apply the same principles, and flags, to the float filter. We can also use negative values.


<?php

/*** an array of values ***/
$array = array(1.2,"1.7","""-12345.678""some text""abcd4.2efgh", array());

/*** validate the array ***/
$validation_array filter_var($arrayFILTER_VALIDATE_FLOATFILTER_REQUIRE_ARRAY);

/*** dump the array of validated data ***/
var_dump($validation_array);

?>

The above code will return a list something like this

  • array(7) {
  • [0]=> float(1.2)
  • [1]=> float(1.7)
  • [2]=> bool(false)
  • [3]=> float(-23234.123)
  • [4]=> bool(false)
  • [5]=> bool(false)
  • [6]=> array(0) { }
  • }

The filter will also allow us to filter an array with a user specified seperator. So that a float like 1,234 can be accepted.


<?php

/*** an array of floats with seperators ***/
$floats = array(
"1,234" => ",",
"1.234" => "..",
"1.2e3" => ","
);

/*** validate the floats against the user defined decimal seperators ***/
foreach ($floats as $float => $dec_sep)
    {
        
$out filter_var($floatFILTER_VALIDATE_FLOAT, array("options"=>array("decimal" => $dec_sep)));
    
/*** dump the results ***/
        
var_dump($out);
    }
?>

From the code above we get the output as follows..

  • float(1.234)
  • Warning: filter_var() [function.filter-var]: decimal separator must be one char in /www/filter.php on line 13
  • bool(false)
  • bool(false)

Lets step through this for a minute. The first value has used a comma as a decimal seperator. Normally this would be invalid and be regarded as a thousands seperator. However we have specified the comma as the decimal seperator and so it remains valid and the filter replaces the decimal seperator with a dot and returns 1.234. Next we have a Warning due to the use of a double dot as a decimal seperator. This is nvalid as a decimal seperator must be one character only. Following this, the filter returns bool(false) for that value. The final value also returns bool(false) as the decimal seperators do not match.

Validate REGEX

If you are unfamiliar with regex then it is highly recommended you begin with the phPRO Introduction to PHP Regular Expressions. This should put you in good condition for the following. For those adept at regex, then read on. Validating by regex is something we do commonly for information that should fit a pattern. The filter extension allows us to validate variables in the same manner as we have seen previously. First we will attempt a simple pattern match to see if a string begins with the letter and then we will try to match an email address.


<?php

/*** the string we wish to match ***/
$string "Try to match me";

/*** try to validate with the regex pattern ***/
if(filter_var($stringFILTER_VALIDATE_REGEXP, array("options"=>array("regexp"=>"/^T(.*)/"))) === false)
    {
    
/*** if there is no match ***/
    
echo "Sorry, no match";
    }
else
    {
    
/*** if we match the pattern ***/
    
echo "The string begins with T";
    }
?>

From the above code we see the string does match the pattern and a bool(false) is not returned. If the pattern does match the string value is returned just as with all the previous validation types. Lets see a little more complex regex to try to match an email address.

<?php

/*** an email address ***/
$email "hatemail@phpro.org";

/*** the pattern we wish to match against ***/
$pattern "/^\S+@[\w\d.-]{2,}\.[\w]{2,6}$/iU";

/*** try to validate with the regex pattern ***/
if(filter_var($emailFILTER_VALIDATE_REGEXP, array("options"=>array("regexp"=>$pattern))) === false)
        {
        
/*** if there is no match ***/
        
echo "Sorry, no match";
        }
else
        {
        
/*** if we match the pattern ***/
        
echo "The email address is valid";
        }
?>

So, the above code will match the email address and print the line
The email address is valid
Normally you would not use this method of email checking for email addresses as the filter extension provides checking for this purpose, but more on that later. Should you accidently omit the regex in your checking an E_WARNING is generated and the filter will return bool FALSE.

Validate a URL

URLs can be tricky to deal with. there is no maximum length defined in the rfc and you would be totally amazed at just how many different variations and formats a URL can be in. Recommended reading is RFC 1738 which explains all you need to know about URL validation. You could then write up your own class with which to validate all your ipv4 and ipv6 URLs etc. Or, you can simply use FILTER_VALIDATE_URL in filter_var(). Lets see how it works in its simplest form.

<?php

/*** a rfc compliant web address ***/
$url "http://www.phpro.org";

/*** try to validate the URL ***/
if(filter_var($urlFILTER_VALIDATE_URL) === FALSE)
        {
        
/*** if there is no match ***/
        
echo "Sorry, $url is not valid!";
        }
else
        {
        
/*** if we match the pattern ***/
        
echo "The URL, $url is valid!<br />";
        }
?>

Above we see, like in the examples previous, a simple check of the variable with an if statement tells us the url value is valid. But not all URLs are in this form. According to the rfc, a URL can take many forms. The URL may be in the form of an IP address, or have a QUERY string attached to it. This is why URLs have been so difficult to validate in the past. Several flags have been provided with the URL validation filter to check for some of these occurances and to validate against them. They are listed here.

  • FILTER_FLAG_SCHEME_REQUIRED
  • FILTER_FLAG_HOST_REQUIRED
  • FILTER_FLAG_PATH_REQUIRED
  • FILTER_FLAG_QUERY_REQUIRED

We will begin at the top here and check for flag scheme.


<?php

/*** a non rfc compliant URL ***/
$url "index.php";

/*** try to validate the URL ***/
if(filter_var($urlFILTER_VALIDATE_URLFILTER_FLAG_SCHEME_REQUIRED) === FALSE)
        {
        
/*** if there is no match ***/
        
echo "Sorry, $url is not valid!";
        }
else
        {
        
/*** if the URL is valid ***/
        
echo "The URL, $url is valid!";
        }
?>

We see from above that the URL supplied does not validate against the FILTER_FLAG_SCHEME_REQUIRED flag. The URL needs to be properly formatted to fit the required scheme. Lets try again with a different URL.


<?php

/*** a rfc compliant URL ***/
$url "http://foo";

/*** try to validate the URL ***/
if(filter_var($urlFILTER_VALIDATE_URLFILTER_FLAG_SCHEME_REQUIRED) === FALSE)
        {
        
/*** if there is no match ***/
        
echo "Sorry, $url is not valid!";
        }
else
        {
        
/*** if the URL is valid ***/
        
echo "The URL, $url is valid!";
        }
?>

Now we see that the URL validates against the scheme. The return values are bool(false) if the validation fails, or if it passes validation the URL string is returned. Lets continue with the other flags and FLAG_HOST_REQUIRED. If we were to alter the above filter flag from FILTER_FLAG_SCHEME_REQUIRED to FILTER_FLAG_HOST_REQUIRED it would still validate as the URL http://foo has the host name of "foo". Lets try with an invalid host name.

<?php
/*** a path with no host name ***/
$url "/path/to/foo";

/*** try to the validate the URL ***/
if(filter_var($urlFILTER_VALIDATE_URLFILTER_FLAG_HOST_REQUIRED) === FALSE)
        {
        
/*** if there is no match ***/
        
echo "Sorry, $url is not valid!";
        }
else
        {
        
/*** if the URL is valid ***/
        
echo "The URL, $url is valid!";
        }
?>

Now we see that the missing hostname does not validate against the required flag and returns bool(false). What would happen if you tried a URL like http:// one wonders. It is left as an excercise for the reader to try. To enable correct validation the FILTER_FLAG_HOST_REQUIRED flag must be satisified such as the example below.

<?php
/*** a path with no host name ***/
$url "http://phpro.org";

/*** try to the validate the URL ***/
if(filter_var($urlFILTER_VALIDATE_URLFILTER_FLAG_HOST_REQUIRED) === FALSE)
        {
        
/*** if there is no match ***/
        
echo "Sorry, $url is not valid!";
        }
else
        {
        
/*** if the URL is valid ***/
        
echo "The URL, $url is valid!";
        }
?>

Of course now, the code above validates the URL as we have now supplied a value that satisfies the FILTER_FLAG_HOST_REQUIRED flag. Next we see the FILTER_FLAG_PATH_REQUIRED flag. This flag as the name suggests tells the URL validator that a path is required within the url for it to validate. Lets see it in action.


<?php

/*** a URL with a path ***/
$url "http://phpro.org/path/to/foo";

/*** try to the validate the URL ***/
if(filter_var($urlFILTER_VALIDATE_URLFILTER_FLAG_PATH_REQUIRED) === FALSE)
        {
        
/*** if there is no match ***/
        
echo "Sorry, $url is not valid!";
        }
else
        {
        
/*** if the URL is valid ***/
        
echo "The URL, $url is valid!";
        }
?>

We see from the above code that the URL is valid because it contains a path after the host name. Should the path be missing and the URL was simply http://www.phpro.org then it would fail validation and return bool(false). Next we see the FILTER_FLAG_QUERY_REQUIRED flag. You should now have no problems implementing this as it follows the same convention as the previous flags. As the name suggests, this flag requires the URL to have a query string of type file.php?var=foo&var2=bar. The use is exactly the same as before.


<?php

/*** a URL with a query string ***/
$url "http://phpro.org/index.php?var=foo&var2=bar";

/*** try to the validate the URL ***/
if(filter_var($urlFILTER_VALIDATE_URLFILTER_FLAG_QUERY_REQUIRED) === FALSE)
        {
        
/*** if there is no match ***/
        
echo "Sorry, $url is not valid!";
        }
else
        {
        
/*** if the URL is valid ***/
        
echo "The URL, $url is valid!";
        }
?>

Filter IP Address

Following on from validation of URLs, we often find we need to validate an IP Address. Of course, and IP address may be of different formats for ipv4 and ipv6. An IP address may also need to be within a range of private or reserved ranges. The filter extension makes it possible to discern these differences and to validate an IP address to fit most needs. In its simplest form the validation of a url will look like this.


<?php

/*** an IP address ***/
$ip "192.168.0.1";

if(
filter_var($ipFILTER_VALIDATE_IP) === FALSE)
        {
        echo 
"$ip is not a valid IP";
        }
else
        {
        echo 
"$ip is valid";
        }
?>

As we have supplied the above with a valid IP address it validates and all is well. But now we may wish to validate an IPV6 address or an address with a private range. The IP filter has several flag with which to validate an IP address with. Listed here.

  • FILTER_FLAG_IPV4
  • FILTER_FLAG_IPV6
  • FILTER_FLAG_NO_PRIV_RANGE
  • FILTER_FLAG_NO_RES_RANGE

Starting at the top we will check to see if an IP is a valid IPV4 address.


<?php

/*** an IP address ***/
$ip "192.168.0";

/*** try to validate as IPV4 address ***/
if(filter_var($ipFILTER_VALIDATE_IPFILTER_FLAG_IPV4) === FALSE)
        {
        echo 
"$ip is not a valid IP";
        }
else
        {
        echo 
"$ip is valid";
        }
?>

In the above example the IP address has failed to validate as it is not a complete IPV4 address. It would need to be of the form of the example that preceded it, 192.168.0.1 to validate. This is fine, but growth of the net has seen us run out of IPV4 addresses and so we need to validate against IPV6 addresses also. Here we show the use of the FILTER_FLAG_IPV6 flag.



<?php

/*** an IP address ***/
$ip "2001:0db8:85a3:08d3:1319:8a2e:0370:7334";

/*** try to validate as IPV6 address ***/
if(filter_var($ipFILTER_VALIDATE_IPFILTER_FLAG_IPV6) === FALSE)
        {
        echo 
"$ip is not a valid IP";
        }
else
        {
        echo 
"$ip is valid";
        }
?>

Above we see that the IPV6 address is a valid form of IP and passes validation. Next we see if an IP falls within a private range.


<?php

/*** an IP address ***/
$ip "192.168.0.1";

/*** try to validate a private range address ***/
if(filter_var($ipFILTER_VALIDATE_IPFILTER_FLAG_NO_PRIV_RANGE) === FALSE)
        {
        echo 
"$ip is within a private range";
        }
else
        {
        echo 
"$ip is not a private IP";
        }
?>

The above code will print the line
192.168.0.1 is within a private range
because the IP address is within the range of private IP address as specified in the RFC. Try changing the IP to a public IP address, that is one used over the internet, and see the results.

The FILTER_FLAG_NO_RES_RANGE, as the name suggests, will allow no reserved range IP addresses. Only valid non-reserved address will pass validation here.

<?php

/*** an IP address ***/
$ip "255.255.255.255";

/*** try to validate a non reserve range address ***/
if(filter_var($ipFILTER_VALIDATE_IPFILTER_FLAG_NO_RES_RANGE) === FALSE)
        {
        echo 
"$ip is within a reserved range";
        }
else
        {
        echo 
"$ip is not within a reserved range";
        }
?>

Of course, the IP address 255.255.255.255 is within a reserved range and it fails to validate against the FILTER_FLAG_NO_RES_RANGE flag and returns bool FALSE. The filter will take an IPV4 or IPV6 IP address. Try this simple script with some other IP addresses such as:
66.163.161.117
2001:0db8:85a3:08d3:1319:8a2e:0370:7334
and see this in action.

Validate Email Address

What validation suite would be complete without the ability to validate an email address. Almost everybody has their own function and regex for this and many holy wars have been fought over the "correct" method of doing it. We saw earlier how we could do this with the FILTER_VALIDATE_REGEXP filter and supplying a REGEX to the filter. Of course, everybody has their own REGEX that is better than everybody elses. Now we have a standard way that everybody can use. Lets put it to work.

<?php

/*** an email address ***/
$email "kevin@foo.bar.net";

/*** try to validate the email ***/
if(filter_var($emailFILTER_VALIDATE_EMAIL) === FALSE)
        {
    
/*** if it fails validation ***/
        
echo "$email is invalid";
        }
else
        {
    
/*** if the address passes validation ***/
        
echo "$email is valid";
        }
?>

So, there you have the email validated and the world is a better place having saved many kittens. This filter takes no addition flags or options, it simply validates the email.

Sanitizing Variables

Whilst it is well to be able to validate the data we use, it is equally important to be able to clean up any data that may come to our scripts, especially data from user land. The filter_var() function also contains filters for many data types that will clean up data for use in our scripts. Here we will show their uses in a simple context.

Sanitize A String

The FILTER_SANITIZE_STRING filter allows us to filter various information from a string to allow us to safely use the data within for our applications. Lets look at the possible flags for FILTER_SANITIZE_STRING. You can use this to strip tags, unwanted characters or even encode them.

  • FILTER_FLAG_NO_ENCODE_QUOTES
  • FILTER_FLAG_STRIP_LOW
  • FILTER_FLAG_STRIP_HIGH
  • FILTER_FLAG_ENCODE_LOW
  • FILTER_FLAG_ENCODE_HIGH
  • FILTER_FLAG_ENCODE_AMP

These flags perform various sanitizing functions on a string. Without them the FILTER_SANITIZE_STRING works in this way.


<?php

/*** a string with tags ***/
$string "<script>foo</script>";

/*** sanitize the string ***/
echo filter_var($stringFILTER_SANITIZE_STRING);

?>

We see from the above code that the <script></script> tags have been removed leaving only the "foo" text. Using the optional flags we can gain a little more control of this behaviour. Lets modify our string variable a little to include some quotes.

<?php

/*** a string with tags ***/
$string "<script>\"'foo'\"</script>";

/*** sanitize the string ***/
echo filter_var($stringFILTER_SANITIZE_STRING);

?>

If we look at the HTML source produced from the above code, we we see that we now have the <script></script> tags remove and the quotes have been encoded to produce a string like &#34;&#39;foo&#39;&#34;
Should we wish to keep the quotes the FILTER_FLAG_NO_ENCODE_QUOTES flag can help us.


<?php

/*** a string with tags ***/
$string "<script>\"'foo'\"</script>";

/*** sanitize the string ***/
echo filter_var($stringFILTER_SANITIZE_STRINGFILTER_FLAG_NO_ENCODE_QUOTES);

?>

Now we see from the above code, the quotes have not been encoded and are returned without their HTML entity values. We can gain a little more control of the characters we wish to encode with the filter by stipping or encoding only the high, or low characters. High and low characters are defined as those with an ASCII value below 32 or above 32. Lets code up some chars and put the required flags to work.


<?php

/*** a string with tags ***/
$string "<li><script>!@#$%^&*foo</script><br><p /><li />";

/*** sanitize the string ***/
echo filter_var($stringFILTER_SANITIZE_STRINGFILTER_FLAG_STRIP_LOW)

?>

The companion flag to this is of course FILTER_FLAG_STRIP_HIGH, which strips all other characters. It is used in the same manner.


<?php

/*** a string with tags ***/
$string "<li><script>!@#$%^&*foo</script><br><p /><li />";

/*** sanitize the string ***/
echo filter_var($stringFILTER_SANITIZE_STRINGFILTER_FLAG_STRIP_HIGH)

?>


This is all well for stripping out unwanted characters, but does not address the issue of keeping them. To this end we use the encoding functions in the same way.


<?php

/*** a string with tags ***/
$string "<li><script>!@#$%^&*foo</script><br><p /><li />";

/*** sanitize the string ***/
echo filter_var($stringFILTER_SANITIZE_STRINGFILTER_FLAG_ENCODE_LOW)

?>


By now you should be getting into the swing of the filter functions and flags. It should be quite easy to see the companian flag to the code above and how FILTER_FLAG_ENCODE_HIGH works. It will encode ASCII characters over 32.


<?php

/*** a string with tags ***/
$string "<li><script>!@#$%^&*foo</script><br><p /><li />";

/*** sanitize the string ***/
echo filter_var($stringFILTER_SANITIZE_STRINGFILTER_FLAG_ENCODE_HIGH);

?>


Lastly in this section is the FILTER_FLAG_ENCODE_AMP. No prizes for guessing this one. It encodes the & character to &amp;


<?php

/*** a string with an ampersand ***/
$string "http://phpro.org/file.php?foo=1&bar=2";

/*** sanitize the string ***/
echo filter_var($stringFILTER_SANITIZE_STRINGFILTER_FLAG_ENCODE_AMP);

?>

The resulting string will look the same in the browser, however, if you view the source you will see the ampersand has been correctly encode to look like
http://phpro.org/file.php?foo=1&#38;bar=2

The FILTER_SANITIZE_STRING (string) filter has an alias called FILTER_SANITIZE_STRIPPED (stripped). I have no idea why it is there and suggest it dies a slow painful death as it can only lead to confustion.

URL Encode

The ability to url encode data has previously fallen to the urlencode() function. This functionality has been embrace by the filter extension and provides more features than previously. We can now URL encode a string and optionally strip or encode special characters. Lets see it in action.


<?php

/*** a url string ***/
$url "http://phpro.org/a dir!/file.php?foo=1&bar=2";

/*** sanitize (url encode) the url ***/
echo filter_var($urlFILTER_SANITIZE_ENCODED);

?>

The above code will produce a string like this
http%3A%2F%2Fphpro.org%2Fa%20dir%21%2Ffile.php%3Ffoo%3D1%26bar%3D2
As you can see, the spaces and special chars have been encoded for use in urls. Like the string filter, the encoded filter has HIGH and LOW filtering options with flags that can be set. The LOW filter deals with special chars below ASCII 32 and the HIGH deals with the rest.The flags allow the filtering or sanitizing of the input string. The flags should look familiar by now and are listed below.

  • FILTER_FLAG_STRIP_LOW
  • FILTER_FLAG_STRIP_HIGH
  • FILTER_FLAG_ENCODE_LOW
  • FILTER_FLAG_ENCODE_HIGH

Lets put them each through thier paces to see how each works.


<?php

/*** a url string ***/
$url "http://phpro.org/a dir!/file.php?foo=1&bar=2";

/*** echo our string with the stripped chars ***/
echo filter_var($urlFILTER_SANITIZE_ENCODEDFILTER_FLAG_STRIP_LOW);

?>

Any characters below ASCII 32 will be stripped from the URL variable above. Of course, we can strip all characters above ASCII 32 with FILTER_FLAG_STRIP_HIGH as shown below.


<?php

/*** a url string ***/
$url "http://phpro.org/a dir!/file.php?foo=1&bar=2";

/*** echo our string with the stripped chars ***/
echo filter_var($urlFILTER_SANITIZE_ENCODEDFILTER_FLAG_STRIP_HIGH);

?>

Rather than strip the any ASCII chars from a url we could encode them instead using FILTER_FLAG_ENCODE_HIGH or FILTER_FLAG_ENCODE_LOW.


<?php

/*** a url string ***/
$url "one two&three";

/*** echo and encode the string ***/
echo filter_var($urlFILTER_SANITIZE_ENCODEDFILTER_FLAG_ENCODE_LOW)

?>

The same again but encoding the HIGH characters.


<?php

/*** a url string ***/
$url "one two&three";

/*** echo and encode the string ***/
echo filter_var($urlFILTER_SANITIZE_ENCODEDFILTER_FLAG_ENCODE_HIGH)

?>

Sanitize Special Chars

This filter option allows us to HTML-escape '"<>& and characters with ASCII value less than 32. Because of this there is no FILTER_FLAG_ENCODE_LOW flag. The available flags are listed here

  • FILTER_FLAG_STRIP_LOW
  • FILTER_FLAG_STRIP_HIGH
  • FILTER_FLAG_ENCODE_HIGH


<?php

/*** our string ***/
$string "?><!@#$%^&*()}{~bobthebuilder";

/*** echo the sanitized string ***/
echo filter_var($stringFILTER_SANITIZE_SPECIAL_CHARS)

?>

If we view the result in our browser and view the source, the result from the above code will look this:
?&#62;&#60;!@#$%^&#38;*()}{~bobthebuilder
As you can see, it has seized on the several characters and HTML encoded them. Next we will see how it works with the FILTER_FLAG_STRIP_LOW flag.


<?php

/*** our string ***/
$string "кириллица";

/*** echo the sanitized string ***/
echo filter_var($stringFILTER_SANITIZE_SPECIAL_CHARSFILTER_FLAG_STRIP_LOW);

?>

The sanitized string from above should look a little like this:
кириллица
funky....

We can of course choose to strip the HIGH ASCII values like this:


<?php

/*** our string ***/
$string "fooкириллицаbar";

/*** echo the sanitized string ***/
echo filter_var($stringFILTER_SANITIZE_SPECIAL_CHARSFILTER_FLAG_STRIP_HIGH);

?>

Finally, we can encode the high chars


<?php

/*** our string ***/
$string "fooкириллицаbar&fubar";

/*** echo the sanitized string ***/
echo filter_var($stringFILTER_SANITIZE_SPECIAL_CHARSFILTER_FLAG_ENCODE_HIGH);

?>

In the above code, the foo and the bar remain, whilst the characters between foo and bar have been sanitized, whilst the ampersand has been encoded.

Filter Unsafe RAW

This is perhaps the oddest of the filters. The PHP manual quotes it as "Do nothing, optionally strip or encode special characters." Well, doing nothing is what I do best. Not to be out-performed it still keeps all the flags as listed below.

  • FILTER_FLAG_STRIP_LOW
  • FILTER_FLAG_STRIP_HIGH
  • FILTER_FLAG_ENCODE_LOW
  • FILTER_FLAG_ENCODE_HIGH
  • FILTER_FLAG_ENCODE_AMP

You should by now know how to use the STRIP and ENCODE flags. If you do not, return to the top of this document and start from their. Here we will show only the FILTER_FLAG_ENCODE_AMP flag in use.


<?php

/*** our string ***/
$string "Bed & Breakfast";

/*** echo the sanitized string ***/
echo filter_var($stringFILTER_UNSAFE_RAWFILTER_FLAG_ENCODE_AMP);

?>

The code above will print the following line
Bed &#38; Breakfast
As you can see,the ampersand is now encoded as the flag dictates.

Sanitize an Email Address

Earlier we saw how to filter and validate an email address. Here we can take the email address and sanitize it. That is, remove illegal or unwanted characters from it. It is surprising the amount of characters that are allowed in a valid email address. They are:
All letters, digits and $-_.+!*'(),{}|\\^~[]`<>#%";/?:@&=.
Lets put it to the test.

<?php

/*** an email address ***/
$email "kevin&friends@(foo).ex\\ample.com";

/*** sanitize the email address ***/
echo filter_var($emailFILTER_SANITIZE_EMAIL);

?>

The above code will produce the output:
kevin∓friends@foo.example.com
The ampersand has remained but the () and the three backslashes have been removed, leaving us with a usable, or sanitized, email address.

Sanitize a URL

Unlike the FILTER_SANITIZE_ENCODED filter that encodes URL strings, or the FILTER_VALIDATE_URL which checks if a URL is valid, the FILTER_SANITIZE_URL will strip out illegal characters. The characters that are not removed are letters and digits and the following:
$ - _ . + ! * ' ( ) , { } | \ \ ^ ~ [ ] ` > < # % " ; / ? : @ & = .
To see it in action is simple as this filter takes no flags.

<?php

/*** a URL ***/
$url "http://www.phÆpro.oФrg";

/*** sanitize the URL ***/
echo filter_var($url,  FILTER_SANITIZE_URL);

?>

The url string above contains 2 utf-8 characters (your browser may not show these, check source) which are illegal in urls. The FILTER_SANITIZE_URL filter has stripped these characters from the string leaving us with a valid URL.

Sanitize an Integer

To sanitize an Integer is simple with the FILTER_SANITIZE_INT filter. This filter strips out all characters except for digits and . + -
It is simple to use and we no longer need to boggle our minds with regular expressions.

<?php

/*** an interger ***/
$int "abc40def+;2";

/*** sanitize the integer ***/
echo filter_var($intFILTER_SANITIZE_NUMBER_INT);

?>

The above code produces an output of 40+2 as the none INT values, as specified by the filter, have been removed.

Sanitize a Float

Sanitizing a float is a little more playful and as FILTER_SANITIZE_NUMBER_FLOAT filter takes 3 flags.

  • FILTER_FLAG_ALLOW_FRACTION
  • FILTER_FLAG_ALLOW_THOUSAND
  • FILTER_FLAG_ALLOW_SCIENTIFIC

These flags are self explanatory and are easy to use as shown below. First we will put the FILTER_SANITIZE_NUMBER_FLOAT filter to the test without any flags.

<?php

/*** a floating point number ***/
$float "abc40.4def+;2";

/*** sanitize the float ***/
echo filter_var($floatFILTER_SANITIZE_NUMBER_FLOAT);

?>

Like the previous example this filter has stripped out all chacaracters except for digits and the + and - characters. The dot character is removed with this filter. Optionally would could keep the .(dot) character with the optional FILTER_FLAG_ALLOW_FRACTION flag.

<?php
/*** a floating point number ***/
$float "abc40.4def+;2";

/*** sanitize the float ***/
echo filter_var($floatFILTER_SANITIZE_NUMBER_FLOATFILTER_FLAG_ALLOW_FRACTION);

?>

Above we now see that the script returns
40.4+2
as we have now allowed fractional notation. We could also allow thousand seperators with the FILTER_FLAG_ALLOW_THOUSAND flag as demonstrated below.

<?php

/*** a floating point number ***/
$float "abc40.,000+;2";

/*** sanitize the float ***/
echo filter_var($floatFILTER_SANITIZE_NUMBER_FLOATFILTER_FLAG_ALLOW_THOUSAND);

?>

Now the script above returns
40,000+2
because we have allowed the use of the thousand seperator in the string. If we wished to use scientific notation we can also allow the use of the characters e and E as we see below here.

<?php

/*** a floating point number ***/
$float "abc40+;4.2e";

/*** sanitize the float ***/
echo filter_var($floatFILTER_SANITIZE_NUMBER_FLOATFILTER_FLAG_ALLOW_SCIENTIFIC);

?>

This should now be rather self explanatory as we see the flag allowing scientific notation and the script returns
40+42e
Note the the decimal seperator has been removed also.

Magic Quotes

The use of magic quotes in PHP has been a contentious issue from its inception. The removal of magic quotes in favour of user defined escaping of characters makes a lot of sense. Here we can impliment the functionality of magic quotes without having them force upon us by php.ini settings which may vary from server to server. The FILTER_SANITIZE_MAGIC_QUOTES filter applies the addslashes function to a string, thus providing us with an escaped string for use in our applications.

<?php

/*** a string ***/
$string "test'test2'test3'' test\'\"test5";

/*** sanitize the float ***/
echo filter_var($stringFILTER_SANITIZE_MAGIC_QUOTES)

?>

As we can see, this above script returns
test\'test2\'test3\'\' test\\\'\"test5
as the addslashes() function has been applied to the string and escaped the characters that need to be escaped.

Callback Filter

The FILTER_CALLBACK filter does exactly what it says. Calls a user-defined function to filter our data. This functionality permits us full control of the filtering of data. Here we will begin with a simple user defined function that converts spaces to underscores.

<?php

/**
* Callback function
* Convert spaces to underscores
*
* @param $string
*
* @return string
*
**/
function space2underscore($string) {
  return 
str_replace(" ""_"$string);
}

$string "This is not a love song";

echo 
filter_var($stringFILTER_CALLBACK, array("options"=>"space2underscore"));

?>

We see the filter has used our space2underscore() function as a callback and converted the spaces in the string so that it now returns
This_is_not_a_love_song.
We could use a PHP function rather than our own as demonstrated here

<?php

/*** a string ***/
$string "This is not a love song";

/*** use a PHP defined function as callback ***/
echo filter_var($stringFILTER_CALLBACK, array("options"=>"ucwords"));

?>

Now we see that using a simple PHP callback function is simple on our string and the return value is
This Is Not A Love Song
. If a non-existant function is used an error an error is generated as shown below.

<?php

/*** a string ***/
$string "phpro.org";

/*** a callback without a declared function ***/
filter_var($stringFILTER_CALLBACK, array("options"=>"non_existant_function"));

?>

The above script will produce an error like
Warning: filter_var() [function.filter-var]: First argument is expected to be a valid callback in /www/fil.php on line 6
We have seen above the use of several functions to use as a callback. But what if we wanted to use several functions for our callback array. Lets put it to the test.

<?php

/*** a string ***/
$string "This is not a love song";

/*** use a PHP defined function as callback ***/
echo filter_var($stringFILTER_CALLBACK, array("options"=>array("ucwords""strtolower")));

?>

GRIM...
We see above that the use of mulitple callbacks is not permitted and will generate an error as follows:
Warning: filter_var() [function.filter-var]: First argument is expected to be a valid callback in /home/kevin/html/fil.php on line 7.
We can however, call a class method as shown below here.

<?php

/*** class method callback ***/
class my_class{

/**
* Callback method
* Convert spaces to underscores
*
* @param $string
*
* @return string
*
**/
function space2underscore($string) {
  return 
str_replace(" ""_"$string);
}

}
/*** end of class ***/

$string "This is not a love song";

echo 
filter_var($stringFILTER_CALLBACK, array("options"=>array("my_class""space2underscore")));

?>

Moving on from the above class we can also use a call back with a thown exception like this..

<?php

/* thrown exception in the callback */
function check_num(&$num) {
if(
$num != 4)
    {
    throw new 
Exception("This is an exception error message");
    }
}

/*** declare a number variable ***/
$num 3;

try {
    
filter_var($numFILTER_CALLBACK, array("options"=>"check_num"));
    }
catch (
Exception $e)
    {
    echo 
$e->getMessage();
    }
?>

From the above code we see that the $num variable does not match the value in the check_num() method and so an exception is thrown. The exception is then caught as usual in the first catch block and the error messages is displayed.

The INPUT Filter

As the name suggests, the input filter gets input from outside our script and can then filter it. The function used for this is the filter_input() function. With this we can validate our variables as the come in from userland and be sure they are dealt with before we start using them. This ensures we have some semblance of a security model in place. If you are not moving to this architecture then you are letting yourself, and your customers, down. The input filter can gather data from several sources.

  • INPUT_GET
  • INPUT_POST
  • INPUT_COOKIE
  • INPUT_ENV
  • INPUT_SERVER
  • INPUT_SESSION (Not yet implemented)
  • INPUT_REQUEST (Not yet implemented)

Here follows a simple example of using the filter_input() function to deal with GET variables. Lets assume you have a URL of the the type http://www.example.com?num=7
Lets see how we can validate this using our input filter.

<?php

/*** filter the input number from GET ***/
if(filter_input(INPUT_GET'num'FILTER_VALIDATE_INT, array("options" => array("min_range"=>1"max_range"=>10))) != FALSE)
        {
        echo 
$_GET['num'].' is valid';
        }
else
        {
        echo 
'Invalid number supplied';
        }
?>

As seen with earlier use of the FILTER_VALIDATE_INT filter, we are able to validate that the supplied value is a digit and that it is with the range of 1 to 10. Should an invalid value be supplied the filter_input will return bool(false). The INPUT_GET parameter tells the filter_input that the value is coming from GET.

In the same manner as we validated the Integer above, we can do the same for any value. Here we show the filter_input in use with a different filter.

<?php

/*** filter the GET text variable with a value of <kevin> ***/
echo  filter_input(INPUT_GET'text'FILTER_SANITIZE_SPECIAL_CHARS);

?>

The above code uses the FILTER_SANITIZE_SPECIAL_CHARS filter to check the value of the GET variable named text. When if finds special chars it converts them for use like this
↦#62;kevin&#60;

Of course we can use any of our filters in this way, here we use the FILTER_SANITIZE_ENCODED filter to encode the variable for use in a url. We use a url of the type filter.php?text=php pro

<?php
/*** input url is filter.php?text=php pro ***/

/*** fiter and encode the GET text variable for use in a url ***/
$url filter_input(INPUT_GET'text'FILTER_SANITIZE_ENCODED);

/*** echo the encoded url ***/
echo '<a href="?text='.$url.'">Link</a>';

?>

From the code above we get the encoded url value like this
<a href="?text=php%20pro">Link</a>
Note that the space has been converted to a HTML special char for use in the url.

So, lets assume we are recieving our data from a form and we need to access the POST array. This is done in the same mammer as accessing GET variables with INPUT_POST as we show here.


<form method="post">
<input type="text" name="number"  value="123blah456" />
<input type="submit" />
</form>
<?php

/*** echo number from form ***/
echo filter_input(INPUT_POST"number"FILTER_SANITIZE_NUMBER_INT);

?>

If we use the form above we find INPUT_POST applies the FILTER_SANITIZE_NUMBER_INT filter to the $_POST['number'] variable. It strips out the characters that are not digits which we can then use in our applications. With this sort of flexibility in use of the various filters, it is a small step to use any filter by applying the same methods. Here we show a simple form POST and a callback to prepare data for INSERTing into a MySQL database.


<form method="post">
<input type="text" name="answer"  value="123bla''h456" />
<input type="submit" />
</form>
<?php

/*** use a callback filter to mysql_real_escape_string ***/
$answer filter_input(INPUT_POST"answer"FILTER_CALLBACK, array("options"=>"mysql_real_escape_string"));

/*** create an sql query ***/
$sql "INSERT INTO quiz (answers) VALUES ('{$answer}')";

/*** echo the query ***/
echo $sql;
?>

The code above demonstrates how flexible the filter extension is. We could of course use $_SERVER or $_SESSION as the INPUTs and the functionality would remain the same.

Filter an Array

Up till now we have focused on validation of variables from various sources. Here we show how the filters can be used on an array of values. This is done using the filter_var_array() function. We will show here a simulated POST array that could come from a form and how to apply different filters to the various array members.

<?php

/**
* Callback function
* @format a name
* @param string $name
* @return string formated name
**/
function formatName($name){
  return 
ucwords(strtolower($name));
}

/*** simulated POST array ***/
$_POST = array(
   
"name"  => "KeViN WatersoN",
   
"age"   => "42",
   
"email" => "example@phpro.org",
);

/*** an array of filters ***/
$filters = array(
   
"name" => array("filter"=>FILTER_CALLBACK"flags"=>FILTER_FLAG_ARRAY"options"=>"formatName"),
   
"age"  => array("filter"=>FILTER_VALIDATE_INT"flags"=>FILTER_FLAG_ARRAY"options"=>array("min_range" => 1"max_range" => 100)),
   
"email"    => FILTER_VALIDATE_EMAIL,
);

/*** apply the filters to the array ***/
$filtered_array filter_var_array($_POST$filters);

/*** loop and display keys and values ***/
foreach($filtered_array as $k=>$v)
        {
        echo 
$k." - ".$v."<br />";
        }
?>

Lets step through the above code to see what we have done here. The output will be like:
name - Kevin Waterson
age - 42
email - example@phpro.org
We began with a simple callback function that we use to apply later in the script. Next we created a simulated POST array to simulate the data coming from a form, or possibly CURL. We then create an array of filters we wish to supply to each of the POST variables. It is then a simple matter for us to apply the filters array to the POST array with the filter_var_array() function. We loop over our filtered_array and see the values. This is by far a superior method of handling variables from users. Of course the POST data could be any array of data. You may be able to see how helpful the callback filter will be when used on POST data intended for INSERTing into a database. A simple callback to a function such as mysql_real_escape_string(), as seen previously, would make life a lot simpler for some.

As mentioned, any array could be used. A POST array was used above as an example, but there is a better way to hand inputs using the filter_input_array. Like the filter_input() function, the filter_input_array() function takes it array of values from inputs as follows

  • INPUT_GET
  • INPUT_POST
  • INPUT_COOKIE
  • INPUT_SERVER
  • INPUT_ENV
  • INPUT_SESSION
  • INPUT_REQUEST

<form method="post">
<input type="text" name="name"  value="Kevin" /><br />
<input type="text" name="age"   value="42" /><br />
<input type="text" name="email" value="example@phpro.org" /><br />
<input type="submit" />
</form>
<?php

$filters 
= array(
   
"age"   =>array("filter"=>FILTER_VALIDATE_INT"flags"=>FILTER_FLAG_ARRAY|FILTER_NULL_ON_FAILURE"options"=>array("min_range"=>0"max_range"=>100)),
   
"name"  => FILTER_SANITIZE_SPECIAL_CHARS,
   
"email" => FILTER_SANITIZE_EMAIL,
   
"notset"=> FILTER_VALIDATE_INT
);

/*** apply the filters to the POST array ***/
$filtered filter_input_array(INPUT_POST$filters);

/*** echo the filtered array members ***/
echo $filtered['name'] .'<br />'$filtered['age'] .'<br />'$filtered['email'].'<br />';

/*** check for the notset variable ***/
if(filter_has_var(INPUT_POST"notset") !== false)
        {
        echo 
'Variable is in filter';
        }
else
        {
        
var_dump($filtered["notset"]);
        }
?>

Stepping through this code we see some familiar filters and some new introductions. When using the FILTER_VALIDATE_INT for the age variable we have added the flag constant FILTER_NULL_ON_FAILURE. This flag tells the filter engine to return NULL rather than bool(false) if the filter fails. This can be handy if we wish to INSERT into a database or something. Wehave used the filter_input_array() function to apply the filters to their respective POST variables much the same as we did with the filter_var_array() function earlier. From there it is simple to echo the filtered array members, or if our program dictated, we may have other uses for them.

At the very last we have used the filter_has_var() function to check if the variable notset is in the INPUT_POST filter. As it is not it returns NULL as seen with the var_dump().

Filter an Array with CALLBACK

Of course, not all arrays come from outside our scripts. Quite often we need to perform operations on our own arrays. Previously, if we wanted to use a callback would would have used array_walk() to apply a callback function. The filter extension deals with this just as simply as with any array. In the following example we will do something useful. The task of hiding email addresses from spam bots is an age old task and many have taken to use ASCII values instead to print thier mails. Latter day spambots read these also, but it makes for a good callback example to iterate over each character of an email address and convert it to its ASCII value using the PHP ord() function.

<?php
echo encodeEmail('example@phpro.org');

/**
 *
 * Return ASCII value web use
 *
 * @param string
 *
 * @return string
 *
 */
function makeASCII($char=0){
  return 
'&#'.ord($char).';';
}


/**
 *
 * @Encode an email to ascii
 *
 * @parma string
 *
 * @return string
 *
 */
function encodeEmail($email){
/*** check if the filter has a var ***/
if(filter_var($emailFILTER_VALIDATE_EMAIL) !== FALSE)
    {
    
/*** split the email into single chars ***/
    
$charArray str_split($email);
    
/*** apply a callback funcion to each array member ***/
    
$encodedArray filter_var($charArrayFILTER_CALLBACK, array('options'=>"makeASCII"));
    
/*** put the string back together ***/
    
$encodedString implode('',$encodedArray);
    return 
'<a href="mailto:'.$encodedString.'">'.$encodedString.'</a>';
    }
else
  {
  return 
false;
  }
}

?>

The string resulting from this will be an ASCII encode mail address for use on a web page like this
<a href="mailto:&#107;&#101;&#118;&#105;&#110;&#64;&#112;&#104;&#112;&#114;&#111;&#46;&#111;&#114;&#103;">&#107;&#101;&#118;&#105;&#110;&#64;&#112;&#104;&#112;&#114;&#111;&#46;&#111;&#114;&#103;<a>
More importantly, what we have done is used the filter_var() function on an array, just as we would a string. The same principles apply and the filter will apply the callback function to each member of the array. It does'nt get any easier than this.

A Real World Example

We have seen above many ways in which we can use the filter extension to improve our script. The use in the real world will not differ greatly from what you have seen here, but will have different value for different applications. With the focus on security, this section has blossomed into its own tutorial which is available at http://www.phpro.org/tutorials/PHP-Security.html. This tutorialis highly recommended for those wishing to use the filter functions for input validation and sanitation.

Credits

Many thanks to Derick Rethans and Pierre for their help with the code in this tutorial and quick fixes to bugs in the PHP 5.2.0 core as we came across them. Pierre and Derick are PHP core developers and the creators of the filter extenstion. You can catch the latest from Pierre on his blog at http://blog.thepimp.net.