Implementing an Activity Log in Your Web App Using PHP

In this tutorial, we will explore the creation of an activity log feature in web applications using the PHP language. The objective is to offer students and those new to the PHP language a reference or guide to enhance their knowledge and capabilities in developing efficient and effective web applications. I will explain the step-by-step process of creating a reusable PHP Class that implements an Activity Log feature into a PHP project. Additionally, I will provide a complete source code zip file for a sample web application that demonstrates the usage of the mentioned PHP Class.

What are the benefits of implementing an Activity Log in Web Applications?

Implementing an Activity Log in web applications provides tailored advantages for the dynamic nature of online platforms. Careful consideration of the information to be logged is crucial, balancing the need for detailed records with concerns for data privacy and performance. When implemented effectively, an activity log can significantly contribute to the security, performance, and usability of web applications.

How to create a reusable Activity Logger Class in PHP?

Here are the steps for creating an Activity Logger Class in PHP:

Step 1: Creating the Class

Assuming that we have already created a new PHP file named `activity-log.class.php`, open the file in your preferred code editor, such as Sublime Text, MS VS Code, or Notepad++. Then, structure the PHP class as follows:

  1. <?php
  2. class ActivityLog{
  3.  
  4. }
  5. ?>

Step 2: Create a Database Connection

Next, we need to create the class method or function that allows us to connect to the Database. We can achieve this by adding the database connection in the `__construct` function of the class and closing it in the `__destruct` function. To do this, refer to the following snippet:

  1. <?php
  2. class ActivityLog{
  3.     // DB Connection object
  4.     private $db;  
  5.     function __construct($dbhost="", $dbuser = "", $dbpassword = "", $dbname = "", $db_prefix =""){
  6.         // Check if DB credentials is empty
  7.         if(empty($dbhost) || empty($dbuser) || empty($dbname)){
  8.             throw new ErrorException("Database Credentials cannot be empty!");
  9.             exit;
  10.         }
  11.  
  12.         // Connect to the database
  13.         try{
  14.             $this->db = new MySQLi($dbhost, $dbuser, $dbpassword, $dbname);
  15.         }catch(Exception $e){
  16.             throw new ErrorException($e->getMessage());
  17.             exit;
  18.         }
  19.        
  20.     }
  21.    function __destruct()
  22.     {
  23.         if($this->db){
  24.             $this->db->close();
  25.         }
  26.     }
  27. }
  28. ?>

Step 3: Create the Log Table in Database

Next, let's create the function that checks if the Activity Log's Table is already created; otherwise, the function will create a new database table for this. See the following snippet:

  1. <?php
  2. class ActivityLog{
  3.  
  4.     // DB Connection
  5.     private $db;    
  6.     // Log Table Name
  7.     private $dbTbl = "site_activity_log_automation_tbl";
  8.     // DB Prefix
  9.     private $db_prefix;
  10.  
  11.     function __construct($dbhost="", $dbuser = "", $dbpassword = "", $dbname = "", $db_prefix =""){
  12.         // Check if DB credentials is empty
  13.         if(empty($dbhost) || empty($dbuser) || empty($dbname)){
  14.             throw new ErrorException("Database Credentials cannot be empty!");
  15.             exit;
  16.         }
  17.         // Connect to the database
  18.         try{
  19.             $this->db = new MySQLi($dbhost, $dbuser, $dbpassword, $dbname);
  20.         }catch(Exception $e){
  21.             throw new ErrorException($e->getMessage());
  22.             exit;
  23.         }
  24.        
  25.         // SEt DB Prefix to log db table
  26.         $this->db_prefix = $db_prefix;
  27.         if(!empty($this->db_prefix)){
  28.             $this->db_prefix .= "_";
  29.         }
  30.         $this->dbTbl = $this->db_prefix . $this->dbTbl;
  31.  
  32.  
  33.         // Check if Log Table already exists, otherwise, create table
  34.         try{
  35.             $tbl_described =  $this->db->query("DESCRIBE `{$this->db_prefix}`");
  36.         }catch(Exception $e){
  37.             $this->create_tbl();
  38.         }
  39.        
  40.     }
  41.  
  42.     /**
  43.      * Log Table Creation
  44.      */
  45.     function create_tbl(){
  46.         $sql = "CREATE TABLE IF NOT EXISTS `{$this->dbTbl}`
  47.            (
  48.                `id` bigint(30) PRIMARY KEY AUTO_INCREMENT,
  49.                `user_id` bigint(30) NOT NULL,        
  50.                `ip` varchar(25) NOT NULL,        
  51.                `url` text NOT NULL,        
  52.                `action` text NOT NULL,        
  53.                `created_at` datetime NOT NULL DEFAULT current_timestamp()        
  54.            )ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci
  55.        ";
  56.         try{
  57.             $table_create = $this->db->query($sql);
  58.         }catch(Exception $e){
  59.             throw new ErrorException($e->getMessage());
  60.             exit;
  61.         }
  62.     }
  63.     function __destruct()
  64.     {
  65.         if($this->db){
  66.             $this->db->close();
  67.         }
  68.     }
  69. }
  70. ?>

The snippet above showcases the creation of the Activity Log Table if the table does not exist yet and when the class is called.

Step 4: Creating a Log Function

Next, let's create the function that inserts the log data into the database. To do that, we can write it as follows:

  1. <?php
  2. /**
  3. * Sanitize values
  4. * @param mixed any
  5. */
  6. function sanitize_value($value){
  7.     if(!is_numeric($value) && empty($value)){
  8.         if( is_object($value) && is_array($value) ){
  9.             $value = json_encode($value);
  10.         }else{
  11.             $value = addslashes(htmlspecialchars($value));
  12.         }
  13.     }
  14.     return $value;
  15. }
  16.  
  17. /**
  18.     * Insert Activity Log
  19.     * @param array [user_id, ip, url, action]
  20.     *  
  21.     */
  22. public function log( $data = [] ){
  23.     if(empty($data)){
  24.         throw new ErrorException("Log data are required!");
  25.         exit;
  26.     }
  27.  
  28.    
  29.     $params_values = [];
  30.     $params_format = [];
  31.     $query_values  = [];
  32.  
  33.     foreach($data as $k => $v){
  34.         $v = $this->sanitize_value($v);
  35.         if(!empty($v)){
  36.             if(is_numeric($v)){
  37.                 $fmt = "d";
  38.             }else{
  39.                 $fmt = "s";
  40.             }
  41.             $query_values[] = "`{$k}`";
  42.             $params_values[] = $v;
  43.             $params_format[] = $fmt;
  44.         }
  45.     }
  46.  
  47.     if(empty($query_values)){
  48.         throw new ErrorException("All Log data provided are empty or invalid!");
  49.         exit;
  50.     }
  51.  
  52.     $sql = "INSERT INTO `{$this->dbTbl}` (".implode(",", $query_values).") VALUES (".( implode( ",", str_split( str_repeat( "?", count( $query_values ) ) ) ) ).")";
  53.  
  54.     $stmt = $this->db->prepare($sql);
  55.  
  56.     $fmts = implode("", $params_format);
  57.  
  58.     $stmt->bind_param($fmts, ...$params_values);
  59.  
  60.     $executed = $stmt->execute();
  61.  
  62.     if($executed){
  63.  
  64.         $resp = [
  65.             "status" => "success"
  66.         ];
  67.  
  68.     }else{
  69.  
  70.         $resp = [
  71.             "status" => "error",
  72.             "sql" => $sql,
  73.             "queries" => $query_values,
  74.             "formats" => $fmts,
  75.             "values" => $params_values,
  76.         ];
  77.  
  78.     }
  79.     return $resp;
  80. }
  81. ?>

Here's the complete script of the reusable Activity Log class in PHP. I've also included a function that retrieves all the activity logs from the database.

  1. <?php
  2. /**
  3.  * Class Required Parameters
  4.  * @param str DB Host Name
  5.  * @param str DB User Name
  6.  * @param str DB Password
  7.  * @param str DB Name
  8.  * @param str DB Prefix
  9. */
  10. class ActivityLog{
  11.  
  12.     // DB Connection
  13.     private $db;    
  14.     // Log Table Name
  15.     private $dbTbl = "site_activity_log_automation_tbl";
  16.     // DB Prefix
  17.     private $db_prefix;
  18.  
  19.     function __construct($dbhost="", $dbuser = "", $dbpassword = "", $dbname = "", $db_prefix =""){
  20.         // Check if DB credentials is empty
  21.         if(empty($dbhost) || empty($dbuser) || empty($dbname)){
  22.             throw new ErrorException("Database Credentials cannot be empty!");
  23.             exit;
  24.         }
  25.         // Connect to the database
  26.         try{
  27.             $this->db = new MySQLi($dbhost, $dbuser, $dbpassword, $dbname);
  28.         }catch(Exception $e){
  29.             throw new ErrorException($e->getMessage());
  30.             exit;
  31.         }
  32.        
  33.         // SEt DB Prefix to log db table
  34.         $this->db_prefix = $db_prefix;
  35.         if(!empty($this->db_prefix)){
  36.             $this->db_prefix .= "_";
  37.         }
  38.         $this->dbTbl = $this->db_prefix . $this->dbTbl;
  39.  
  40.  
  41.         // Check if Log Table already exists, otherwise, create table
  42.         try{
  43.             $tbl_described =  $this->db->query("DESCRIBE `{$this->db_prefix}`");
  44.         }catch(Exception $e){
  45.             $this->create_tbl();
  46.         }
  47.        
  48.     }
  49.  
  50.     /**
  51.      * Log Table Creation
  52.      */
  53.     function create_tbl(){
  54.         $sql = "CREATE TABLE IF NOT EXISTS `{$this->dbTbl}`
  55.            (
  56.                `id` bigint(30) PRIMARY KEY AUTO_INCREMENT,
  57.                `user_id` bigint(30) NOT NULL,        
  58.                `ip` varchar(25) NOT NULL,        
  59.                `url` text NOT NULL,        
  60.                `action` text NOT NULL,        
  61.                `created_at` datetime NOT NULL DEFAULT current_timestamp()        
  62.            )ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci
  63.        ";
  64.         try{
  65.             $table_create = $this->db->query($sql);
  66.         }catch(Exception $e){
  67.             throw new ErrorException($e->getMessage());
  68.             exit;
  69.         }
  70.     }
  71.  
  72.     /**
  73.      * Sanitize values
  74.      * @param mixed any
  75.      */
  76.     function sanitize_value($value){
  77.         if(!is_numeric($value) && empty($value)){
  78.             if( is_object($value) && is_array($value) ){
  79.                 $value = json_encode($value);
  80.             }else{
  81.                 $value = addslashes(htmlspecialchars($value));
  82.             }
  83.         }
  84.         return $value;
  85.     }
  86.  
  87.     /**
  88.      * Insert Activity Log
  89.      * @param array [user_id, ip, url, action]
  90.      *  
  91.      */
  92.     public function log( $data = [] ){
  93.         if(empty($data)){
  94.             throw new ErrorException("Log data are required!");
  95.             exit;
  96.         }
  97.  
  98.        
  99.         $params_values = [];
  100.         $params_format = [];
  101.         $query_values  = [];
  102.  
  103.         foreach($data as $k => $v){
  104.             $v = $this->sanitize_value($v);
  105.             if(!empty($v)){
  106.                 if(is_numeric($v)){
  107.                     $fmt = "d";
  108.                 }else{
  109.                     $fmt = "s";
  110.                 }
  111.                 $query_values[] = "`{$k}`";
  112.                 $params_values[] = $v;
  113.                 $params_format[] = $fmt;
  114.             }
  115.         }
  116.  
  117.         if(empty($query_values)){
  118.             throw new ErrorException("All Log data provided are empty or invalid!");
  119.             exit;
  120.         }
  121.  
  122.         $sql = "INSERT INTO `{$this->dbTbl}` (".implode(",", $query_values).") VALUES (".( implode( ",", str_split( str_repeat( "?", count( $query_values ) ) ) ) ).")";
  123.  
  124.         $stmt = $this->db->prepare($sql);
  125.  
  126.         $fmts = implode("", $params_format);
  127.  
  128.         $stmt->bind_param($fmts, ...$params_values);
  129.  
  130.         $executed = $stmt->execute();
  131.  
  132.         if($executed){
  133.  
  134.            $resp = [
  135.                 "status" => "success"
  136.            ];
  137.  
  138.         }else{
  139.  
  140.             $resp = [
  141.                 "status" => "error",
  142.                 "sql" => $sql,
  143.                 "queries" => $query_values,
  144.                 "formats" => $fmts,
  145.                 "values" => $params_values,
  146.            ];
  147.  
  148.         }
  149.         return $resp;
  150.     }
  151.  
  152.     /**
  153.      * Log Data
  154.      * @param $user_id mixed|int User ID
  155.      * @param $action  str       Action Data
  156.      */
  157.     public function setAction($user_id= "", $action = ""){
  158.         $data = [];
  159.  
  160.         extract($_SERVER);
  161.         $data['ip'] = $REMOTE_ADDR;
  162.         $data['url'] = (empty($HTTPS) ? 'http' : 'https') . "://{$HTTP_HOST}{$REQUEST_URI}";
  163.         $data["user_id"] = $user_id;
  164.         $data["action"] = addslashes(htmlspecialchars($action));
  165.  
  166.         return $this->log($data);
  167.     }
  168.  
  169.     /**
  170.      * Get All Logs
  171.      */
  172.     public function getLogs(){
  173.  
  174.         $query = $this->db->query("SELECT * FROM `{$this->dbTbl}` order by `id` desc");
  175.         $result = $query->fetch_all(MYSQLI_ASSOC);
  176.  
  177.         return $result;
  178.     }
  179.  
  180.     function __destruct()
  181.     {
  182.         if($this->db){
  183.             $this->db->close();
  184.         }
  185.     }
  186. }
  187. ?>

Using the PHP Class provided above, we can now log the actions or any activity of the users in the website by simply implementing the script like the following:

  1. <?php
  2. $dbData = [
  3.     "localhost", // Hostname
  4.     "root",      // Username
  5.     "",          // Password
  6.     "dummy_db"  // DBName
  7. ];
  8. $activityLog = new ActivityLog(...$dbData);
  9. $activityLog->setAction($_SESSION['user_id'], "accessed the home page");
  10. ?>

To gain a better understanding of the usage of the PHP Activity Logger Class provided above, I've developed a simple web application with CRUD (Create, Read, Update, and Delete) functionalities. In this web application, user activities are logged in the database, such as successful logins, adding new data, updating data, and deleting data. The complete source code zip file of the web application I created is available and is free to download on this website. The download button is located below this tutorial content.

Snapshots

Explore the snapshots below, showcasing the Sample Web Application I created:

Customer List

Implementing an Activity Log in PHP

Customer Form Modal

Implementing an Activity Log in PHP

Activity Logs

Implementing an Activity Log in PHP

And there you have it! I trust this Creation Activity Logger PHP Class Tutorial will be beneficial for your needs and prove valuable for your current and future PHP projects. Delve deeper into this website for additional Tutorials, Free Source Codes, and Articles covering various programming languages.

You may also find interest in exploring the following:

Happy Coding =)

Add new comment