Filtering and validating PHP data. Common mistakes

A well designed filter is powerful tool, which users can use. In fact, this is an important function if your website (online store) has a lot of products distributed in different categories.

SOURCES

For e-commerce, it's a way to increase conversion rates by reducing the time it takes for a user to find what they're looking for.

Creating such features is never easy: filters are highly dependent on the content of the website; Also, the filter bar should not be distracting, the focus should be on the content/products. So we tried to make your life a little easier by building you a highly customizable and easy to integrate CSS filter panel.

It takes advantage of CSS Transitions, CSS Transformations and jQuery to provide smooth transitions when needed.

Creating a structure

The HTML structure is a little more complex than usual. First of all, there are two main blocks of content: the header and main elements, the second one is used to wrap both gallery.cd-gallery and filter.cd-filter . In addition to this we have a nested navigation (nested within 2 div elements, due to the dropdown effect visible on mobile devices) and filter trigger.cd-filter-trigger .

You may also notice a lot of class names (for example in gallery list items) and data filters: these are used for filtering content, not for styling.

Note. The purpose of the .cd-gallery> li.gap element is to work in combination with text: justify; Property applied to .cd-gallery to create a gallery grid. You need to create as many .gap elements as maximum amount elements in line -1.


Content Filter






  • All

  • All

  • Color 1

  • Color 2









No results found




Block title





Close

Filters

Adding a style

Most CSS concerns styling form elements and other basic decorations. It's interesting how we defined and used some classes - in combination with jQuery - to change the behavior of some elements based on certain events.

For example: On all devices, the filter bar freezes when it reaches the top of the viewport. To achieve this effect, we used the .is-fixed class applied to the main element (.cd-main-content) so that we can orient some of its children. Specifically: .cd-tab-filter-wrapper is in a static position, while .cd-filter and .cd-filter-trigger are in an absolute position (relative to .cd-main-content). When we apply the .is-fixed class to .cd-main-content , we switch the position of all these elements to Fixed.

Cd-tab-filter-wrapper ( background-color: #ffffff; z-index: 1; ) .cd-filter ( position: absolute; top: 0; left: 0; width: 280px; height: 100%; background: #ffffff; z-index: 2; transform: translateX(-100%); transition: transform 0.3s, box-shadow 0.3s; ) .cd-filter-trigger ( position: absolute; top: 0; left: 0; height: 50px; width: 60px; z-index: 3; ) .cd-main-content.is-fixed .cd-tab-filter-wrapper ( position: fixed; top: 0; left: 0; width: 100% ; ) .cd-main-content.is-fixed .cd-filter ( position: fixed; height: 100vh; overflow: hidden; ) .cd-main-content.is-fixed .cd-filter-trigger ( position: fixed ; )

Another thing worth mentioning is the .filter-is-visible class. It is applied to multiple elements when the user launches the filter panel. On all devices, it is used to change the translateX value of the .cd-filter element (from -100% to 0). On larger devices (>1170px) we also target .cd-gallery and .cd-tab-filter and reduce their width: this way the panel will not overlap the content and the user will use additional features Space to apply filters and view changes at the same time, without having to close the panel.

Event Handling

To implement the content filter functionality, we integrated the MixItUp jQuery plugin. To initialize the plugin in the gallery container, we use the mixItUp() function and declare a buttonFilter variable that contains all custom functionality filter. Additionally, we use jQuery to open/close the filter panel and fix it (along with the tabbed navigation) so that it still shows up when scrolling through the gallery.

How to use

First, you need to download the plugin archive from the developer’s page and unpack it into a directory on your website. Two versions are available - a minimized (Production version) and for developers (Development version). In the version for developers, the text of the plugin is presented in a structured form with comments, which is convenient for understanding the principle of operation (the source code for the lesson contains a version of the plugin for developers with translated comments).

Then to the section on the page on which you plan to use filtering, you need to insert the plugin connection code:

$(document).ready(function() ( $("element_for_filtering").liveFilter("option"); ));

You need to replace “/path_to_plugin/” with the path where the plugin is located liveFilter, which you unpacked earlier. You also need to replace “element_for_filtering” CSS selector, which matches the desired element.

Plugin parameter "option" controls the use of animation when hiding and showing elements during filtering. The following values ​​are available: basic - elements are simply turned off/on without any animation, slide - filtered elements will be collapsed/expanded, fade - filtered elements will be gradually turned on/off.

For example:

$(ul#filter_me).liveFilter("slide");

The above code tells the LiveFilter plugin to filter unordered list with id “ filter_me” and use animation “ slide”.

The plugin can be used for unordered and ordered lists and tables. You must specify the required selector when calling the plugin.

Important! For the plugin to work, you need to add a text input field with the class to the page "filter". This field will be used to enter filtering text:

Example of a page using a filter:

Example of using the LiveFilter plugin $(document).ready(function() ( $(ul#filter_me").liveFilter("slide"); ));

  • Point No. 1.
  • Point No. 2.
  • Point No. 3.
  • Point No. 4.
  • Point No. 5.
  • Point No. 6.
  • Point No. 7.
  • Point No. 8.
  • Point No. 9.
  • Point No. 10.

Plugin code

(function($)( $.fn.liveFilter = function (aType) ( // Determine what will be filtered. var filterTarget = $(this); var child; if ($(this).is("ul")) ( child = "li"; ) else if ($(this).is("ol")) ( child = "li"; ) else if ($(this).is("table")) ( child = " tbody tr"; ) // Define variables var hide; var show; var filter; // Event for the input element $("input.filter").keyup(function() ( // Get the filter value filter = $(this) .val(); // Get what needs to be hidden and what needs to be shown hide = $(filterTarget).find(child + ":not(:Contains("" + filter + ""))"); show = $(filterTarget).find(child + ":Contains("" + filter + "")") // Animate the items that need to be hidden and shown if (aType == "basic") ( hide.hide() ; show.show(); ) else if (aType == "slide") ( hide.slideUp(500); show.slideDown(500); ) else if (aType == "fade") ( hide.fadeOut(400 ); show.fadeIn(400); ) )); // Custom expression for the case-insensitive text function contains() jQuery.expr[":"].Contains = function(a,i,m)( return jQuery(a ).text().toLowerCase().indexOf(m.toLowerCase())>=0; ); ) ))(jQuery);

We learned how to collect data on the client and send it to the server. And on the server they wrote a stub in the place where products filtered by the entered parameters should be returned. Now we will get rid of the stub and write a couple of methods and queries that pull the necessary products from the database and return them to the client. The lesson is quite short. Let's get started

What do we do?

We only need to complete 3 points:

  • 1. Receive data from the client and process it to suit the needs of the server. For example, set the default parameters
  • 2. Write, in fact, the code itself to retrieve products from the database. First of all, prepare an sql query
  • 3. Return the received data to the client
Receiving data from the client

You may ask: why do we need to highlight this simple operation separately if we can easily pull out all the data from the $_GET array?

Firstly, in order to set the default values. You can't rely on the client to take care of this himself.

Secondly, not all data is in $_GET in a usable form. For example, it is more convenient for us to pass sorting from the client with one parameter in the form of field_direction, for example, price_asc. But in a sql query these are separate entities, so they need to be pre-processed.

The situation is similar with brands. On the client, we send them as an array of brands, and php also receives them as an array. But for an sql query you need a string - a list of brands separated by commas. Therefore, brands also need to be further processed.

So, let's write a getOptions() function that will retrieve data from $_GET and convert it into a form convenient for us. I have already given almost all the introductory information, so let’s immediately look at the finished code.

// Getting data from the array _GET function getOptions() ( // Category and prices $categoryId = (isset($_GET["category"])) ? (int)$_GET["category"] : 0; $minPrice = ( isset($_GET["min_price"])) ? (int)$_GET["min_price"] : 0; $maxPrice = (isset($_GET["max_price"])) ? (int)$_GET["max_price" ] : 1000000; // Brands $brands = (isset($_GET["brands"])) ? implode($_GET["brands"], ",") : null; // Sorting $sort = (isset($ _GET["sort"])) ? $_GET["sort"] : "price_asc"; $sort = explode("_", $sort); $sortBy = $sort; $sortDir = $sort; return array(" " => $brands, "category_id" => $categoryId, "min_price" => $minPrice, "max_price" => $maxPrice, "sort_by" => $sortBy, "sort_dir" => $sortDir); )

Here we see that we first get the category id. If the category is not passed, we consider category_id = 0. The minimum price will be 0, the maximum will be 1 million. If your online store sells plutonium (oil to the Chinese, ants individually), then you can always add zeros to the required line or, at worst, make payments in euros.

Let's transform the sorting differently. Separately, pull out the sorting field and the parameter: asc or desc.

Please note that in all cases we do not forget to substitute the default value if the required parameter did not arrive from the client. And now that all the data has been converted, all that remains is to return it from the function in the associative array via return array(...)

Preparing an SQL query and retrieving data from the database

All data has been prepared in the form we need, now let’s write a request and execute it. The getGoods($options, $conn) function will do this. In parameters it takes $options - data prepared by the previous function, and $conn - the database connection object that we created in the previous lesson. Our task is to write a sql query. In general it looks like this:

Select g.id as good_id, g.good as good, b.brand as brand, g.price as price, g.rating as rating, g.photo as photo from goods as g, brands as b where g.category_id = selected_category and g.brand_id in (comma-separated list of brands) and g.brand_id = b.id and (g.price between minimum_price and maximum_price) order by sort_field sort_direction

We retrieve the fields we need by applying a series of where conditions and specifying the desired sort. There are no issues with prices and sorting; we simply substitute the required values ​​in the appropriate places in the request. But you need to be more careful with the category and brands, and here’s why.

Each product we have always has a category. There is no concept of a zero category in our database - we did this for our own convenience, in order to understand that the user in the browser did not select any category (or selected all - for us it is the same thing). And in this case we should not include the line in the request
g.category_id = selected_category and
The same goes for brands; if they are not selected, then we skip the corresponding line. Here's what it looks like in code.

// Receiving goods function getGoods($options, $conn) ( // Required parameters $minPrice = $options["min_price"]; $maxPrice = $options["max_price"]; $sortBy = $options["sort_by"] ; $sortDir = $options["sort_dir"]; // Optional Parameters$categoryId = $options["category_id"]; $categoryWhere = ($categoryId !== 0) ? " g.category_id = $categoryId and " : ""; $brands = $options["brands"]; $brandsWhere = ($brands !== null) ? " g.brand_id in ($brands) and " : ""; $query = " select g.id as good_id, g.good as good, b.brand as brand, g.price as price, g.rating as rating, g.photo as photo from goods as g, brands as b where $ categoryWhere $brandsWhere g.brand_id = b.id and (g.price between $minPrice and $maxPrice) order by $sortBy $sortDir "; $data = $conn->query($query); return $data->fetch_all(MYSQLI_ASSOC); )

First, we extract the price and sorting variables from the $options array - they are simply inserted into the query without changes. And for the category and brands, we form the $categoryWhere and $brandsWhere lines according to the principle: the necessary condition for the where section, if there is data, and an empty line if there is no data. Thus, we got a fairly sane sql query that takes into account all our wishes. The last two lines execute this request and return from the function an array of objects with the required fields. All that remains is to put everything together and send the received goods back to the already waiting client/browser.

We return goods to the client

This is the easiest part of the lesson. Let's look at the stub written in the previous lesson.

// Connect to the database $conn = connectDB(); // Return a successful response to the client echo json_encode(array("code" => "success", "data" => $_GET));

Let's replace this code with

// Connect to the database $conn = connectDB(); // Receive data from the client $options = getOptions(); // Get goods $goods = getGoods($options, $conn); // Return a successful response to the client echo json_encode(array("code" => "success", "options" => $options, "goods" => $goods));

We added a couple of lines: the getOptions function retrieved the data into the $options variable. We immediately used it to obtain getGoods goods, the results were saved in $goods. And we expanded the response to the client. Data parameter renamed it to options and returned not the contents of $_GET, but already converted values. And in the goods parameter, an array of received goods was returned.

This concludes the lesson. While we cannot use this data on the client, render it in the browser - we will do this in the next lesson. But we can always open the console, poke buttons and checkboxes and make sure that the server returns us the correct goods.

Checking the results of the work

Let's select the Smartphones category and mark the brands Apple and Samsung. In the response we will see that the server returned 3 products, sorted by ascending price

Now let's set the minimum price to 20 thousand and change the sorting to descending price
As you can see, there are now only 2 products - one Samsung was discarded due to a price of 17 thousand that did not match the filters. And the goods are sorted in reverse. If you did everything correctly, you will see exactly the same picture.

You can still play with the console and make sure that the data is returned correctly. In the end, the most important thing is to get the filters to work correctly, returning the correct list of products. Scattering the received data across the page, taking into account the ready-made layout, is outwardly the most interesting thing, but from a development point of view it is quite simple. But I won’t get ahead of myself - details in the next lesson.

The material is intended mainly for beginner web programmers.

Introduction. Often I am approached by clients who have installed self-written CMS or modules written by novice web programmers who do not understand what is needed to protect data and often copy filtering functions without thinking about how they work and what exactly needs to be done with them.

Here I will try to describe in as much detail as possible common mistakes when filtering data in PHP script and give simple tips how to properly filter data.

There are many articles on the Internet about data filtering, but they are not complete and without detailed examples.

Debriefing.Filtering. Error No. 1 For numeric variables, the following check is used:
$number = $_GET["input_number"]; if (intval($number)) ( ... execute the SQL query... )
Why will it lead to SQL injection? The fact is that the user can specify the value in the input_number variable:
1"+UNION+SELECT
In such cases, the check will be completed successfully, because the intval function gets the integer value of a variable, i.e. 1, but nothing has changed in the $number variable itself, so all malicious code will be passed to the SQL query.
Correct filtering:
$number = intval($_GET["input_number"]); if ($number) ( ... execute SQL query... )
Of course, the condition can change, for example if you need to get only a certain range:
if ($number >= 32 AND $number dir = MAIN_DIR . "/template/" . $config["skin"];
IN in this case you can replace the value of the $_COOKIE["skin"] variable and cause an error, as a result of which you will see the absolute path to the site folder.
If you use the cookie value to save to the database, then use one of the filtering described above, this also applies to the $_SERVER variable. Filtering. Mistake #5. The register_globals directive is enabled. Be sure to turn it off if it's on.
In some situations, you can pass the value of a variable that should not have been passed, for example, if the site has groups, then for group 2 the $group variable should be empty or equal to 0, but it is enough to fake the form by adding the code:

In a PHP script, the $group variable will be equal to 5 if it was not declared in the script with a default value. Filtering. Mistake #6. Check your downloaded files.
Check the following points:
  • File extension. It is advisable to prohibit downloading files with extensions: php, php3, php4, php5, etc.
  • Is the file uploaded to the server move_uploaded_file
  • file size
  • Examination. Mistake #1. I came across cases when for an AJAX request (for example: increasing reputation) the user’s name or ID was passed (to whom the reputation was increasing), but in PHP itself there was no check for the existence of such a user.
    For example:
    $user_id = intval($_REQUEST["user_id"]); ... INSERT INTO REPLOG SET uid = "($user_id)", plus = "1" ... ... UPDATE Users SET reputation = reputation+1 WHERE user_id = "($user_id)" ...
    It turns out that we are creating an entry in the database that is completely useless to us. Check. Mistake #2. When performing various types of actions (adding, editing, deleting) with data, do not forget to check the user’s rights to access this function and additional features (use html tags or the ability to publish material without verification).

    A long time ago I corrected a similar error in one forum module, when any user could edit a message to the administration.

    Examination. Mistake #3. When using multiple php files do a simple check.
    In the index.php file (or in any other main file), write the following line before including other php files:
    define("READFILE", true);
    At the beginning of other php files write:
    if (! defined ("READFILE")) ( exit ("Error, wrong way to file.
    Go to main."); }
    This way you will restrict access to files. Check. Mistake #4. Use hashes for users. This will help prevent this or that function from being called via XSS.
    An example of compiling a hash for users:
    $secret_key = md5(strtolower("http://site.ru/" . $member["name"] . sha1($password) . date("Ymd"))); // $secret_key is our hash
    Next, in all important forms, substitute the input with the value of the user’s current hash:

    While running the script, check:
    if ($_POST["secret_key"] !== $secret_key) ( exit ("Error: secret_key!"); ) Check. Mistake #5. When outputting SQL errors, make a simple restriction on access to information. For example, set the password for the GET variable:
    if ($_GET["passsql"] == "password") ( ... SQL error output... ) else ( ... Just information about the error, no details... )
    This will allow you to hide information from the hacker that can help him hack the site. Verification. Mistake #5. Try not to include files by getting file names from outside.
    For example:
    if (isset($_GET["file_name"])) ( include $_GET["file_name"] .".php"; )
    Use the switch

    CSS3 filters render in the browser visual effects, similar to Photoshop filters. Filters can be added not only to images, but also to any non-empty elements.

    The set of filters is not limited to those preinstalled in the browser. You can also use SVG filters by downloading them from the link along with the svg element.

    Filters were originally created as part of the SVG specification. Their task was to apply pixel grid-based effects to vector graphics. With the support SVG browsers It became possible to use these effects directly in browsers.

    Browsers process the page pixel by pixel, applying the specified effects and drawing the result on top of the original. Thus, by using several filters you can achieve different effects; they seem to overlap each other. The more filters, the longer it takes the browser to render the page.

    You can apply multiple filters at the same time. The classic way to apply such effects is when hovering over an element: hover .

    Browser support

    IE: not supported
    Edge: 13.0 except url()
    Firefox: 35.0
    Chrome: 18.0 -webkit-
    Safari: 9.1, 6.0 -webkit-
    Opera: 40.0, 15.0 -webkit-
    iOS Safari: 9.3, 6.1 -webkit-
    Android Browser: 53.0, 4.4 -webkit-
    Chrome for Android: 55.0, 47.0 -webkit-

    filter
    blur() The value is specified in length units, for example px, em. Applies a Gaussian blur to the original image. The larger the radius value, the greater the blur. If no radius value is specified, the default is 0.
    brightness() The value is specified in % or decimal fractions. Changes the brightness of the image. The higher the value, the brighter the image. The default value is 1.
    contrast() The value is specified in % or decimal fractions. Adjusts the contrast of the image, i.e. the difference between the darkest and lightest areas of the image/background. The default value is 100%. A value of zero will hide the original image under a dark gray background. Values ​​increasing from 0 to 100% or 0 to 1 will gradually open the original image to the original display, and values ​​above will increase the contrast between highlights and shadows.
    drop-shadow() The filter acts similar to the box-shadow and text-shadow properties. Uses the following values: X offset Y offset blur stretch shadow color. Distinctive feature filter is that a shadow is added to elements and its contents taking into account their transparency, i.e. if the element contains text inside, then the filter will add a shadow for both the text and visible boundaries block. Unlike other filters, for this filter it is necessary to set parameters (the minimum is the offset value).
    grayscale() Extracts all the colors from a picture, making the output a black and white image. The value is specified in % or decimal fractions. The higher the value, the stronger the effect.
    hue-rotate() Changes the colors of the image depending on the specified rotation angle in the color wheel. The value is specified in degrees from 0deg to 360deg. 0deg is the default value, meaning no effect.
    invert() The filter makes the image negative. The value is specified in %. 0% does not apply a filter, 100% completely converts colors.
    opacity() The filter works similarly to opacity property, adding transparency to the element. A distinctive feature is that browsers provide hardware acceleration for the filter, which improves performance. An added bonus is that the filter can be combined with other filters at the same time, creating interesting effects. The value is set to % only, 0% makes the element completely transparent, and 100% has no effect.
    saturate() Controls color saturation, working on the principle of a contrast filter. A value of 0% removes color, while 100% has no effect. Values ​​from 0% to 100% reduce color saturation, values ​​above 100% increase color saturation. The value can be specified either in % or as an integer, 1 is equivalent to 100% .
    sepia() An effect that imitates antiquity and “retro”. A value of 0% does not change appearance element, and 100% fully reproduces the sepia effect.
    url() The function accepts the location of an external XML file with an svg filter, or an anchor to a filter located in the current document.
    none Default value. Indicates no effect.
    initial Sets this property to its default value.
    inherit Inherits the property value from the parent element.