How to improve security and structure in my PHP search query using MySQLi? [duplicate]

I’m building a simple PHP page that connects to a MySQL database and displays search results from a teacher table. The search checks if the input appears in the name, email, or subject columns. Here’s my current code:

<?php
$servername = "localhost";
$username = "root";
$password = "";
$dbname = "school";

$mysqli = new mysqli($servername, $username, $password, $dbname);
if ($mysqli->connect_error) {
  die("Connection failed: " . $mysqli->connect_error);
}

$search = isset($_GET['search']) ? trim($_GET['search']) : '';

if ($search === '') {
   $sql = "SELECT * FROM teacher";
} else {
   $like = "%" . $mysqli->real_escape_string($search) . "%";
   $sql = "SELECT * FROM teacher WHERE name LIKE '$like' OR email LIKE '$like' OR subject LIKE '$like'";
}

$result = $mysqli->query($sql);
?>

<!DOCTYPE html>
<html>
<head>
   <title>Search Results</title>
</head>
<body>

<?php
if ($result->num_rows > 0) {
   echo "<h1>Search Results</h1>";
   echo "<table>";
   echo "<tr><th>ID</th><th>Name</th><th>Email</th><th>Subject</th></tr>";
   while($row = $result->fetch_assoc()) {
       echo "<tr>";
       echo "<td>" . $row["teacher_id"] . "</td>";
       echo "<td>" . $row["name"] . "</td>";
       echo "<td>" . $row["email"] . "</td>";
       echo "<td>" . $row["subject"] . "</td>";
       echo "</tr>";
    }
    echo "</table>";
} else {
    echo "<h1>No results found</h1>";
}
$mysqli->close();
?>
<a href="xxx_searchform.html">back</a>

</body>
</html>

The code works fine, but I have a few questions:

Is this query safe from SQL injection, or should I use prepared statements even though I’m using real_escape_string?

Is there a better way to structure the SQL logic when the search input is empty versus when it’s not?

Are there any performance or security issues I should be aware of when searching with LIKE across multiple fields like this?

Any improvement suggestions or best practices are appreciated. Thanks!

Google Custom Search API returns 0 results for an indexed URL

I’m using the Google Custom Search JSON API to check whether a specific URL is indexed. However, even though I can find the page in Google manually, the API returns totalResults: 0

Here’s the URL I’m checking:
https://diario24horas.com/madrid/consigue-un-cabello-liso-y-saludable-con-un-alisado-permanente-organico-en-dim-salon/

And this is the request I’m sending:
GET https://www.googleapis.com/customsearch/v1?q=site:https://diario24horas.com/madrid/consigue-un-cabello-liso-y-saludable-con-un-alisado-permanente-organico-en-dim-salon/&key=API_KEY&cx=MY_cx

I get this response:
{
“searchInformation”: {
“totalResults”: “0”
}
}

a solution for my problem

How to serve React on root URL and Laravel Blade on /admin routes (e.g. /admin/login) on Hostinger?

I have a project with both a React frontend and a Laravel backend. I want to achieve the following routing logic:

When a user visits www.example.com, it should load the React frontend (like an SPA).

When the user visits www.example.com/admin/login, it should route to Laravel’s Blade template for admin login.

I’m hosting the project on Hostinger (shared hosting) and need help setting up this structure correctly.

// Catch-all route for React frontend
Route::get('/', function () {
    return file_get_contents(public_path('react-app/public/index.html'));
});

// Catch React frontend routes (e.g., /about, /contact, etc.)
Route::get('/{any}', function () {
    return file_get_contents(public_path('react-app/public/index.html'));
})->where('any', '^(?!admin).*');

I write like that but I want to know that is this good approach?
I mean can it create problems in future and can it create problems to frontend?

Getting a 403 error from nginx when trying to build an nginx and php-fpm dev environment

I’m trying to get a simple nginx with php project up and running under Docker, but I’m getting a 403 error now that I’ve modified my nginx configuration to point at the php server.

My docker-compose.yaml is as follows:

version: '3.8'

# Services
services:

  # Nginx Service
  nginx:
    image: nginx:latest
    ports:
      - 8880:80
    volumes:
      - ./src:/var/www/php
      - ./nginx/conf.d:/etc/nginx/conf.d
    depends_on:
      - php

  # PHP Service
  php:
    image: php:8.1-fpm
    working_dir: /var/www/php
    volumes:
      - ./src:/var/www/php

I have a php.conf file under my ./nginx/conf.d directory as follows:

server {
    listen 80 default_server;
    listen [::]:80 default_server;
    root   /var/www/php;
    index  index.php;

    location ~* .php$ {
        fastcgi_pass   php:9000;
        include        fastcgi_params;
        fastcgi_param  SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param  SCRIPT_NAME     $fastcgi_script_name;
    }
}

and under my ./src directory I have a very simple index.php that should call phpinfo() as follows:

<?php
phpinfo():
?>

nginx itself was working fine with the default configuration and the container would serve the “Welcome to nginx!” page no problem. But with the above config to point to the php-fpm container it breaks.

The error is coming from the nginx container according to the logs:

192.168.1.162 - - [10/May/2025:23:24:02 +0000] "GET / HTTP/1.1" 403 555 "-" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36" "-"
2025/05/10 23:24:02 [error] 21#21: *7 "/var/www/php/index.php" is forbidden (13: Permission denied), client: 192.168.1.162, server: , request: "GET / HTTP/1.1", host: "192.168.1.99:8880"

I have connected to both containers and have checked that index.php is both visible and readable (I can cat the file no problem from inside the container), I’ve even added other read and execute permissions to the index.php file but that makes no difference.

I guess something simple must be wrong with the php.conf as I don’t see anything in the php container’s log to say that it has received any forwarded request from the nginx container, but I am getting nowhere trying to figure out what is wrong.

Docker is running on a UGreen NAS, but this seems more fundamental than a host problem. I think I am just missing something in the setup.

Memory overflow when saving an Excel document?

I’m solving a problem such as saving big data to an Excel document. There is a console command that uploads data from the database to an xlsx document.

The code performs the following actions:

  1. Allocates memory to the process.
  2. Creates a Spreadsheet object for the header and fills the spreadsheet with headers.
  3. Next, in the loop, it creates temporary Spreadsheet1 and in batches of 500 records, we pull it from the database and write it to the same document.
  4. When a new iteration of the while loop begins, we destroy Spreadsheet1.
  5. At the end and at intermediate stages, we save the document.
    As a result, an error occurs:

Worksheet!D301 -> Unable to access External Workbook

at the stage of loading the second data packet. Here is the code:

ini_set('memory_limit', '512M');
...
// Create new object - Spreadsheet
$spreadsheet = new Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();

 $count_data = DB::table($table_name)->count();

            $data = DB::table($table_name)->get();
            //skip($pos)->take(1000)->

            $headers = [];
            if (count($data) > 0) {
                // header  
              $this->info('Create header');
                foreach ($data as $itm) {

                    $Arr = json_decode($itm->data_json);

                    if (is_object($Arr))
                        // Add keys in array of headers
                        foreach ($Arr as $key => $value) {
                            if (!in_array($key, $headers)) {
                                $headers[] = $key;
                            }
                        }
                }

                $rowIndex = 1;
                $columnIndex = 1; // Start with first column 
                foreach ($headers as $colIndex => $header) {
                    //$sheet->setCellValueByColumnAndRow($columnIndex++, 1, $header);
                    $colLetter = Coordinate::stringFromColumnIndex($colIndex + 1); 
                   // create Alfabet string
                    $cell = $colLetter . $rowIndex;
                    $sheet->setCellValue($cell, $header);
                }

                $this->info('Header created...');
                $this->info('Rec data...');

                try {
                    $writer = new Xlsx($spreadsheet);
                    $writer->save(base_path('public') . '/' . $document);
                } catch (PhpOfficePhpSpreadsheetWriterException $e) {
                    $this->error('Error of save document: ' . $e->getMessage());
                }

$rowIndex = 2; // Begin with second record
$pos = 0;    
while ($rowIndex < $count_data) {

   $dump = DB::table($table_name)->skip($pos)->take(500)->get();

if ($dump->count()) {
$spreadsheet1 = PhpOfficePhpSpreadsheetIOFactory::load(base_path('public').'/'.$document);
//$spreadsheet1 = new Spreadsheet();
$sheet1 = $spreadsheet1->getActiveSheet();

foreach ($dump as $item) {
   $Arr = json_decode($item->data_json);
       foreach ($headers as $colIndex => $header) {
           $colLetter = Coordinate::stringFromColumnIndex($colIndex + 1);
           $cell = $colLetter . $rowIndex;
           $sheet1->setCellValue($cell, $Arr->$header ?? '');
       }
       $rowIndex++;
}

$this->info('Save document ' . base_path('public') . '/' .$document);
$this->info('Memory Size: ' . memory_get_usage() . ' bite');

try {
      $writer = new Xlsx($spreadsheet1);
      $writer->save(base_path('public') . '/'.$document);
      sleep(5);
      unset($spreadsheet1);
      unset($sheet1);
} catch (PhpOfficePhpSpreadsheetWriterException $e) {
      $this->error('Error of save document: ' . $e->getMessage());
    }
}

How to solve the problem?

Read the id attribute of a tag in SVG file with getElementById using PHP

I have an SVG file named rectangle.svg (UPDATED)

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" 
  "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="300" height="130" xmlns="http://www.w3.org/2000/svg">
  <rect id="myid" width="200" height="100" x="10" y="10" rx="20" ry="20" fill="blue" />
  <rect id="myid2" width="200" height="100" x="10" y="10" rx="20" ry="20" fill="blue" />
</svg>

I try to read rect id with the readid.php (UPDATED):

<?php
    $dom = new DOMDocument("1.0", "utf-8");
    $dom->load('rectangle.svg');
    if ($dom->validate()) {
        echo "This document is valid!n";
    }
    $div = $dom->getElementById('myid');
    echo 'Result: '.$div->getAttribute('id');
?>

Output of php readid.php is:

This document is valid!
Result:

Why does not read the id attribute?

I would like to read the attributes and values of

<rect id="myid" width="200" height="100" x="10" y="10" rx="20" ry="20" fill="blue" />

how to make a WordPress plugin that alters the http status of 404 pages to 200 and uses the current theme to display 404 pages output

I am working on a plugin that changes the response for 404 pages so it issues HTTP status 200 instead of 404.

I already have the status change working using code like this:

function HTTP404Template()
{
    return __DIR__.'/404_template.php';
}
    
add_filter( '404_template', 'HTTP404Template');

Then the 404_template.php has code like this:

<?php
    status_header('200');
    add_filter( 'wp_robots', 'wp_robots_no_robots' );
    get_header();
    get_footer();
?>

Although this works, it shows the current theme template defined by another plugin, but it does not show the 404 page of that theme?

How can I change the output of a 404 to make the HTTP status be 200 and still show the current theme 404 page?

How to make a custom WooCommerce product type behave like Variable Product with full variation UI?

I’m creating a custom WooCommerce product type called bookable_service that should behave exactly like the native variable product type.

What I’ve implemented:

  • Created a class WC_Product_Variable_Bookable_Service extending WC_Product_Variable
  • Overrode get_type() to return 'variable' in admin, and 'bookable_service' on frontend
  • Registered the type using product_type_selector and woocommerce_product_class
  • Enabled variation support with woocommerce_product_type_supports
  • Injected 'product-type-variable' class into the <body> via admin JavaScript

Problem:

Despite this, the WooCommerce admin UI does not:

  • Show the “Used for variations” checkbox
  • Show the Variations tab in Product Data
  • Allow variation pricing to be added

If I switch the product type to 'variable', everything works fine. But with 'bookable_service', WooCommerce seems to ignore variation logic in the admin.

What I need:

I want WooCommerce to treat bookable_service exactly like a variable product — with full variation UI — but allow me to keep a custom product type for conditional frontend logic.

Is there a clean way to fully replicate Variable Product behavior for a custom product type without overriding WooCommerce templates or JS?

I expected that by extending WC_Product_Variable and returning 'variable' in get_type() during admin, WooCommerce would treat my custom type as variation-compatible. But it seems the admin UI logic is tied to hardcoded type strings.

Looking for a WooCommerce-native way to support full variation functionality in a custom product type.

Checking if a file descriptor is writable in PHP

I know there are already 3 standard streams: STDIN (0), STDOUT (1), and STDERR (2). I am testing the possibility of utilizing non-standard streams (> 2) in PHP.
My intention is to write to a non-standard FD (FD 3) if it is writable, otherwise fallback to an alternative (which defaults to STDERR). I have tried the following code, which is working perfectly fine:

<?php
//This is done so that notices regarding invalid FDs are thrown as errors
set_error_handler('errorHandler');
function errorHandler($severity, $message, $filename, $lineno) {
  if (error_reporting() == 0) return;
  if (error_reporting() & $severity) {
    throw new ErrorException($message, 0, $severity, $filename, $lineno);
  }
}

file_put_contents("php://fd/1", "Writing to STDOUT!n");
file_put_contents("php://fd/2", "Writing to STDERR!n");
write2Fd(3, "Writing to FD 3!n");

function write2Fd(int $fd, string $contents, int $alt = 2) {
    try {
        //Try writing to the given FD
        file_put_contents("php://fd/$fd", $contents);
    } catch(Exception $e) {
        //Fallback to alternative FD
        file_put_contents("php://fd/$alt", $contents);
    }
}
?>

I am running the program on a Linux (Arch) system in Bash shell with the following command:

php test.php 3>fd3out.txt

This output is shown in the terminal:

Writing to STDOUT!
Writing to STDERR!

And this text is found inside the fd3out.txt file:

Writing to FD 3!

The code is inspired from this answer, which achieves the same task in Python.

Using try-catch seems to be a hacky way to solve the problem of checking whether a stream is writable or not. I have also tried alternative solutions which involve using file_exists and is_writable:

function write2Fd(int $fd, string $contents, int $alt = 2) {
    $file = "php://fd/$fd";
    if (file_exists($file) && is_writable($file)) file_put_contents($file, $contents);
    else file_put_contents("php://fd/$alt", $contents);
}

But for some reason, both the checks return false even if FD 3 is created by the parent process.
What is the correct way to check if a stream is available and whether it is writable or not in PHP?

Update PHP 8.3.6 to 8.3.21 [closed]

How can I make Apache recognize a PHP version? I did the make install and when I run php -v, the version appears, but Apache still shows version 8.3.6.

Currently there is no way to do it by apt and I can’t add the ondrej/php repository, I searched in the PHP documentation, but it doesn’t say how to do this either.

Fatal error: Class ‘mysqli’ not found in OpenCart after PHP update? can advise? [duplicate]

Fatal error: Uncaught Error: Class ‘mysqli’ not found in /home/u963972343/domains/upgraded.co.in/public_html/system/library/db/mysqli.php:7 Stack trace: #0 /home/u963972343/domains/upgraded.co.in/public_html/storage/modification/system/library/db.php(35): DBMySQLi->__construct(‘localhost’, ‘u963972343_upgr…’, ‘Vansh@1401’, ‘u963972343_upgr…’, ‘3306’) #1 /home/u963972343/domains/upgraded.co.in/public_html/system/framework.php(80): DB->__construct(‘mysqli’, ‘localhost’, ‘u963972343_upgr…’, ‘Vansh@1401’, ‘u963972343_upgr…’, ‘3306’) #2 /home/u963972343/domains/upgraded.co.in/public_html/system/startup.php(104): require_once(‘/home/u96397234…’) #3 /home/u963972343/domains/upgraded.co.in/public_html/index.php(25): start(‘catalog’) #4 {main} thrown in /home/u963972343/domains/upgraded.co.in/public_html/system/library/db/mysqli.php on line 7″ my website www.upgraded.co.in built on opencart version3.0.3.2, i already tried (changing PHP version, clearing cache, restoring backup) but still not able to acess my admin panel nor my website. can anyone provide me the 100% woking fix for this tht can be done either through hostinger or suggest your way. thanks in advance.

Gedmo/Doctrine Tree Extension Cannot use object as array

I’ve created an entity with a closure table and a repository alongside. I’m trying a basic example of building a tree but am seeing the error Cannot use object of type AppEntityDiscussionComment as array when calling getCommentTreeForDiscussion

Entity:

#[GedmoTree(type: 'closure')]
#[GedmoTreeClosure(class: CommentClosure::class)]
#[ORMEntity(repositoryClass: CommentRepository::class)]
#[ORMHasLifecycleCallbacks]
class Comment implements Identifiable
{
    use IdentifiableEntity;
    use TimestampableEntity;

    #[ORMColumn(type: 'text')]
    private string $content;

    #[ORMManyToOne(targetEntity: User::class)]
    #[ORMJoinColumn(nullable: false)]
    private User $author;

    #[ORMManyToOne(targetEntity: Discussion::class, inversedBy: 'comments')]
    #[ORMJoinColumn(nullable: false)]
    private Discussion $discussion;

    #[GedmoTreeParent]
    #[ORMManyToOne(targetEntity: self::class, inversedBy: 'replies')]
    #[ORMJoinColumn(referencedColumnName: 'id', nullable: true, onDelete: 'CASCADE')]
    private ?Comment $parent = null;

    #[ORMOneToMany(targetEntity: self::class, mappedBy: 'parent', cascade: ['persist', 'remove'])]
    private Collection $replies;

    #[GedmoTreeLevel]
    #[ORMColumn(type: 'integer')]
    private int $level = 0;

    #[ORMManyToMany(targetEntity: User::class)]
    #[ORMJoinTable(name: 'comment_likes')]
    private Collection $likedBy;

    // --- Getters / Setters

Closure:

#[ORMEntity]
class CommentClosure extends AbstractClosure
{
    #[ORMManyToOne(targetEntity: Comment::class)]
    #[ORMJoinColumn(name: 'ancestor', referencedColumnName: 'id', nullable: false, onDelete: 'CASCADE')]
    protected $ancestor;

    #[ORMManyToOne(targetEntity: Comment::class)]
    #[ORMJoinColumn(name: 'descendant', referencedColumnName: 'id', nullable: false, onDelete: 'CASCADE')]
    protected $descendant;
}

Repository:

class CommentRepository extends ClosureTreeRepository
{
    public function __construct(EntityManagerInterface $manager)
    {
        parent::__construct($manager, $manager->getClassMetadata(Comment::class));
    }

    public function getCommentTreeForDiscussion(Discussion $discussion): array
    {
        $roots = $this->findBy(['discussion' => $discussion, 'parent' => null], ['createdAt' => 'ASC']);

        return $this->buildTree($roots);
    }
}