Showing posts with label php. Show all posts
Showing posts with label php. Show all posts

Saturday, February 22, 2020

PHP is not recognized as an internal or external command in command prompt

Need to add C:\xampp\php to your PATH environment variable.

Then close your command prompt and restart again.

It is very important because if you do not restart your command prompt then changes will not be reflected.
Go to My Computer->properties -> Advanced system setting

Now click on Environment Variables

Add ;C:\xampp\php in path variable value

Now restart command prompt DONE!

Note: Make sure you run CMD via run as administrator

Sunday, February 16, 2020

PHP - Encryption and Decryption of Large Files with OpenSSL

PHP lacks a build-in function to encrypt and decrypt large files. openssl_encrypt can be used to encrypt strings, but loading a huge file into memory is a bad idea.

This example uses the symmetric AES-256-CBC algorithm to encrypt smaller chunks of a large file and writes them into another file.
<?php
define('FILE_ENCRYPTION_BLOCKS', 10000);
/**
 * Encrypt the passed file and saves the result in a new file with ".enc" as suffix.
 *
 * @param string $source Path to file that should be encrypted
 * @param string $key The key used for the encryption
 * @param string $dest File name where the encryped file should be written to.
 * @return string|false  Returns the file name that has been created or FALSE if an error occured
 */
function encryptFile($source, $key, $dest)
{
    $key = substr(sha1($key, true), 0, 16);
    $iv = openssl_random_pseudo_bytes(16);

    $error = false;
    if ($fpOut = fopen($dest, 'w')) {
        // Put the initialzation vector to the beginning of the file
        fwrite($fpOut, $iv);
        if ($fpIn = fopen($source, 'rb')) {
            while (!feof($fpIn)) {
                $plaintext = fread($fpIn, 16 * FILE_ENCRYPTION_BLOCKS);
                $ciphertext = openssl_encrypt($plaintext, 'AES-256-CBC', $key, OPENSSL_RAW_DATA, $iv);
                // Use the first 16 bytes of the ciphertext as the next initialization vector
                $iv = substr($ciphertext, 0, 16);
                fwrite($fpOut, $ciphertext);
            }
            fclose($fpIn);
        }
        else {
            $error = true;
        }
        fclose($fpOut);
    }
    else {
        $error = true;
    }

    return $error ? null : $dest;
}

/**
 * Dencrypt the passed file and saves the result in a new file, removing the
 * last 4 characters from file name.
 *
 * @param string $source Path to file that should be decrypted
 * @param string $key The key used for the decryption (must be the same as for encryption)
 * @param string $dest File name where the decryped file should be written to.
 * @return string|false  Returns the file name that has been created or FALSE if an error occured
 */
function decryptFile($source, $key, $dest)
{
    $key = substr(sha1($key, true), 0, 16);

    $error = false;
    if ($fpOut = fopen($dest, 'w')) {
        if ($fpIn = fopen($source, 'rb')) {
            // Get the initialzation vector from the beginning of the file
            $iv = fread($fpIn, 16);
            while (!feof($fpIn)) {
                $ciphertext = fread($fpIn, 16 * (FILE_ENCRYPTION_BLOCKS + 1)); // we have to read one block more for decrypting than for encrypting
                $plaintext = openssl_decrypt($ciphertext, 'AES-256-CBC', $key, OPENSSL_RAW_DATA, $iv);
                // Use the first 16 bytes of the ciphertext as the next initialization vector
                $iv = substr($ciphertext, 0, 16);
                fwrite($fpOut, $plaintext);
            }
            fclose($fpIn);
        }
        else {
            $error = true;
        }
        fclose($fpOut);
    }
    else {
        $error = true;
    }

    return $error ? null : $dest;
}

$key = 'my secret key';

$fileName = __DIR__ . '/testfile.txt';
file_put_contents($fileName, 'File would be encrypted...');

$result = encryptFile($fileName, $key, $fileName . '.enc');
if ($result) {
    echo "FILE ENCRYPTED TO " . $result;

    $result = decryptFile($result, $key, $fileName . '.dec');
    if ($result) {
        echo "<BR>FILE DECRYPTED TO " . $result;
    }
}
?>

Saturday, August 11, 2018

Setting Up Recaptcha 2.0 with JavaScript and PHP

reCAPTCHA need to verify bots (automatic system) can't take advantage of your site. reCAPTCHA 2.0 is somewhat different than the old reCAPTCHA. In version 1.0 it required to type text but now in 2.0 it’s simply a click of a button as below image.

The term of verifying front end users is a bit different. A variable named "g-recaptcha-response" is sent through a POST request to your server-side script, and along with your reCAPTCHA secret key that you get when you sign up for your website, you need to pass that secret key along to Google to tell that the user is a bot or not to determine by system. The response from Google is a JSON response on success for failure both case, so that we will be using file_get_contents() and json_decode() to fetch and parse the response and decide that front user is valid or not.


The below is just a HTML form you should create to see captcha in your desired page. The div element with the class of "g-recaptcha" is the element where we want to place our Google captcha (reCAPTCHA). You need to replace data-sitekey with the key you get from google when you sign up for reCAPTCHA (https://www.google.com/recaptcha/intro/index.html).
<form method="post" enctype="multipart/form-data">
    <div class="g-recaptcha" data-sitekey="YOUR_KEY"></div>
    <button type="submit">CHECK RECAPTCHA</button>
</form>
But I will show another technique how to show reCAPTCHA using JavaScript. Below is a simple script to show how you can integrate reCAPTCHA using JavaScript. But server side validation using PHP. Use need to create an div with ID (suppose, you can create your div with other ID) captcha_container
<script src='https://www.google.com/recaptcha/api.js'></script>

var captchaContainer = grecaptcha.render('captcha_container', {
    'sitekey' : 'Your sitekey',
    'callback' : function(response) {
        console.log(response);
    }
});
Now time to validate reCAPTCHA from server side.
<?php
$url = 'https://www.google.com/recaptcha/api/siteverify';

$data = array(
    'secret' => 'YOUR_SECRET',
    'response' => $_POST["g-recaptcha-response"]
);

$options = array(
    'http' => array(
        'method' => 'POST',
        'content' => http_build_query($data)
    )
);

$context = stream_context_create($options);
$verify = file_get_contents($url, false, $context);
$captcha_success = json_decode($verify);

if ($captcha_success->success == false) {
    echo "<p>You are a bot! Go away!</p>";
}
else if ($captcha_success->success == true) {
    echo "<p>You are not not a bot!</p>";
}

Saturday, June 23, 2018

How to pass parameters / arguments to your PHP script via the command line

If you want to be able to assign variable names for the values being passed in like php script.php -pvalue1, getopt() is the way to do it. Lets look at a different version of the script now >
$val = getopt("p:");
There are some major differences here. First with getopt() you must specify which command line argument you want to retrieve. In the case of this script, it looks for the "-name" argument, that's specified by the "name:" value passed to getopt(). The colon (:) means that the parameter must have a value. If you're used to doing something like "script.php?name=Name&roll=Roll" this is an equivalent for the command line.
php script.php --name=test --roll="03 d"
$val = getopt("name:roll:");

Friday, June 22, 2018

Header only retrieval in php via curl | PHP CURL get content type from URL | PHP CURL retrieve headers information from URL

Response headers can contain valuable information and may help to keep your API responses simpler by separating the actual response data from accessory metadata.
For instance, when querying the API for a list of posts, the response body includes just the content but there are also some other valualbe information sent as headers:
The PHP code for the CURL request would look something like this:
$agent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36 X-Client-Data: CIa2yQEIpLbJAQjBtskBCKmdygEIqKPKARiSo8oB";
$curl = curl_init();
curl_setopt($curl, CURLOPT_URL, $url);
curl_setopt($curl, CURLOPT_FILETIME, true);
curl_setopt($curl, CURLOPT_NOBODY, true);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_HEADER, true);
curl_setopt($curl, CURLOPT_USERAGENT, $agent);
$header = curl_exec($curl);
$info = curl_getinfo($curl);
curl_close($curl);
Which will output as below:
HTTP/1.1 200 OK
Date: Fri, 22 Jun 2018 06:47:27 GMT
Server: Apache/2.4.33 (Win32) OpenSSL/1.0.2n PHP/5.6.35
Last-Modified: Sun, 11 Feb 2018 16:39:41 GMT
ETag: "5ae6f-564f2687e28f8"
Accept-Ranges: bytes
Content-Length: 372335
Content-Type: application/pdf

Array
(
    [url] => http://localhost/text-finder/download.pdf
    [content_type] => application/pdf
    [http_code] => 200
    [header_size] => 265
    [request_size] => 258
    [filetime] => 1518367181
    [ssl_verify_result] => 0
    [redirect_count] => 0
    [total_time] => 0.016
    [namelookup_time] => 0.016
    [connect_time] => 0.016
    [pretransfer_time] => 0.016
    [size_upload] => 0
    [size_download] => 0
    [speed_download] => 0
    [speed_upload] => 0
    [download_content_length] => 372335
    [upload_content_length] => -1
    [starttransfer_time] => 0.016
    [redirect_time] => 0
    [redirect_url] => 
    [primary_ip] => ::1
    [certinfo] => Array
        (
        )

    [primary_port] => 80
    [local_ip] => ::1
    [local_port] => 51877
)


HTTP/1.1 200 OK
Date: Fri, 22 Jun 2018 06:48:11 GMT
Server: Apache/2.4.33 (Win32) OpenSSL/1.0.2n PHP/5.6.35
Last-Modified: Fri, 22 Jun 2018 04:18:28 GMT
ETag: "92-56f3352eebe85"
Accept-Ranges: bytes
Content-Length: 146
Content-Type: text/html

Array
(
    [url] => http://localhost/text-finder/test2.html
    [content_type] => text/html
    [http_code] => 200
    [header_size] => 253
    [request_size] => 256
    [filetime] => 1529641108
    [ssl_verify_result] => 0
    [redirect_count] => 0
    [total_time] => 0.015
    [namelookup_time] => 1.0E-6
    [connect_time] => 0.015
    [pretransfer_time] => 0.015
    [size_upload] => 0
    [size_download] => 0
    [speed_download] => 0
    [speed_upload] => 0
    [download_content_length] => 146
    [upload_content_length] => -1
    [starttransfer_time] => 0.015
    [redirect_time] => 0
    [redirect_url] => 
    [primary_ip] => ::1
    [certinfo] => Array
        (
        )

    [primary_port] => 80
    [local_ip] => ::1
    [local_port] => 51944
)

Friday, April 20, 2018

Extending from Laravel 5 core - SqlServerConnection | Extending The Connection Class In Laravel

The first thing the ConnectionFactory::createConnection() method does is to check if the db.connection.{$driver} alias is bound, and if so, it returns that connection object. If it is not bound, it returns the base connection object (Illuminate\Database\MySqlServerConnection for the mysql driver)
Therefore, all you need to do to use your own custom connection is to bind the db.connection.mysql alias to your custom MySqlServerConnection class
You can create a new service provider in which to do this, or you can just add the line to your existing AppServiceProvider
<?php
namespace App\Providers;

use Illuminate\Contracts\Events\Dispatcher as DispatcherContract;
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    public function boot(DispatcherContract $events)
    {
        parent::boot($events);
    }

    public function register() {
        $this->app->bind('db.connection.mysql', \App\Database\MySqlConnection::class);
    }
}
Remember that there may be two methods in any ServiceProvider, first call "register" method of each ServiceProvider listed and after that "boot" method called each ServiceProvider listed in "config/app.php" in providers section.
<?php
return [
    'providers' => [
        App\Providers\EventServiceProvider::class,
        App\Providers\AppServiceProvider::class
    ]
];
You have to create a MySQL connection class \App\Database\MySqlConnection
<?php
namespace App\Database;

use Illuminate\Database\MySqlConnection as ParentMySqlConnection;

class MySqlConnection extends ParentMySqlConnection {
    public function select($query, $bindings = [], $useReadPdo = true) {
        return parent::select($query, $bindings, $useReadPdo);
    }
}
So its completed, MySqlConnection is now extended with our own connection class.
So you can now anything do with your custom connection class.

Thursday, April 19, 2018

How to get current time in milliseconds in PHP | Converting microtime() to milliseconds/seconds

The short answer is
$milliseconds = round(microtime(true) * 1000);
Use microtime(). This function returns a string separated by a space. The first part is the fractional part of seconds, the second part is the integral part. Pass in true to get as a number:
var_dump(microtime()); // string(21) "0.89115400 1283846202"
var_dump(microtime(true)); // float(1283846202.89)
64 bits platforms only!
function milliseconds() {
    $mt = explode(' ', microtime());
    return ((int)$mt[1]) * 1000 + ((int)round($mt[0] * 1000));
}

Wednesday, April 11, 2018

Using PHP's glob() function to find files in a directory | Filter files in directory | Regex filter files in directory

The examples below look at a directory with the following, the same example directory as used in the read through directory post
To find all the files in the directory /path/to/directory with a .txt file extension, you can do this:
$files = glob("/path/to/directory/*.txt");
Array
(
    [0] => /path/to/directory/bar.txt
    [1] => /path/to/directory/foo.txt
    [2] => /path/to/directory/link2foo.txt
)
There are flags which can be passed as a second optional parameter. One of these is GLOB_BRACE which means that e.g. {jpg,gif,png} will be expanded to match jpg, gif and png which can be useful if you need to look for a particular set of files by their extension, in this example for image files
$files = glob("/path/to/directory/*.{jpg,gif,png}", GLOB_BRACE);
Array
(
    [0] => /path/to/directory/1.jpg
    [1] => /path/to/directory/2.gif
    [2] => /path/to/directory/3.png
)

Can a PHP script execute common code before exit() | register_shutdown_function | PHP execute a piece of code before process terminated by exit() or die() method

Registers a callback to be executed after script execution finishes or exit() is called.
Multiple calls to register_shutdown_function() can be made, and each will be called in the same order as they were registered. If you call exit() within one registered shutdown function, processing will stop completely and no other registered shutdown functions will be called.
<?php
function shutdown()
{
    // This is our shutdown function, in 
    // here we can do any last operations
    // before the script is complete.

    echo 'Script executed with success', PHP_EOL;
}

register_shutdown_function('shutdown');
?>
<?php 
namespace App\Test;

class CallbackClass { 
    function CallbackFunction() { 
        // refers to $this 
    } 

    function StaticFunction() { 
        // doesn't refer to $this 
    } 
} 

function NonClassFunction() { 
} 
?> 

there appear to be 3 ways to set a callback function in PHP (using register_shutdown_function() as an example): 

1: register_shutdown_function('NonClassFunction'); 

2: register_shutdown_function(array('\App\Test\CallbackClass', 'StaticFunction')); 

3: $o =& new CallbackClass(); 
   register_shutdown_function(array($o, 'CallbackFunction')); 

Friday, April 6, 2018

How to Make Async Requests in PHP | Methods for synchronous processes in PHP | Making synchronous function call in PHP | Make sure same function will not execute parallel


<?php
function executeSync($key, $closure) {
    $sem = getSemGetCustom($key);
    if(getSemAcquireCustom($sem)) {
        $closure();

        getSemReleaseCustom($sem);
    }
    else {
        throw new \Exception("Processing");
    }
}

function getSemGetCustom($key) {
    return fopen(sys_get_temp_dir() . DIRECTORY_SEPARATOR . md5($key) . '.sem', 'w+');
}

function getSemAcquireCustom($sem) {
    return flock($sem, LOCK_EX);
}

function getSemReleaseCustom($sem) {
    return flock($sem, LOCK_UN);
}

function logText($text) {
    file_put_contents('data.txt', $text.PHP_EOL , FILE_APPEND | LOCK_EX);
}

$id = $_GET["id"];
$closure = function() use($id) {
    logText("STARTING-$id");
    sleep(1);
    logText("FINISHED-$id");
};

executeSync("KEY", $closure);
Or if you want to set timeout for lock
<?php
public static function lockAndExecute($key, Closure $closure, $timeout_seconds = 5) {
    $file = sys_get_temp_dir() . DIRECTORY_SEPARATOR . md5($key) . '.tmp';
    $handle = fopen($file, 'w+');
    while (!flock($handle, LOCK_EX | LOCK_NB)) {
        usleep(1000 * 249);
        $timeout_seconds = $timeout_seconds - 0.25;
        if ($timeout_seconds <= 0) {
            throw new Exception("Unable to acquire lock");
        }
    }
    try {
        return $closure();
    }
    finally {
        flock($handle, LOCK_UN);
        fclose($handle);
    }
}

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Custom Popup</title>
    <meta name="viewport" content="user-scalable=yes, initial-scale=1, maximum-scale=1,minimum-scale=1, width=device-width, height=device-height, target-densitydpi=device-dpi"/>
    <script src="//ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
    <script type="text/javascript">
        $(document).ready(function () {
            $.get("exec.php", { id: 1 });
            $.get("exec.php", { id: 2 });
            $.get("exec.php", { id: 3 });
            $.get("exec.php", { id: 4 });
            $.get("exec.php", { id: 5 });
            $.get("exec.php", { id: 6 });
            $.get("exec.php", { id: 7 });
            $.get("exec.php", { id: 8 });
            $.get("exec.php", { id: 9 });
            $.get("exec.php", { id: 10 });
        });
    </script>
</head>
<body>

</body>
</html>

You can see that function "executeSync" executing synchronously.

Saturday, January 27, 2018

How to create and download a csv file from php script | PHP : Array To CSV - Download CSV File | Creating downloadable CSV files using PHP

CSV (comma-separated values) is the most widely supported format for transferring tabular data between applications. The ability to export data in CSV format is a useful feature for many programs, and is becoming increasingly common in web applications. This page explains how to use PHP to create CSV files, and how to ensure that your visitor’s browser offers to download the file instead of displaying it.
<?php
header("Content-type:application/octet-stream");
header("Content-Disposition:attachment;filename='PHP Download as CSV.csv'");
$out = fopen('php://output', 'w');

# If you want to process static data
fputcsv($out, array("Name", "Roll"));
fputcsv($out, array("Pritom 1", "Roll 1", "Extra 1"));
fputcsv($out, array("Pritom 2", "Roll 2", "Extra 2"));

# Fetch data from MySQL
mysql_connect('localhost', 'root', '');
mysql_select_db('test');
$rows = mysql_query('SELECT * FROM t1');

// loop over the rows, outputting them
while ($row = mysql_fetch_assoc($rows)) fputcsv($out, $row);

fclose($out);
In a real application the database connection parameters should be defined as constants in a separate configuration file.

Thursday, December 28, 2017

PHP Round up to specific number | Round to nearest number | Round to next multiple of number



<?php
#Round to the next multiple of 5, exclude the current number
function roundNumberToUp($n, $x = 5)
{
    return round(($n + $x / 2) / $x) * $x;
}
roundNumberToUp(10);    #10
roundNumberToUp(10.2);  #15
roundNumberToUp(11.5);  #15

#Round to the nearest multiple of 5, include the current number
function roundNumberToNearest($n, $x = 5)
{
    return (round($n) % $x === 0) ? round($n) : round(($n + $x / 2) / $x) * $x;
}
roundNumberToNearest(10);   #10
roundNumberToNearest(10.3); #10
roundNumberToNearest(10.5); #15
roundNumberToNearest(11.7); #15

#Round up to an integer, then to the nearest multiple of 5
function roundNumberToNearestAsInteger($n, $x = 5)
{
    return (ceil($n) % $x === 0) ? ceil($n) : round(($n + $x / 2) / $x) * $x;
}
roundNumberToNearestAsInteger(10.3);    #15
roundNumberToNearestAsInteger(11.7);    #15



Friday, December 22, 2017

Get code line and file name that's executing processing the current function in PHP


function log() {
    try {
        $bt = debug_backtrace();
        $fileAndLine = "";
        for ($i = 0; $i < 10; $i++) {
            $row = $bt[$i];
            if (isset($row["file"]) && isset($row["line"])) {
                $fileAndLine = $row["file"] . "::" . $row["line"];
                $i = 50;
            }
        }
        return $fileAndLine;
    }
    catch (Exception $ex) {
        return "";
    }
}


Thursday, December 7, 2017

PHP Call Static Function Method Dynamically Based on __callStatic Method



<?php
abstract class ParentClass {
    public static function __callStatic($method, $args) {
        echo "Method=".$method.",Args=".json_encode($args)."\r\n<BR/>";
    }
}

class ChildClass extends ParentClass {
    public static function x1() {
        $args = func_get_args();
        call_user_func_array(
            array(parent::class, __FUNCTION__), $args
        );
    }
}

ChildClass::x1("x1", 10, 20);
ChildClass::x2("x2", 30, 40);


And output would be like:


Method=x1,Args=["x1",10,20] 
Method=x2,Args=["x2",30,40] 

Saturday, July 22, 2017

PHP Session: Use Multiple Session Same Request | Duplicate Session | Use Value Of Another Session | Value From Another Session

PHP Session: Use Multiple Session Same Request | Duplicate Session | Use Value Of Another Session | Value From Another Session.


<?php
$path = ini_get('session.save_path');
session_start();
$id = session_id();
$_SESSION["name"] = isset($_GET["name"]) ? $_GET["name"] : "No Name";
$_SESSION["tokens"] = array();
echo "<pre>";
echo "Session Save Path: " . $path . ",Session_id=$id\r\n\r\n";
willThisWork();
echo "\r\nORIGINAL_SESSION\r\n";
print_r($_SESSION);

function willThisWork() {
    $existing = session_id();
    session_write_close();

    ob_start();
    session_id("MyCommonSessionInstance");
    session_start();
    if (!isset($_SESSION["tokens"])) {
        $_SESSION["tokens"] = array();
    }
    for ($i = 0; $i < 10000; $i++) {
        array_push($_SESSION["tokens"], md5(time().rand(9999,999999)));
    }
    $start = microtime(true);
    if (count($_SESSION["tokens"]) > 100000) {
        $_SESSION["tokens"] = array_slice($_SESSION["tokens"], 75000);
    }
    $in_array = count($_SESSION["tokens"]).",EXISTS=" . (in_array($_SESSION["tokens"][count($_SESSION["tokens"]) - 1], $_SESSION["tokens"]));
    session_write_close();
    ob_get_clean();
    echo("TIME_TAKE=" . ((microtime(true) - $start) / 1000))." ms\r\n";


    session_id($existing);
    session_start();
    echo "COUNT=$in_array\r\n";
}
echo "</pre>";

And output is below:

Session Save Path: C:\xampp\tmp,Session_id=crolfji99hva2o0dflkg4ddj93

TIME_TAKE=1.2001037597656E-5 ms
COUNT=65000,EXISTS=1

ORIGINAL_SESSION
Array
(
    [name] => No Name
    [tokens] => Array
        (
        )

)

Thursday, June 29, 2017

How create json format with group-concat mysql | How to put JSON into a column data if sub-query returns more than 1 row in MySQL | Optimized way of getting subqueries at once using JSON

How create json format with group-concat mysql |  How to put JSON into a column data if sub-query returns more than 1 row in MySQL | Optimized way of getting subqueries at once using JSON.

A bit convoluted, but you could create JSON objects for each row, concatenate them using GROUP_CONCAT and cast the result (wrapped in [] to make it an array) to JSON;

Below is the full example:


<?php
define("DB_DEFAULT_HOST", "localhost");
define("DB_DEFAULT_DATABASE", "test71");
define("DB_DEFAULT_USER", "root");
define("DB_DEFAULT_PASSWORD", "");

$dbh = null;
try {
    $dbh = new PDO(
        "mysql:host=" . DB_DEFAULT_HOST . ";dbname=" . DB_DEFAULT_DATABASE,
        DB_DEFAULT_USER,
        DB_DEFAULT_PASSWORD
    );
    $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    $dbh->beginTransaction();

    $query =
        "SELECT
            s.name AS Name, s.roll AS Roll,
            CONCAT(
              '[', COALESCE(
                GROUP_CONCAT(
                  CONCAT('{', 
                    '\"ExamName\":', '\"', HEX(CONVERT(e.subject USING utf32)), '\"',
                    ',\"Score\":', '\"', e.score, '\"',
                  '}')
                  SEPARATOR ','
                ), ''
              ), ']'
            ) AS ExamsRaw
        
        FROM student AS s
          LEFT JOIN exams AS e ON e.student_id=s.id
        
        GROUP BY s.id";

    $students = $dbh->query($query)->fetchAll(PDO::FETCH_OBJ);
    foreach ($students as $entity) {
        $entity->Exams = array();
        foreach (json_decode($entity->ExamsRaw) as $group) {
            $group->ExamName = hexToStr($group->ExamName);
            array_push($entity->Exams, $group);
        }
        unset($entity->ExamsRaw);
    }
    echo "<pre>";
    print_r($students);
    echo "</pre>";
}
catch (\Exception $ex) {
    echo "MySQL_Error=" . $ex->getMessage();
    $dbh->rollBack();
}

function hexToStr($hex)
{
    $string = '';
    for ($index = 0; $index < strlen($hex) - 1; $index += 2) {
        $string .= chr(hexdec($hex[$index] . $hex[$index + 1]));
    }
    return $string;
}

And below is the output of above example code block:


Array
(
    [0] => stdClass Object
        (
            [Name] => Prtiom First Year
            [Roll] => P1
            [Exams] => Array
                (
                    [0] => stdClass Object
                        (
                            [ExamName] => ©|æ|þ|½|±|®|¶
                            [Score] => 1.12
                        )

                    [1] => stdClass Object
                        (
                            [ExamName] => Subject 2   
                            [Score] => 2.54
                        )

                    [2] => stdClass Object
                        (
                            [ExamName] =>  ¡¢£¤¥¦§¨©
                            [Score] => 1.98
                        )

                )

        )

    [1] => stdClass Object
        (
            [Name] => Pritom Second Year
            [Roll] => P2
            [Exams] => Array
                (
                    [0] => stdClass Object
                        (
                            [ExamName] => Subject 2
                            [Score] => 4.00
                        )

                    [1] => stdClass Object
                        (
                            [ExamName] => Subject 3
                            [Score] => 3.00
                        )

                )

        )

)

Aware that mysql group function max length is by default 1024 characters. If your group function more than 1024 then you have to change limit of mysql group function using following query:

This is for current session:
SET SESSION group_concat_max_len = 1000000;

But if you want it global scope, then do following:
SET GLOBAL group_concat_max_len = 1000000;