Get user location in PHP

In this article, we will teach you, how you can get user location in PHP. We have a users table where we are saving the user’s location and the time when the last location was fetched.

CREATE TABLE users (
    id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(255) NOT NULL,
    email VARCHAR(255) NOT NULL UNIQUE,
    password VARCHAR(255) NOT NULL,
    location JSON NULL DEFAULT NULL,
    last_location_at TIMESTAMP NULL DEFAULT NULL
);

The most important columns are “location” and “last_location_at”. We will be fetching the user’s location every 24 hours. The following code will fetch the user location in PHP.

if (isset($_SERVER['HTTP_X_FORWARDED_FOR']))
{
    $client_ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
}
else if (isset($_SERVER['HTTP_X_REAL_IP']))
{
    $client_ip = $_SERVER['HTTP_X_REAL_IP'];
}
else
{
    $client_ip = $_SERVER['REMOTE_ADDR'];
}

$user_agent = $_SERVER['HTTP_USER_AGENT'];

$timestamp = strtotime($user->last_location_at);

$current_timestamp = time();

$difference = $current_timestamp - $timestamp;

$twenty_four_hours_in_seconds = 24 * 60 * 60;

if ($difference >= $twenty_four_hours_in_seconds)
{
    $curl = curl_init();
    curl_setopt_array($curl, [
        CURLOPT_URL => "http://www.geoplugin.net/json.gp?ip=" . $client_ip,
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_POST => true,
        CURLOPT_POSTFIELDS => json_encode([]),
        CURLOPT_HTTPHEADER => [
            "Content-Type: application/json"
        ]
    ]);
    $response = curl_exec($curl);

    if (curl_errno($curl))
    {
        $error = curl_error($curl);
    }
    else
    {
        $response = json_decode($response);
        if ($response->geoplugin_status == 200)
        {
            $location = [
                "city" => $response->geoplugin_city,
                "continent" => $response->geoplugin_continentName,
                "continentCode" => $response->geoplugin_continentCode,
                "country" => $response->geoplugin_countryName,
                "countryCode" => $response->geoplugin_countryCode,
                "currencyCode" => $response->geoplugin_currencyCode,
                "currencySymbol" => $response->geoplugin_currencySymbol,
                "currencyConverter" => $response->geoplugin_currencyConverter,
                "latitude" => $response->geoplugin_latitude,
                "longitude" => $response->geoplugin_longitude,
                "region" => $response->geoplugin_region,
                "ipAddress" => $response->geoplugin_request,
                "timezone" => $response->geoplugin_timezone,
                "user_agent" => $user_agent
            ];
            $location = json_encode($location);

            // UPDATE `users` SET location = '$location', last_location_at = NOW() WHERE id = 2
        }
    }
    curl_close($curl);
}

Explanation:

  • Line 01-12: We are fetching the user’s IP address. When a user accesses the web server through a proxy or load balancer, the “REMOTE_ADDR” does not provide the actual IP address, rather it provides the IP address of the load balancer. That is why we are checking if the “HTTP_X_FORWARDED_FOR” or “HTTP_X_REAL_IP” headers are present. These headers are present when the user accesses the web server via proxy or load balancer.
  • Line 14: Getting the user’s device and browser information. It will tell if the user is using Chrome, Firefox, Safari, or whatever the browser. It will also tell the operating system he is using, if it is Mac OS, Windows, or Linux.
  • Line 16: Here we are getting the timestamp since the last location was fetched of that user. You might be getting this value from the database. If it is null, then it will return the seconds since 1970, which is good in case of null.
  • Line 18: Get the current timestamp in seconds.
  • Line 20: Get the difference between 2 times.
  • Line 22: Calculate how many seconds are there in 24 hours.
  • Line 24: Check if the time has passed 24 hours.
  • After that, I am calling a GeoPlugin API and passing the client’s IP address to it. I am saving all the required fields returned from API in a variable $location and converting that to JSON string. Then you can save that $location string in your database. Make sure to update the last_location_at to the current timestamp. This will make sure that the next location will be fetched after 24 hours.

One more thing:

You can show the user location on Google Maps too. You can follow this guide.

This feature has been practically implemented in an open-source Laravel boilerplate.

How to fix Laravel 403 forbidden storage error

If you keep getting 403 forbidden error on your Laravel storage files and you want to know how to fix this, you have landed on the right site.

403 forbidden error is typically when you do not have enough permission to read a file and it happens mostly on live servers (on production).

I was working on a Laravel project that allows users to upload and share files. To manage all user’s files, I am creating a sub-folder for each user with his ID. For example, if the user ID is 2, a folder named “2” will be created inside the storage sub-folder “files”. So the complete path to that folder will be: “storage/app/public/files/2”. I got this error due to permission of sub-folder “2”. I managed to fix it after a few hours, so I thought I might share the solution with others and it might help someone.

Note: The project I was working on was the Multi-purpose platform.

If you are seeing this error on your localhost, try running the following command:

php artisan storage:link

The solution that works for me is to set the permission of the parent folder as soon as the file is uploaded.

$file_path = "folder_name/" . $file->getClientOriginalName(); // storage/app/public/folder_name/file.png
$file->storeAs("/public", $file_path);

// Get the full path to the folder
$full_path = storage_path('app/public/folder_name');

// Set permissions using PHP's chmod function
chmod($full_path, 0775);

This will ensure to give the read, write, and execute permission to the folder, not the files inside it. If you give the execute permission to the file, then a hacker can put a malicious file in it and execute it.

You might need to delete the folder “folder_name” and try again, it should work fine now. So that’s how you can fix the 403 forbidden error on your Laravel storage files. If you face any problem in following this, kindly do let me know.

Get duration of video in PHP

If you are developing an application in PHP or any of its frameworks (e.g. Laravel) where you are allowing users to upload videos, and you want to get the duration of the video then this article is for you.

Getting the duration of user uploaded video has many benefits. One of them is you can limit the video duration. For example, you might have seen in TikTok that it allows videos of 15 or seconds only (for free users). To upload videos of longer length, you need to get their premium membership.

First, you need to install a library called getID3. You can install it by running the following command in your terminal:

composer require james-heinrich/getid3

I have a file saved at the following path:

http://127.0.0.1:8000/storage/video.mp4

The following PHP code will get the duration of the above video:

$remote_filename = "storage/" . $file_path;

if ($fp_remote = fopen($remote_filename, "rb"))
{
    $local_temp_filename = tempnam("/tmp", "getID3");

    if ($fp_local = fopen($local_temp_filename, "wb"))
    {
        while ($buffer = fread($fp_remote, 8192))
        {
            fwrite($fp_local, $buffer);
        }
        fclose($fp_local);

        $get_id3 = new \getID3;
        $file_info = $get_id3->analyze($local_temp_filename);

        $seconds = $file_info["playtime_seconds"];
        $seconds = round($seconds);

        return $seconds;
    }
}

Explanation:

  1. Line #3 will open the uploaded file in read-only binary mode.
  2. Line #5 will create a temporary file name in the directory “/tmp” (you do not need to create this directory). And the prefix of that file name will be “getID3”.
  3. Line #7 will open the temporary file in write-only binary mode.
  4. Line #9 reads the whole remote file in chunks of 8 KB per chunk. The loop continues until there is no more data, meaning the video file is read completely.
  5. Line #11 will write the buffer from the remote file to the local temporary file.
  6. Line #15 creates a new instance for class getID3. This class handles the metadata information for multimedia files like videos etc.
  7. Line #16 extracts the metadata information from local temporary file.
  8. Line #18 will get the seconds from the $file_info array.
  9. Finally, line #19 will round the number to the nearest whole number.

If you want to know how to get the size of uploaded file in PHP, you can check our this tutorial. Let me know if that works for you.

Get uploaded file size in Laravel 11

If you are working in Laravel and want to get the size of uploaded file, you have come to the right place. Let’s say you are developing an application where it is required to see the total file size uploaded by user. In this case, you need to keep track of all the uploaded files.

You can either loop through all files in storage and get their sizes. Or you can get the file size during upload and save it in your database. Either way, this article will help you.

In this article, we will do the following:

  1. Create an HTML form to upload file
  2. Upload file and get uploaded file size
  3. Convert bytes to readable format

1. Create an HTML form to upload file

First, we will create an HTML form to upload the file.

<!-- resources/views/upload.blade.php -->

<form method="POST" enctype="multipart/form-data" action="{{ url('/upload') }}">
    {{ csrf_field() }}

    <input type="file" name="image" accept="image/*" required />
    <input type="submit" value="Upload" />
</form>

This will create an input-type file that will allow only images. It will be a required field, the form will not be submitted until a file is selected. And a submit button labeled as “Upload”, when clicked will submit the form.

This will also create a hidden CSRF field required for Laravel form submission.

One more thing you can do here is to allow the user to preview the image before uploading. Check this tutorial if you want to know how to do it.

2. Upload file and get uploaded file size

Next step is to upload the file to storage. For this, first, we need to create a route that will send the request to our controller.

// routes/web.php

use App\Http\Controllers\UserController;

Route::post("/upload", [UserController::class, "upload"]);

After that, we need to create that method in our controller.

// app/Http/Controllers/UserController.php

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class UserController extends Controller
{
    public function upload()
    {
        //
    }
}

Inside this method, we will upload the file and get its size.

if (request()->file("image"))
{
    $image = request()->file("image");

    $file_path = "images/" . $image->getClientOriginalName();
    $image->storeAs("/public", $file_path);

    // PHP way
    $file_size = filesize(storage_path("app/public/" . $file_path));
    
    // Laravel way
    $file_size = $file->getSize();
    
    return $file_size;
}

This will return the file size in bytes. You can convert the bytes into a human-readable format by following the next step.

3. Convert bytes to readable format

Create the following method in your controller.

private function bytes_to_size($bytes)
{
    $sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
    if ($bytes == 0) return '0 Byte';
    $i = floor(log($bytes, 1024));
    return round($bytes / pow(1024, $i), 2) . ' ' . $sizes[$i];
}

You can call this function in the following way:

$file_size = $this->bytes_to_size($file_size);

That’s it. That’s how you can get the uploaded file size in Laravel 11 and also can convert the bytes in human-readable format.