PHP 7.4.3环境下strpos/stristr等字符串检测函数服务器端失效问题排查及代码优化咨询
Let's break down exactly what's going wrong with your code on PHP 7.4, and walk through the fixes step by step:
1. The Root Cause: Uninitialized $result Variable
Your biggest issue is that $result isn't initialized before you start concatenating to it.
- In PHP 8+, uninitialized variables used in string operations are automatically treated as empty strings (
""), so your concatenation works as expected. - In PHP 7.4, an uninitialized
$resultisnull. When you passnulltostristr()orstrpos(), the function returnsfalse(and may trigger a warning depending on your error settings), which breaks your button display logic.
2. Secondary Issue: String Concatenation Risk for Multi-Digit IDs
Even if you fix the initialization, concatenating IDs into a string like "123" creates a hidden bug: if you have a privacy ID like 10, checking if the string contains "1" will incorrectly return true (since "10" has a "1" in it). This will lead to false positives for consent status.
3. Security Risk: SQL Injection in Your SELECT Query
You're directly concatenating $_SESSION['USER_ID'] into your SQL query, which is a critical security vulnerability. Always use prepared statements for database queries that include user input (even session data, since it can be manipulated in some edge cases).
Optimized & Fixed Code
Here's the revised version of your PHP logic that addresses all these issues:
Full Revised Code Snippet
<?php include "../functions/notLoggedIn.php"?> <!DOCTYPE html> <html lang="de"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <!-- Bootstrap --> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous"> <script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.10.2/dist/umd/popper.min.js" integrity="sha384-7+zCNj/IqJ95wo16oMtfsKbZ9ccEh31eOz1HGyDuCQ6wgnyJNSYdrPa03rtR1zdB" crossorigin="anonymous"> </script> <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.min.js" integrity="sha384-QJHtvGhmr9XOIpI6YVutG+2QOK9T+ZnN4kzFN1RtK3zEFEIsxhlmWl5/YESvpZ13" crossorigin="anonymous"> </script> <!-- Eigenes CSS --> <link rel="stylesheet" href="custom.css"> <!-- Titel der Webseite --> <title>Datenschutzeinwilligung</title> <!-- Favicon Icon --> <link rel="icon" href="./favicon.ico" type="image/x-icon" /> </head> <body> <!-- Navigationsleiste --> <?php include_once "nav_logedin.php"?> <!-- Seiteninhalt --> <div class="container x-height-app"> <h1 class="text-center mt-3">Datenschutzeinwilligung</h1> <?php include "mysql.php"; // Use prepared statement to fetch agreed privacy IDs (no SQL injection risk) // Only select the PRIVACY_ID we need, not full rows $stmt = $mysql->prepare(" SELECT privacy.PRIVACY_ID FROM privacy JOIN privacy_user ON privacy.PRIVACY_ID = privacy_user.PRIVACY_ID WHERE USER_ID = ? AND privacy_user.VALID_TILL IS NULL "); $stmt->execute([$_SESSION['USER_ID']]); // Fetch all IDs into an array (cleaner and safer than string concatenation) $agreedPrivacyIds = $stmt->fetchAll(PDO::FETCH_COLUMN, 0); // Handle consent update if(isset($_POST['update'])){ $PRIVACY = $_POST['privacy']; $USER_ID = $_SESSION['USER_ID']; $pdoQuery = "INSERT INTO privacy_user SET USER_ID=:USER_ID, PRIVACY_ID=:PRIVACY_ID, VALID_FROM=now()"; $pdoQuery_run = $mysql->prepare($pdoQuery); $pdoQuery_exec = $pdoQuery_run->execute(array(":USER_ID"=>$USER_ID, ":PRIVACY_ID"=>$PRIVACY)); // Use header redirect instead of meta refresh (avoids duplicate form submissions) header("Location: " . $_SERVER['PHP_SELF']); exit; } // Handle consent revocation if(isset($_POST['delete'])){ $PRIVACY = $_POST['privacy']; $USER_ID = $_SESSION['USER_ID']; $pdoQuery = "UPDATE privacy_user SET `VALID_TILL`=now() WHERE USER_ID=:USER_ID AND PRIVACY_ID=:PRIVACY_ID AND VALID_TILL IS NULL"; $pdoQuery_run = $mysql->prepare($pdoQuery); $pdoQuery_exec = $pdoQuery_run->execute(array(":USER_ID"=>$USER_ID, ":PRIVACY_ID"=>$PRIVACY)); header("Location: " . $_SERVER['PHP_SELF']); exit; } ?> <!-- Verarbeitung personenbezogener Daten --> <div class="card-body"> <div> <form method="post"> <div class="card shadow pb-2 bg-body rounded"> <h3 class="text-center card-header">Verarbeitung personenbezogener Daten</h3> <div class="card-body"> <input type="hidden" value="1" name="privacy"> <?php // Use in_array() for accurate ID checking (no string matching bugs) echo in_array(1, $agreedPrivacyIds) ? '<button class="btn btn-danger" name="delete" type="submit">Ich willige nicht ein</button>' : '<button class="btn btn-primary" name="update" type="submit">Ich willige ein</button>'; ?> </div> </div> </form> </div> </div> <!-- ... rest of your consent sections go here, using the same in_array() pattern ... --> </div> <!-- Footer --> <?php include_once "footer.php"?> </body> </html>
Key Improvements Explained
- Array Storage for Consent IDs: We use
$agreedPrivacyIdsas an array to store consent IDs, eliminating any string-matching bugs and making the logic more readable. - Prepared Statements: All database queries use parameter binding to prevent SQL injection, which is a critical security best practice.
- Reliable Redirects: Replaced
<meta http-equiv='refresh'>withheader()redirects, which prevents duplicate form submissions if the user refreshes the page. - Efficient Data Fetching: We only fetch the
PRIVACY_IDcolumn instead of full rows, reducing unnecessary data transfer from the database.
Quick Fix If You Want to Keep String Concatenation (Not Recommended)
If you absolutely need to stick with the string approach for some reason, just initialize $result to an empty string before your loop:
$result = ''; // Add this line foreach($users as $user){ $result .= $user['PRIVACY_ID']; } // Then use strpos with string literals to avoid type issues echo (strpos($result, '1') !== false) ? '<button class="btn btn-danger" name="delete" type="submit">Ich willige nicht ein</button>' : '<button class="btn btn-primary" name="update" type="submit">Ich willige ein</button>';
But the array approach is far more robust and less error-prone.
内容的提问来源于stack exchange,提问作者max

