2 factor Login Authentication – PHP & Twilio SMS

In this tutorial, we are going to implement 2 factor login authentication. 2 factor login authentication means that user will be verified twice before login.

  1. 1st factor should be what they know, like password or some security question.
  2. 2nd factor will be what they have, like mobile or fingerprint.

Database structure

First create a table where all users data will be stored. You might already have that if you are working on existing project. You need to add a new field “phone” in your existing “users” table because we will be sending SMS to this field value. There should be an option to enable or disable this 2 factor authentication, so a boolean field named “is_tfa_enabled”. Also a pin number which will be sent as SMS.

users

  • id int(11) NOT NULL AUTO_INCREMENT
  • email text NOT NULL
  • password text NOT NULL
  • phone text NOT NULL
  • is_tfa_enabled tinyint(1) NOT NULL
  • pin text NOT NULL

User registration

During registration, we need to add a new field for phone number. Below is a sample registration form with password encryption.

Set default values to following fields:

  • “is_tfa_enabled” as false
  • “pin” as empty
<form method="POST" action="register.php">
	
	<input type="email" name="email">
	<input type="password" name="password">
	<input type="text" name="phone">
	
	<input type="submit" name="register">
</form>

register.php

<?php

	if (isset($_POST["register"]))
	{
		$email = $_POST["email"];
		$phone = $_POST["phone"];
		$password = $_POST["password"];
		$password = password_hash($password, PASSWORD_DEFAULT);

		$conn = mysqli_connect("localhost", "root", "", "tutorials");
		
		$sql = "INSERT INTO users (email, phone, password, is_tfa_enabled, pin) VALUES ('$email', '$phone', '$password', 0, '')";
		mysqli_query($conn, $sql);

		header("Location: login.php");
	}

?>

User login – Send OTP

There will be no extra field in login, it will have email/username and password or whatever fields you are using to login. The important thing is, we need to send an SMS if the user has enabled 2-factor authentication. Below is a typical login form:

<form method="POST" action="login.php" id="form-login">
	<input type="email" name="email">
	<input type="password" name="password">
	
	<input type="submit" name="login">
</form>

We will send an SMS using Twilio if user has enabled 2-factor authentication. First you need to include Twilio PHP library in your project using composer. Open terminal in your project root folder and run the following command to install Twilio:

composer require twilio/sdk

Create an account on Twilio from here. After creating account, you will be given a $15 balance to test the API. On your Twilio console dashboard you will find your “account SID” and “auth token”. You will need to place those on below variables accordingly:

Twilio console

login.php

<?php

	session_start();

	require_once "vendor/autoload.php";
	use Twilio\Rest\Client;

	$sid = "";
	$token = "";

	if (isset($_POST["login"]))
	{
		$email = $_POST["email"];
		$password = $_POST["password"];

		$conn = mysqli_connect("localhost", "root", "", "tutorials");
		
		$sql = "SELECT * FROM users WHERE email = '$email'";
		$result = mysqli_query($conn, $sql);

		if (mysqli_num_rows($result) > 0)
		{
			$row = mysqli_fetch_object($result);
			if (password_verify($password, $row->password))
			{
				if ($row->is_tfa_enabled)
				{
					$row->is_verified = false;
					$_SESSION["user"] = $row;

					$pin = rand(0, 9) . rand(0, 9) . rand(0, 9) . rand(0, 9) . rand(0, 9) . rand(0, 9);
					
					$sql = "UPDATE users SET pin = '$pin'  WHERE id = '" . $row->id . "'";
					mysqli_query($conn, $sql);

					$client = new Client($sid, $token);
					$client->messages->create(
						$row->phone, array(
							"from" => "",
							"body" => "Your adnan-tech.com 2-factor authentication code is: ". $pin
						)
					);

					header("Location: enter-pin.php");
				}
				else
				{
					$row->is_verified = true;
					$_SESSION["user"] = $row;

					header("Location: index.php");
				}
			}
			else
			{
				echo "Wrong password";
			}
		}
		else
		{
			echo "Not exists";
		}
	}

?>
  1. We need to call session_start() in order to use any $_SESSION variable.
  2. If the user has provided correct email and password, then we are checking if that user has enabled 2-factor authentication for his account.
  3. Then we are generating a random pin code of 6 digits and sending it to the user’s phone number.
  4. We are using “is_verified” field in $_SESSION to check if user account is pin verified or not.
  5. After SMS has been sent, you will be redirected to “enter-pin.php” where you need to enter the pin sent in SMS.

enter-pin.php

This file will only contain 1 field to enter pin code. User ID is already been stored in $_SESSION.

<form method="POST" action="enter-pin.php">
	<input type="text" name="pin">
	
	<input type="submit" name="enter_pin">
</form>

And when this form submits, we are simply going to check the entered pin with database and login the user. We also need to empty the pin from database so that it cannot be used again.

<?php

	session_start();

	if (isset($_POST["enter_pin"]))
	{
		$pin = $_POST["pin"];
		$user_id = $_SESSION["user"]->id;

		$conn = mysqli_connect("localhost", "root", "", "tutorials");
		
		$sql = "SELECT * FROM users WHERE id = '$user_id' AND pin = '$pin'";
		$result = mysqli_query($conn, $sql);

		if (mysqli_num_rows($result) > 0)
		{
			$sql = "UPDATE users SET pin = '' WHERE id = '$user_id'";
			mysqli_query($conn, $sql);

			$_SESSION["user"]->is_verified = true;
			header("Location: index.php");
		}
		else
		{
			echo "Wrong pin";
		}
	}

?>

Now after verification user will be redirected to “index.php” which is by default the home of every website. You might already have a check to prevent the user from entering this page without login. Just need to change few things,

  • Check if user is logged in and pin verified.
  • A switch to turn on/off 2-factor authentication.
  • A link to logout the user.

index.php

<?php

session_start();
$conn = mysqli_connect("localhost", "root", "", "tutorials");

if (isset($_SESSION["user"]) && $_SESSION["user"]->is_verified)
{

	$user_id = $_SESSION["user"]->id;

	if (isset($_POST["toggle_tfa"]))
	{
		$is_tfa_enabled = $_POST["is_tfa_enabled"];

		$sql = "UPDATE users SET is_tfa_enabled = '$is_tfa_enabled' WHERE id = '$user_id'";
		mysqli_query($conn, $sql);

		echo "Settings changed";
	}

	$sql = "SELECT * FROM users WHERE id = '$user_id'";
	$result = mysqli_query($conn, $sql);
	$row = mysqli_fetch_object($result);

	?>

	<form method="POST" action="index.php">
		<h1>Enable TFA</h1>

		<input type="radio" name="is_tfa_enabled" value="1" <?php echo $row->is_tfa_enabled ? "checked" : ""; ?>> Yes
		<input type="radio" name="is_tfa_enabled" value="0" <?php echo !$row->is_tfa_enabled ? "checked" : ""; ?>> No

		<input type="submit" name="toggle_tfa">
	</form>

	<a href="logout.php">
		Logout
	</a>

	<?php
}
else
{
	header("Location: login.php");
}

We are displaying 2 radio buttons and an ability to automatically select the radio based on enable or disability of 2-factor authentication. You can use the same condition on all pages which you want to show to only authenticated users. Now we just need to logout the user. We have created a link with text “Logout” which when clicked will redirect the user to “logout.php” page.

logout.php

<?php

session_start();
unset($_SESSION["user"]);
session_destroy();
header("Location: login.php");

?>

Re-send OTP after 30 seconds

If you want to resend the OTP after 30 seconds, first, we need to call a Javascript function using setTimeout method.

setTimeout(function () {
// [call AJAX here]
}, 30 * 1000)

Then we need to call an AJAX to resend the OTP. The following code goes in the [call AJAX here] section:

const form = document.getElementById("form-login")

const ajax = new XMLHttpRequest()
ajax.open("POST", "login", true)

ajax.onreadystatechange = function () {
	if (this.readyState == 4) {
		if (this.status == 200) {
			console.log(this.responseText)
		}
	}
}

const formData = new FormData(form)
ajax.send(formData)

Show timer

If you want to show the timer for 30 seconds. First, create a <div> where the timer text will be displayed.

<div id="timer"></div>

Then we need to call a setInterval function to call each second and decrement the value.

let seconds = 30

const timerInternval = setInterval(function () {
	seconds--
	document.getElementById("timer").innerHTML = seconds
	if (seconds <= 0) {
		stopInterval(timerInternval)
	}
}, 1000)

That’s how you can add 2 factor login authentication with a timer on your website. If you want to know how to add “reset password” feature in your website, check our free tutorial here.

https://drive.google.com/file/d/1NosW0DI4WBG6cFz9f0xzluNAnkBPma1H/view?usp=sharing

Custom sort – jQuery UI, AJAX, PHP & MySQL

We are going to implement a custom sort using jQuery UI, which helps you to customize the order of data being displayed on your website. First, add CDN for jquery and jquery UI. Goto cdnjs.com to get CDN link for jQuery and visit this link to get CDN link for jQuery UI. Copy the JS link for jQuery, and CSS and JS links for jQuery UI. Create 2 script tags for jQuery and jQuery UI JS and one link tag for jQuery UI CSS.

index.php

<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.min.js">
</script>

<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.1/jquery-ui.css">

<script src="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js">
</script>

First you need to show all data from database. Here we are connecting with our sample database and displaying all products based on their product code.

<?php

    $conn = mysqli_connect("localhost", "root", "", "classicmodels");
    $result = mysqli_query($conn, "SELECT * FROM products ORDER BY productCode DESC");

?>
  1. Create a div container which will enclose all data.
  2. Create a while loop to run through all records in database.
  3. Inside this loop, create a div tag which will represent each product separately.
  4. Give it a class so it will group each product under same class.
  5. and give it a unique ID, now ID will be the value which will tell the order of all products in an array. In our database, unique ID of product is productCode, so we will use that. But you must use as per your database table.
<div id="myData">
    <?php while ($row = mysqli_fetch_object($result)) { ?>

        <div 
            class="item"
            id="<?php echo $row->productCode; ?>"
            style="margin-top: 0px; margin-bottom: 0px;">
                
            <img
                src="<?php echo $row->image; ?>"
                style="width: 200px;">

            <h3><?php echo $row->productName; ?></h3>

        </div>
    <?php } ?>
</div>

Now we need to find a way to able to drag them so we can move them. So,

  1. Create a script tag.
  2. Attach a listener which will be called when the page is fully loaded.
  3. Select the div container using ID.
  4. Call sortable function, it exists in jquery UI. Its constructor will receive an object where you can specify options.
<script>

    window.products = [];
    
    $(function () {
        $("#myData").sortable({
            "items": ".item",
            "opacity": 0.6,
            "cursor": "move",
            "update": function (event, ui) {
                var data = $("#myData").sortable("toArray");
                
                // if using table
                // data = data.filter( n => n );

                window.products = data;
            }
        });
    });
</script>

jQuery UI sortable options

  1. “items”: Selector of each individual item. So we will use the div inside loop having class class=”item”.
  2. “opacity”: Give it an opacity, so when we select some product it will be highlighted among others.
  3. “cursor”: A cursor which will be displayed on div only during dragging an item.
  4. “update”: A function which will be called when you finish dragging some item from one place to another.

The update function has 2 parameters, events and UI (although we are not going to use any of them but you might need them in your case). Inside this function, we will get the new order of items in a variable. Select the main div container tag using its ID selector and call sortable function same as above. And inside it pass the argument “toArray”. It will return all items unique ID in a sorted array.

If you are using tables instead of divs, you might receive first element of array as empty string. You can prevent this by calling filter function to this array. Passing n means to remove null or empty strings.

Save custom sequence in database

Now we will create a button which when clicked will save this data in selected sequence.

<button type="button" onclick="saveData();">Save</button>

Create a function inside script tag:

function saveData() {
    var ajax = new XMLHttpRequest();
    ajax.open("POST", "save.php", true);
    ajax.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
    ajax.send("data=" + JSON.stringify(window.products));

    ajax.onreadystatechange = function () {
        if (this.readyState == 4 && this.status == 200) {
            console.log(this.responseText);
        }
    };
}
  1. First create an AJAX object.
  2. Call open method and set the request method to “POST”.
  3. Second parameter will be the name of file where data needs to be sent.
  4. Since this is a post request, so we also need to attach a header for content type.
  5. And send the data will be in name value pair separated by ampersand sign.
  6. Since we want to send an array so we need to convert that into JSON string.
  7. onreadystatechange event will be fired when the response has been received from server.

save.php

Create a new file named “save.php” which will handle the request. First make a connection with database. Create a new table in database which will hold the sequence of products, its structure will be as below:

Table name: product_order, columns:

id integer auto increment
product_id integer
order_number integer
Then in “save.php” paste the following code:

<?php

    $conn = mysqli_connect("localhost", "root", "", "classicmodels");

    $data = json_decode($_POST["data"]);

    mysqli_query($conn, "DELETE FROM product_order");

    $sql = "INSERT INTO product_order(product_id, order_number) VALUES";

    for ($a = 0; $a < count($data); $a++)
    {
        $sql .= " ('" . $data[$a] . "', '" . ($a + 1) . "')";

        if ($a < count($data) - 1)
        {
            $sql .= ",";
        }
    }

    mysqli_query($conn, $sql);
    echo "Done";

?>

First get all data that we are receiving in a separate variable, and since we are sending JSON string so we need to decode that JSON back in PHP array. First of all, whenever the sequence of products changes, we have to remove all the previous sequence from database. Then we will prepare an SQL query to insert the new sequence in product order table. So loop through each product ID. Product ID will be what we are receiving from AJAX. Order number must be starting from 1, to 2 3 and so on, that is why we are incrementing in variable $a.

Display data by custom sort

Now go back to index.php and change the SQL query to following:

<?php

$conn = mysqli_connect("localhost", "root", "", "classicmodels");

$result = mysqli_query($conn, "SELECT * FROM products INNER JOIN product_order ON products.productCode = product_order.product_id ORDER BY product_order.order_number ASC");

?>

So we just need to change this query to display products based on order number from product_order table. Inner join with product_order, based on productID. And order by order_number from product_order table. Ascending order will make it start from 1 to 2 and 3 and so on.

That’s how you can implement a custom sort in jQuery UI using AJAX, PHP and MySQL.

[wpdm_package id=’219′]

Multiple file upload in bootstrap modal – PHP & MySQL

We will teach you how you can create a bootstrap modal that handles multiple file upload and save them in MySQL database.

Download Bootstrap FileDialog

You are going to need a library called Bootstrap FD, you can get it from here. After downloading the library, extract the ZIP file and goto “dist” folder. Here you will find CSS and JS files, copy them in your project and include them in your header or footer. This library requires bootstrap and jQuery to be included in your project. If you have setup the bootstrap and jQuery, you can just use their CDN links rather than downloading.

<link href="//netdna.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap.min.css" rel="stylesheet">
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js"></script>
<script src="//netdna.bootstrapcdn.com/bootstrap/3.3.1/js/bootstrap.min.js"></script>

<link rel="stylesheet" href="bootstrap.fd.css">
<script src="bootstrap.fd.js"></script>

First we have included the bootstrap CSS file, then jQuery and then bootstrap JS file. Because bootstrap JS also requires jQuery so you must include the jQuery before bootstrap JS. You can change the version number of CDN links if there is new version available.

Show multiple file upload dialog

Create a button which when clicked will call a function in Javascript. To open the bootstrap modal, we have to use the function $.FileDialog() and this will open the pop-up where you can drag and drop your files. For multiple file upload, we will not be using an input type file. But we will be using a library called FileDialog. You can also set options for this dialog, for example, whether you want the user to select only images files or all type of files etc.

<form>
	<button type="button" onclick="selectFiles();">
		Select files
	</button>

	<input type="submit">
</form>
<script>
	function selectFiles() {
		$.FileDialog({
			"accept": "image/*"
		})
	}
</script>

To preview the image files selected by user, just add a div tag and give it a unique ID.

<div id="selected-images"></div>

Preview selected images

And change your $.FileDialog function to the following. “files.bs.filedialog” function will be called when user select the files and press “OK”. Create a global array which will hold all images. All selected images will be received in “event.files” array, so loop through it and push in that global array. Create a string variable which will hold the img tag HTML. In the same loop, we are creating an img tag and each object of “event.files” array contains a variable named “content” while contains the content of image. This can be used.

window.selectedImages = [];

$.FileDialog({
	"accept": "image/*"
}).on("files.bs.filedialog", function (event) {
	var html = "";
	for (var a = 0; a < event.files.length; a++) {
		selectedImages.push(event.files[a]);
		html += "<img src='" + event.files[a].content + "'>";
	}
	document.getElementById("selected-images").innerHTML += html;
});

To save these images on server, give your form a unique ID and attach an onsubmit event with it. We will be using FormData object to send all images and other form fields via AJAX. And onsubmit event will prevent the default behaviour of form. This will prevent the form from submitting and will call your javascript function.

Call AJAX to upload file

This function will create a new FormData object and append all images in it. Make sure to add brackets “[]” with images key so it will send all images as array. Otherwise, only the last selected image will be processed by server. Then it sends a neat AJAX request in Vanilla JS and attach the form data in it.

After the server processed the files and send the response back to client, you will need to show some text to user or wants to redirect to different page. So in “onreadystatechange” event you can put all your code which needs to be executed once response is received from server.

<form id="form" onsubmit="return submitForm();">
function submitForm() {
	var form = document.getElementById("form");
	var formData = new FormData(form);

	for (var a = 0; a < selectedImages.length; a++) {
		formData.append("images[]", selectedImages[a]);
	}

	var ajax = new XMLHttpRequest();
	ajax.open("POST", "Http.php", true);
	ajax.send(formData);

	ajax.onreadystatechange = function () {
		if (this.readyState == 4 && this.status == 200) {
			console.log(this.responseText);
		}
	};

	return false;
}

Http.php

In this file, create a connection with your database (you might have already set up). Create a separate table where path of each image will be saved. Loop through all the images, insert them in database, and finally save the image file in your server. Create a new folder named “images” or any other of your choice, where all images will be stored. Timestamp is prepended with each image name just to make it unique. As you might have seen, if you download some image from facebook or whatsapp, it always has a different name no matter if that image is uploaded by another person.

<?php

$conn = mysqli_connect("localhost", "root", "", "tutorials");

for ($a = 0; $a < count($_FILES["images"]["name"]); $a++)
{
	$path = "images/" . time() . "-" . $_FILES["images"]["name"][$a];

	$sql = "INSERT INTO images(image_path) VALUES('$path')";
	mysqli_query($conn, $sql);

	move_uploaded_file($_FILES["images"]["tmp_name"][$a], $path);
}

echo "Done";

That’s how you can create a bootstrap modal that handles multiple file upload and save them in MySQL database in PHP.

Learn how to show progress bar while uploading the file.

[wpdm_package id=’213′]

How to create and read RSS feed for your website – PHP

RSS feed is formerly known as RDF Site Summary feed, now it stands for Really Simple Syndication. It is a form of web feed that allows other computer programs to fetch updates about the website. Programs known as RSS aggregators are created which reads the feed in XML format in a standardized way.

Examples

For example, if you are running an E-commerce site. Then creating an RSS feed will help visitors to view the latest products on your website. Similarly, if you are running a blog. Then your RSS feed will be about the latest posts that you have published on your blog.

RSS feeds are XML files, so they must follow all the standard formats of XML. That includes the special characters. If your feed has some special characters like a single or double quote, you need to convert them to HTML entities before publishing or updating your feed.

RSS aggregators

The programs or scripts that are used to read these feeds are known as RSS aggregators. They follow a specific pattern to read and display feeds to users. So you must follow the rules and proper tags of XML to reach the global audience. There are a lot of tags which can be used in RSS but the most common are title, description, link, language, image, url, category, copyright and author.

How to create an RSS feed

There are multiple ways to create an RSS feed for your website and different people adopt different ways, that’s okay. You can either create a button on the admin panel to generate feeds and export the XML file. Then you can upload it manually using your cPanel file manager. Or you can add a function that will create or update an XML file whenever you post something new on your website. We will be using the second technique in this tutorial.

Also, you will need to create a button on your website’s main page from where users can see your feeds. One common practice is to create an anchor tag in HTML that will redirect the user to that XML file. User can then add that RSS link in this RSS aggregator software. Then will automatically be notified when you upload something new on your website. and your RSS feed gets updated.

Standard format

The standard format of creating an RSS feed is:

<?xml version="1.0" ?>
<rss version="2.0">
    <channel>
        <title></title>
        <description></description>
        <language>en-US</language>
        <link>http://website.com/</link>

        <item>
            <title>Pont Yacht
            <description>Measures 38 inches Long.</description>
            <link>http://website.com/prod.php?id=3212</link>
        </item>
    </channel>
</rss>

You can display as many item tags as you want but the format should remain the same. So copy and paste the following code in file where you want to generate your RSS feed. Typically whenever you post something new on your website.

<?php

$web_url = "http://" . $_SERVER["SERVER_NAME"] . $_SERVER["REQUEST_URI"];

$str = "<?xml version='1.0' ?>";
$str .= "<rss version='2.0'>";
	$str .= "<channel>";
		$str .= "<title>My website</title>";
		$str .= "<description>My website</description>";
		$str .= "<language>en-US</language>";
		$str .= "<link>$web_url</link>";

		$conn = mysqli_connect("localhost", "root", "", "classicmodels");
		$result = mysqli_query($conn, "SELECT * FROM products ORDER BY productCode DESC");

		while ($row = mysqli_fetch_object($result))
		{
			$str .= "<item>";
				$str .= "<title>" . htmlspecialchars($row->productName) . "</title>";
				$str .= "<description>" . htmlspecialchars($row->productDescription) . "</description>";
				$str .= "<link>" . $web_url . "/product.php?id=" . $row->productCode . "</link>";
			$str .= "</item>";
		}

	$str .= "</channel>";
$str .= "</rss>";

file_put_contents("rss.xml", $str);
echo "Done";
?>

First we are creating a variable $web_url that will hold the base URL of the website. You can just set this variable a static value. But we are trying to make it dynamic so you won’t have difficulty when moving to different domains. Then we are creating a string variable $str that will hold all the RSS feed value in string format. We will be using this variable when creating an XML file.

<channel>

Channel tag will be served as a category since you might have different types of data on your website. So you can put different data in different channel tags. For example, you might have an E-commerce site and also the latest blog posts from one of your blog site.

Then we have title, description, link and url tags, they will be used to explain the properties of channel tag. The link must be your website’s main URL and language can be in one of the standard language codes. For getting website main URL we will be using PHP built-in SERVER variable and SERVER_NAME will return the name of the server which in this case will be localhost, and REQUEST_URI will return the address of folder from where this script is getting executed.

Then we are connecting with the database and fetching all the latest products in descending order (newest to oldest). You can see that we are using htmlspecialchars() function which helps to convert special characters into HTML entities. Since XML do not interpret special characters. So if you have any special characters and you are not using this function, then it might trigger an error in your RSS feed.

Saving XML file

Finally, we are calling file_put_contents() function which will create an XML file with the content of $str variable. If the file with same name already exists, it will simply update the file and override the previous content. Or if you are using Linux, make sure you have folder’s write permission in order to create that file.

If you run the script now you will be able to see a file named rss.xml created in your project’s root folder. You can drag that file in your browser, and you will see the feed in proper XML format. Now the last thing you can do is to display a link in your website which when clicked will redirect the user to this page.

Link to feed

We will be creating an anchor tag which when clicked will redirect the visitors to that XML file. We have also downloaded a logo of RSS from the internet. Instead of text we will be placing this image inside the anchor tag. Just copy and paste the following code anywhere in your website where you want to display a link to this RSS feed:

<a href="rss.xml" target="_blank">
	<img src="feed-icon.png" style="width: 100px;">
</a>

Since we want the feed to open in a new tab so it will not disrupt the user from using the site, so we have added an attribute target=”_blank” which will tell the browser to open that link in a new tab.

[wpdm_package id=’207′]

PHP PDO prepared statement – CRUD Operation

PHP PDO (PHP Data Objects) is basically an abstraction to database access, which means it is not limited to only MySQL or any other database system. If you are building a website in MySQL and suddenly you are required to change the database from MySQL to PostgreSQL, if you are using PDO at backend then only minor changes needs to be done in order to switch database. However, in other cases, you might have to re-write all the queries again in order to switch database.

PDO also provides a very neat way to write prepared statements which helps to prevent SQL injection in your database. In this tutorial, we will be performing completed CRUD (Create, Read, Update, Delete) operation using PHP PDO & MySQL. For sending data from forms, we will using AJAX so you also learn to fetch data from database without having to refresh the page.

Create data using PHP PDO prepared statement

First we will create a form from where we will get the input from user, and when user click the submit button, it will send an AJAX request with all form data. At the server end, server will first connect with our sample database called classicmodels using PDO. Then we will get all fields attached in AJAX request, prepare a query to prevent from SQL injection. Finally execute the query to add a new row in database.

You need to create a new file named save-data.php which will store the record in database. As this is an insert operation, so we might need the newly inserted ID on client side. So we can get the newly inserted ID by calling the $conn->lastInsertId() method from PDO connection object.

index.php

<form onsubmit="return addData();">
    <p>
        <input id="firstName" placeholder="Enter first name">
    </p>

    <input type="submit">
</form>

<script>
	function addData() {
        var firstName = document.getElementById("firstName").value;

        var ajax = new XMLHttpRequest();
        ajax.open("POST", "save-data.php", true);
        ajax.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
        ajax.send("firstName=" + firstName);

        ajax.onreadystatechange = function () {
            if (this.readyState == 4 && this.status == 200) {
                console.log(this.responseText);
            }
        };

        return false;
    }
</script>

save-data.php

<?php

// Connecting with database
$conn = new PDO("mysql:host=localhost;dbname=classicmodels", "root", "");

// SQL query string
$sql = "INSERT INTO employees (firstName) VALUES(:firstName)";

// Preparing the statement
$result = $conn->prepare($sql);

// Actually executing the query in database
$result->execute(array(
	":firstName" => $_POST["firstName"]
));

// Sending inserted ID back to AJAX
echo $conn->lastInsertId();
?>

Remember that you should not write variables directly in SQL query string. You just have to mark the places where you wanted to put variables like :firstName. This will tell that we are going to map this with our variable later. Then in execute() function, you have to pass an array and map each string with corresponding variable.

Read data using PHP PDO prepared statement

Now that we have saved the data in database, now we need to display it to the user. First we will create table tag where we will display all records from database in tabular form. Then we will call an AJAX to request the server to pull records from database and return them as JSON. When data is returned from server, we will convert the JSON back to Javascript objects and display that in table rows and columns.

index.php

<table>
    <tbody id="data"></tbody>
</table>

<script>
	var ajax = new XMLHttpRequest();
	ajax.open("GET", "get-data.php", true);
	ajax.send();

	ajax.onreadystatechange = function () {
	    if (this.readyState == 4 && this.status == 200) {
	        var data = JSON.parse(this.responseText);

	        var html = "";
	        for (var a = 0; a < data.length; a++) {
	        	var id = data[a].employeeNumber;

	            html += "<tr>";
	                html += "<td>" + data[a].firstName + "</td>";
	            html += "</tr>";
	        }

	        document.getElementById("data").innerHTML += html;
	    }
	};
</script>

On server side, we will connect with database using PDO same way as we did for insert operation. When you are working on projects, make sure to use only one PDO object. You can do that by either using singleton objects or sending persistent attribute in PDO constructor after password field as below.

Using singleton

if ($conn == null)
{
	$conn = new PDO("mysql:host=localhost;dbname=classicmodels", "root", "");
}

Connect with database, execute the query to get all records from database. In this query, we are not using any variable input which is received from user, so no need to use prepared statement. But if you are required to use user input variable in query, you should follow the same method as we did for insert operation.

get-data.php

<?php

$conn = new PDO("mysql:host=localhost;dbname=classicmodels", "root", "", array(
	PDO::ATTR_PERSISTENT => true
));

$sql = "SELECT * FROM employees";
$result = $conn->query($sql);

echo json_encode($result->fetchAll());

?>

Adding persistent attribute in PHP PDO constructor will make your website faster by caching the connection. So the next time some script request for database connection, it will not create a new connection again but will return the cached connection. This will result in removing the overhead on database for making multiple connection on same database. Persistent connections are not closed at the end of script but will remain open for other scripts too.

Update data using PDO

Updating data consists of 3 parts.

  1. Create an edit button which when clicked will redirect to new page.
  2. Get all data of selected record from database and auto-populate that in form.
  3. Update the data in database when that form submits.

In your read operation AJAX where you are displaying data in table, add the below line inside the loop along with other <td> tags. This will redirect to edit page along with ID of selected record. That should be the unique ID in your table. We were already getting this ID variable in read operation.

index.php

html += "<td><a href='edit.php?id=" + id + "'>Edit</a></td>";

The code used for update operation is almost similar to the one we used for insert operation. First we will get the record from database using ID which we will be receiving from URL. User might temper that URL so we must bind that in prepared statement to prevent SQL injection.

The form will be similar to insert operation’s form, except we add a new hidden field for sending ID of selected record. It will also be appended in AJAX request. The rest will remains same.

edit.php

<?php

    $conn = new PDO("mysql:host=localhost;dbname=classicmodels", "root", "");

    $id = $_GET["id"];

    $sql = "SELECT * FROM employees WHERE employeeNumber = :id";
    $result = $conn->prepare($sql);
    $result->execute(array(
        ":id" => $id
    ));
    $data = $result->fetch();

?>

<form onsubmit="return editData();">
    <input type="hidden" id="id" value="<?= $id; ?>">
    <input id="firstName" placeholder="Enter first name" value="<?= $data["firstName"]; ?>">

    <input type="submit">
</form>

<script>
    function editData() {
        var firstName = document.getElementById("firstName").value;
        var id = document.getElementById("id").value;

        var ajax = new XMLHttpRequest();
        ajax.open("POST", "edit-data.php", true);
        ajax.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
        ajax.send("firstName=" + firstName + "&id=" + id);

        ajax.onreadystatechange = function () {
            if (this.readyState == 4 && this.status == 200) {
                console.log(this.responseText);
            }
        };

        return false;
    }
</script>

Delete data using PDO

Deleting data consists of 2 parts.

  1. Create a delete button which when clicked will send an AJAX request.
  2. Server will run the delete query on selected record’s unique ID.

In your read operation AJAX where you are displaying data in table, add the below line inside the loop along with other <td> tags. This will call our Javascript function along with ID of selected record. That should be the unique ID in your table. We were already getting this ID variable in read operation.

A new function deleteData(id) will be created which will accept the ID of selected record and pass that ID with the AJAX request to the server.

index.php

html += "<td><button onclick='deleteData(\"" + id + "\")'>Delete</button></td>";

function deleteData(id) {
    if (confirm("Are you sure ?")) {
        var ajax = new XMLHttpRequest();
        ajax.open("POST", "delete-data.php", true);
        ajax.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
        ajax.send("id=" + id);

        ajax.onreadystatechange = function () {
            if (this.readyState == 4 && this.status == 200) {
                console.log(this.responseText);
            }
        };
    }
}

Delete operation will be almost similar to update operation. You just create a prepared statement and map the ID which we are receiving from AJAX request and execute the query.

delete-data.php

<?php

$conn = new PDO("mysql:host=localhost;dbname=classicmodels", "root", "");

$sql = "DELETE FROM employees WHERE employeeNumber = :id";
$result = $conn->prepare($sql);
$result->execute(array(
	":id" => $_POST["id"]
));

echo "Done";

?>

If you want to delete the <tr> tag from <table> when the delete button is pressed, you need to give a custom data ID attribute to TR tag and call the remove() function to remove the TR node.

// In read operation AJAX loop
html += "<tr data-id='" + id + "'>";

// In deleteData() function
document.querySelector("tr[data-id='" + id + "']").remove();

So that’s the completed CRUD operation using PHP PDO & MySQL and AJAX. If you face any problem, feel free to ask in the comment’s section below.

[wpdm_package id=’203′]

Custom error handler – PHP

We will be craeting a custom error handler in PHP. As it name suggests, we will be displaying PHP errors in our own style.

Introduction

Error logs means to store a copy of an error that occurred in your website and saving it in a separate file. This helps you to keep track of all the errors in your website. By default PHP will display the error output on the screen. So if you are running a website on live server, it is always a good idea to keep track of any error, if happened.

For example, if a user perform some action that causes an logical error, so it causes a bug in your code. So you must know what and where that error occurred in your script. We can get the error code and the error code must be one of the following as mentioned in table. We can also get the error string which actually tells the error, for example, “undefined variable”, and file name which causes the error and also it’s line number.

Error levels

ValueConstantDescription
1E_ERRORFatal run-time error
2E_WARNINGRun-time warning
4E_PARSECompile-time parsing error
8E_NOTICEError in script/code
256E_USER_ERRORUser-generated error by calling trigger_error($error)
512E_USER_WARNINGUser-generated warning by calling trigger_error($error)
1024E_USER_NOTICECustom error
8192E_DEPRECATEDDeprecated code error
32767E_ALLAny type of error and warning

Following code will create a custom function and tells PHP to use this function instead of using built-in PHP error system. We will be storing logs for each day so the file name must be of today date. For this purpose, we are using PHP date($format) function to dynamically fetch today’s date. Then we are creating a variable to store error level, actual error, file name where error occurred and line number which causes the error.

We have echo the error so it will be visible in the browser, but can skip this line if you do not want to show the error to your users. We are using file_put_contents($filename, $content, $mode) function to save that error in file. You need to create a folder named logs where all error files will be saved. We will be using FILE_APPEND mode which will create a file if not exists, and append the data at the end of file if file of same name already exists.

PHP built-in error handler function

Finally we will call the set_error_handler($custom_function_name) function that tells the PHP to use custom function for handling errors. The parameter will be the name of function that we have just created, and it’s name must be in string. At the end, we have echo a variable which is not yet created, so it will trigger an error. We did this deliberately to generate an error.

<?php

// Create a custom error function
function on_error($error_no, $error, $filename, $linenumber)
{
	// get today date, saving logs for each day
	$today = date("Y-m-d");

	// Creating array for possible errors
	$error_levels = array(
		"1" => "Fatal error",
		"2" => "Warning",
		"8" => "Error",
		"1024" => "Custom error"
	);
	
	// Getting name of error by error level
	$str = $error_levels[$error_no] . ": ";

	// Display file name where error occurred
	$str .= $error . " in " . $filename;

	// Show line number which causes error
	$str .= " at " . $linenumber;

	// Moving to next line
	$str .= "\n";

	// Display error in browser
	// if you do not want to show errors to user,
	// then you can skip this line
	echo $str;

	// save the $str value in file
	file_put_contents("logs/" . $today . ".txt", $str, FILE_APPEND);
}

// Tells PHP to use custom function for errors
set_error_handler("on_error");

// Generating error deliberately
echo $a;

?>

If you are using Mac or Linux, you may also need to enable a folder to write permissions of the logs folder. That’s how you can create and use the custom error handler function in PHP and save errors in files as logs.

You can also enable all error reporting using default PHP functions:

ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);

In Javascript, you can catch all errors using the following code:

window.onerror = function (error, file, line) {
    alert(error + " at line " + line)
}

If you face any problems, feel free to ask in the comments section below.

[wpdm_package id=’201′]

Page level cache PHP

Page level cache in PHP means that the most frequent data will be stored in cache. So instead of requesting the same data from database, we will just use the cache to display that data. This will reduce the overhead on database by eliminating number of queries on SQL. Or any other database server you are using.

Cached data storage

Cached data will be stored in files. So it is advised not to store sensitive data, like passwords and credit cards, in the cache. Although no one will know by which name you have saved the cache. But in some circumstances, it can be readable. So we must save that data in a database which is not sensitive. And we can use page level cache in PHP without any fear.

Once data is cached in a file then whenever a user requests that data it will not be fetched from the database. Instead it will be fetched from the cache. This will decrease the page load time and hence will improve the performance of website. For example, if a database query took 6 seconds to fetch the data. Then using this technique you can skip those 6 seconds wait.

Expiry time of cached data

You can also set the expiry time of the cache. So if you have cached some data and now the user is seeing the data from cached files. Now, what if you made some changes in the database ? The user will still be seeing the old cached data ! That is where we need to set the expiry time of the cache. A common practice is to expire the cache after 24 hours but you can customize it as per your needs.

Display page load time

First, we need to find a way to check the actual page load time. So we can get the time when we were at 1st line of the page and we can get the time when we were at the last line of the page. Then we will get the difference between start and end time and we will know the number of seconds it takes to load the page.

<?php

$start_time = time();

?>

<?php

$end_time = time();
echo "<h1>Difference: " . ($end_time - $start_time) . " seconds</h1>";

Create cache

We set the name of the file where we want to store the cache. You can create a separate folder where all caches will be stored, most modern frameworks use this approach. We also will set the expiry time of the cache so it will fetch fresh data after a specified time period.

We will be using output buffering which is used to hold plain HTML before it is displayed in the browser. Whatever content is displayed between ob_start() and ob_get_contents() will be saved in the file.

Then we create a file using write mode and the content of the file will be plain HTML that is rendered on the webpage. So if you open the cache file, you will see that it will have all plain HTML (same as you see when you do “view page source”). That is why it is recommended not to save sensitive data like passwords and credit cards in the cache. Finally, we can close the file object and when you run the script now, you will be able to see a new file created with HTML content.

<?php

$cache_file = "cache.php";
$cache_time = 60; // 1 minute

// Start output buffering
ob_start();

// run all DB queries here

$file = fopen($cache_file, "w");
fwrite($file, ob_get_contents());
fclose($file);

Read cache

We will read the cache only if the file exists and it has not been expired. When you run the script the first time, then the file has not yet been created. So that means that you do not have any cache, after that we will check the expiry time of the cache using file modified time.

Now if the cache is found and it is not expired, then we will display the content from the cache instead of requesting from database. To read the file we have 2 options, one is to use the include or require function and the second is to use readfile function. However, readfile is more secure than include so we will be using this.

You can use else condition if you want to perform some other action, that depends on your needs. But for simplicity, we will stop the script after reading from the cache using the exit() function. Paste the following code after $cache_time variable and before ob_start() function.

<?php

$cache_time = 10; // seconds

if (file_exists($cache_file) && (filemtime($cache_file) + $cache_time > time()))
{
	readfile($cache_file);
	exit();
}

ob_start();

That’s it, now if you run the script you will see that when it loads from the database, it takes more time than when it does from the cache. The script is tested on multiple already created projects and by average it loads 5 seconds faster than using without cache.

Tired of clearing browser cache after making changes in CSS or JS files ?

Prevent browser cache from CSS, JS, and image files

[wpdm_package id=’199′]

Use sprites to reduce number of requests on website – Javascript, PHP

Generate sprite image

Sprite is a computer graphics term for a two-dimensional bitmap that is integrated into a larger scene, most often in a 2D video game. Which means that small images are combined into one large image to reduce the number of requests on your server. Sprites helps a lot to reduce the number of HTTP requests.

In CSS sprites, we combine multiple images into a single image. So, if you are using 12 small images in your CSS, it will take 12 requests without sprite. Now if you combine all these images into one image, you will need only 1 request.

The basic principle is, you will combine multiple images into one large image. When you try to access it, the web server will not fetch the new copy of each image everytime you request it. Instead it will fetch just one image which contains all images and you can display them by specifying their position. Same as the image above.

So we will first combine all necessary images into one image and save it as a “sprite.png”. Then we will display the image by telling it’s position. We will be using two files, 1st for combining multiple images into one (save-sprite.php) and 2nd to display the images from sprite.

Generate sprites

First we create an HTML5 canvas tag where all images will be drawn. Give it unique ID to be accessible in Javascript and give it appropriate width and height.

Then we create an array of images which holds width, height and path (src) of each image. We will be looping through this array to automatically draw image on canvas. Also a counter variable to tell the number of current image under process.

Then we get the canvas object and 2D context of canvas. Create a variable named marginLeft to automatically draw the image after the previous one. If this is not specified, then all images will be placed on top of other.

After that, you will create a recursive function that will draw all images inside the array. Inside this function, create a new Image() object and set the image path in src attribute. Call onload function with this image object to wait for the image to fully loaded. The draw the image using drawImage(imageObj, x, y, width, height) function that will render the image on canvas.

Increase margin from left for next image, increment the counter. If there is more images in array then recursion occurs by calling the function itself inside the function. Otherwise if all images are rendered then we will convert that canvas into image and call an AJAX function to save this image data as a sprite.

<!-- Canvas where all images will be drawn -->
<canvas id="myCanvas" width="1000" height="500"></canvas>

<script>

	// An array where all images should be entered
	var images = [{
		width: 308,
		height: 183,
		src: "image1.png"
	}, {
		width: 206,
		height: 260,
		src: "image2.png"
	}];

	// A variable used to get the number of current image
	var count = 0;

	// Get canvas element
	var c = document.getElementById("myCanvas");

	// Get canvas context used for drawing
	var ctx = c.getContext("2d");

	// Give margin to each image so they will be aligned horizontally
	var marginLeft = 0;

	// A recursive function which will keep calling itself
	// until all images are drawn on canvas
	function loadImage() {
		// Create new image object
		var imageObj = new Image();

		// Set the path of image
		imageObj.src = images[count].src;

		// Wait till image fully loaded
		imageObj.onload = function() {
			// Draw image on canvas given image object, x, y, width & height
			ctx.drawImage(imageObj, marginLeft, 0, images[count].width, images[count].height);

			// Increase margin from left
			marginLeft += images[count].width;

			// Increment to next image
			count++;

			// If there is more image to draw
			if (count < images.length) {
				// Recursive occur here
				loadImage();
			} else {
				// All images has been drawn on canvas
				console.log("All images loaded");

				// Convert the canvas into PNG image
				var image = c.toDataURL("image/png", 1);

				// Create AJAX request object
      			var ajax = new XMLHttpRequest();

      			// Set method to POST, file name and asynchnronous
				ajax.open("POST", "save-sprite.php", true);

				// Set headers in POST request
				ajax.setRequestHeader("Content-type", "application/x-www-form-urlencoded");

				// Send the request and pass image data
				ajax.send("image=" + image);

				// Listen for server request changes
				ajax.onreadystatechange = function () {

					// Request is successful if ready state is 4 and status is 200
					if (this.readyState == 4 && this.status == 200) {
						// Print response sent from server
						console.log(this.responseText);
					}
				};
			}
		}
	}

	loadImage();
</script>

Save sprite image

Create a new file named “save-sprite.php” and paste the following code in it. This will give the image data from client and remove the “base64” part from it as that is not necessary. When using AJAX post request we should also convert all spaces into plus sign and then decode the base64 string. Finally you can save the image as separate file.

<?php

$image = $_POST["image"];
$image = explode(";", $image)[1];
$image = explode(",", $image)[1];
$image = str_replace(" ", "+", $image);

$image = base64_decode($image);
file_put_contents("sprite.png", $image);

echo "Done";
?>

Display image from sprite

To display an image we will create a <div> tag and set the background image as sprite. Given the background position allow you to display specific portion of sprite image. For example, if your sprite has 3 images of 100px width each, then your total sprite will be of 300px in width. To display the second image you have give the background position as -100px so it will move the image from left to right.

<style>
	#home {
		width: 308px;
		height: 183px;
		background: url('sprite.png');
		background-repeat: no-repeat;
	}

	#next {
		width: 206px;
		height: 260px;
		background: url('sprite.png');
		background-repeat: no-repeat;
		background-position: -308px 0px;
	}
</style>
<div id="home"></div>
<br>
<div id="next"></div>

Conclusion

Sprites are very useful specially in video games where optimization and performance is very important. So instead of loading all car’s types (Lamborghini, Bugatti, Ferrari, Mclaren) as 4 images they create a single sprite named “cars-sprite.png” which holds all the images of cars. This is just an example, you can categories the sprites as much as required. The point is to send minimum requests on the server.

Hope that helps you in your upcoming or legacy projects to reduce the number of HTTP requests using sprites. If you face any problem, feel free to ask in the comment’s section below.

[wpdm_package id=’195′]

Search in all tables and columns – PHP & MySQL

Search is one of the feature which is needed in almost every website. Whether you are working on admin panel or user side, you need to provide a functionality where user can search and get data. So, in this article, we will discuss how you can search a string in all tables and columns of a database. We will be using PHP and MySQL.

When it comes to admin panel, then it require to search all tables and all its fields in database for search query. For example, admin wants to know all payments transferred via account number 1234. Then he will simply type 1234 and it automatically scan all tables (including the payment’s table) and display the data.

While in typical environment, you have to manually set the table name along with column name to search. So we will make it to search complete database for searched query.

Create a form

Create a simple form where user can enter string and we will search that string in whole database.

<form method="GET" action="search.php">

    <input name="search" placeholder="Enter query">
    <input type="submit" name="submit">

</form>

Perform the search

Create a new file named “search.php” and paste the following code in it:

<?php

// Check if form submits
if (isset($_GET["submit"]))
{
	// Get searched query
    $search = $_GET["search"];

    // Connect with database
    $conn = mysqli_connect("localhost", "root", "", "classicmodels");
}

?>

First we need to get all tables inside the database. We will be running the MySQL command SHOW TABLES and it will return all tables. Then we will loop through each table. Here we will get object like “Tables_in_dbname” where “dbname” will be your database name. In this case, it will be “Tables_in_classicmodels”.

It will give us the table name which can be used to get all columns (structure) and data inside the table.

$tables = mysqli_query($conn, "SHOW TABLES");
while ($table = mysqli_fetch_object($tables))
{
    $table_name = $table->{"Tables_in_classicmodels"};

    // put below code here
}

Now we need to get all the rows and columns of this table. Since we do not know the number of each table so we will create an array that will store all columns of each table with LIKE clause on each column.

<caption>

We can display column name as caption of each table using <caption> tag. Then we will run the query to get all columns of each table and loop through each column and display that as TH (table heading). After that, we will need the $columns array again when display row data. So we will use the function mysqli_data_seek($columns, 0) and it will move the database cursor of this object to zero.

We also will push the column name in an array with LIKE clause so it will become “WHERE column1 LIKE ‘%value%’ OR column2 LIKE ‘%value%’ “ and so on. We can combine this array into string using implode function and append in $sql variable.

Finally we will run this query and loop through each row returned from it. Inside the row array we also need to run the column array to get all columns of that table. Make sure to call mysqli_data_seek function otherwise you won’t be able to see more than 1 row.

<?php

// put this code inside above while loop

// Create SQL query to get all rows (more on this later)
$sql = "SELECT * FROM " . $table_name . " WHERE ";

// An array to store all columns LIKE clause
$fields = array();

// Query to get all columns from table
$columns = mysqli_query($conn, "SHOW COLUMNS FROM " . $table_name);

?>
	
<table>

	<!-- Display table name as caption -->
    <caption>
        <?php echo $table_name; ?>
    </caption>

    <!-- Display all columns in table header -->
    <tr>
        
		<?php
			// Loop through all columns
			while ($col = mysqli_fetch_object($columns)):

				// Use LIKE clause to search input in each column
				array_push($fields, $col->Field . " LIKE '%" . $search . "%'");

		?>

			<!-- Display column in TH tag -->
			<th><?php echo $col->Field; ?></th>

		<?php
			endwhile;

			// Move cursor of $columns to 0 so it can be used again
			mysqli_data_seek($columns, 0);
		?>
        
    </tr>

    <?php
    	// Combine $fields array by OR clause into one string
		$sql .= implode(" OR ", $fields);
		$result = mysqli_query($conn, $sql);

		// Loop through all rows returned from above query
		while ($row = mysqli_fetch_object($result)):
			?>

			<tr>

				<?php
					// Loop through all columns of this table
					while ($col = mysqli_fetch_object($columns)):
				?>

					<td>

						<?php
							// Display row value from column field
							echo $row->{$col->Field};
						?>

					</td>

				<?php endwhile; mysqli_data_seek($columns, 0); /* end of column while loop */ ?>

			</tr>

		<?php endwhile; /* end of row while loop */ ?>

</table>

So that’s it. Hope that helps you in your almost every project because in every website you will need to allow user to search some string in whole database. If you face any problem, please feel free to ask in the comment’s section below.

Learn how you can recursively search string in all folders in your cPanel’s file manager.

Recursively search string in all folders in cPanel – PHP

[wpdm_package id=’193′]

Recursively search string in all folders in cPanel – PHP

Suppose you are working on cPanel on a very big project, now you want to search some string in all folders and in all of their files in cPanel. There are very few cPanel that provide such functionality to search some specific string in all files in your domain. This script will allow you to do this.

You just needs to put this file in the root folder of your domain where you want to perform search. For the sake of simplicity we are creating a simple form where you will enter the string that needs to be searched and hit enter to do the search.

Creating a form to enter string

<form method="GET" action="search.php">
	<input name="search" placeholder="Enter query" autocomplete="off">

	<input type="submit" name="submit">
</form>

Perform search

Create a recursive function that will search for all files. While calling this function you need to send the root folder path in the first call. If you are using Laravel you need to use Laravel built-in base_path() instead of core PHP dirname(__FILE__). Create a new file named “search.php” and paste the following code in it:

search.php

<?php

	// A recursive function to search all files
	function get_directory_content($directory)
	{
		//
	}

	// Check if form submits
	if (isset($_GET["submit"]))
	{
		// Get searched query
		$search = $_GET["search"];

		// Where all files (where that string is found) will be stored
		$results = array();

		// Send root folder path to start the search
		get_directory_content(dirname(__FILE__));
	}

As we will be using $search and $results variable inside the function so we need to declare them as global variables, otherwise they will not be accessible inside the function. Also we need to get all the files inside the folder which is being passed as parameter, loop through all the files.

Skip “.” and “..”

In Unix file system there are 2 commands inside every folder which are “.” and “..”. The first one means the current directory and second one means the parent directory. In this case we will skip the loop by using continue; command.

global $search, $results;

$files = scandir($directory);
foreach ($files as $file)
{
	if ($file == "." || $file == "..")
	{
		continue;
	}
}

Next, we will check if the incoming name is a directory or a file. If it is a file then we will simply process it for searching text. If it is a directory then we will call the recursive function again to get its files too. In this way it will check for all files no matter how many nested folders you have.

$is_file = false;
$path = realpath($directory . DIRECTORY_SEPARATOR . $file);

if (is_dir($path))
{
	get_directory_content($path);
	$is_file = true;
}
else
{
	$is_file = true;
}

Then we will get the content of that file and check if that searched query exists in that file. stripos(content, search) will search case-insensitively which means that you do not have to worry about small and capital letters.

stdClass in PHP

If that text is found in file then we will create a custom object using built-in PHP stdClass() class. If you are working on Laravel then you need to push back-slash before the class name, for example for Laravel \stdClass() because Laravel classes works in namespaces.

Set the name of file in that object and lastly push that object in the $results array we created in second step.

if ($is_file)
{
	$content = file_get_contents($path);

	if (stripos($content, $search) !== false)
	{
		$obj = new stdClass();
		$obj->file_name = $file;

		array_push($results, $obj);
	}
}

Now we will know the name of file where your searched string is found. But we would also want to know the line number in case you have larger files. Also if you were searching for some variable then it will have multiple occurrences in same file so we need to create an array. Another variable will be created to tell the line number of each occurrence.

In order to check each line number we need to loop through each line of that file. To do that, first open the file in read mode using fopen(“file_name”, “r”) and this will return the file handler which we can use later.

fgets in PHP

fgets(file_handler, length) function will read one line each time it is called and by specifying length paramter it will move to the next line every time it is called. In each iteration, we are increasing the number of line by incrementing the variable with 1.

Then we will use the same function to search but this time for single line and not for whole file. If found then we will push that line number in $lines array.

After the while loop we will assign that lines array to our custom object we created in previous step. So lines array will also be sent along with file name which we did in previous step.

$lines = array();
$line_number = 0;

$file_handler = fopen($path, "r");
while ( ( $line = fgets($file_handler, 4096) ) !== false )
{
	$line_number++;

	if (stripos($line, $search) !== false)
	{
		array_push($lines, $line_number);
	}
}

$obj->lines = $lines;

The full recursive function is given below:

function get_directory_content($directory)
{
	global $search, $results;

	$files = scandir($directory);
	foreach ($files as $file)
	{
		if ($file == "." || $file == "..")
		{
			continue;
		}

		$is_file = false;
		$path = realpath($directory . DIRECTORY_SEPARATOR . $file);

		if (is_dir($path))
		{
			get_directory_content($path);
			$is_file = true;
		}
		else
		{
			$is_file = true;
		}

		if ($is_file)
		{
			$content = file_get_contents($path);

			if (stripos($content, $search) !== false)
			{
				// Laravel, \stdClass()
				$obj = new stdClass();
				$obj->file_name = $file;

				$lines = array();
				$line_number = 0;

				$file_handler = fopen($path, "r");
				while ( ( $line = fgets($file_handler, 4096) ) !== false )
				{
					$line_number++;

					if (stripos($line, $search) !== false)
					{
						array_push($lines, $line_number);
					}
				}

				$obj->lines = $lines;

				array_push($results, $obj);
			}
		}
	}
}

To display the data is quite simple, just call the function and loop through $results array.

<?php

get_directory_content(dirname(__FILE__));

foreach ($results as $result):
?>

	<h1><?php echo $result->file_name; ?></h1>

	<h2>Lines</h2>
	<ul>
		<?php foreach ($result->lines as $line) { ?>
		<li>
			<?php echo $line; ?>
		</li>
		<?php } ?>
	</ul>

<?php endforeach; ?>

Hope this tutorial helps you in search your query string in all folders in your cPanel. If it did not work, or if you are having any error in this, kindly do let us know.

Learn more

Search in all tables and columns – PHP & MySQL

[wpdm_package id=’191′]