From ceb7ee44bddb8623104968c4167e6f0bf0b01f5a Mon Sep 17 00:00:00 2001
From: Jordi Kroon
Date: Fri, 2 Jan 2026 01:13:10 +0100
Subject: [PATCH 1/2] refactor: remove error suppression operator (@) and add
proper error handling
Replace error suppression operator (@) with explicit file existence and readability checks. Add new helper functions in include/file.inc (file_get_size, file_get_mtime, file_get_contents_if_exists) to safely handle file operations. This improves code reliability by catching actual errors rather than silencing them, and makes error handling explicit and testable throughout the codebase.
---
bin/news2html | 2 +-
cal.php | 14 ++++++--
download-docs.php | 8 ++---
images/elephpants.php | 2 +-
include/do-download.inc | 2 +-
.../windows-downloads.php | 2 +-
include/file.inc | 35 +++++++++++++++++++
include/footer.inc | 4 +--
include/get-download.inc | 4 +--
include/header.inc | 4 +--
include/layout.inc | 11 +++---
include/manual-lookup.inc | 12 +++----
include/prepend.inc | 3 ++
include/shared-manual.inc | 4 +--
index.php | 15 ++++----
manual/phpfi2.php | 2 +-
pre-release-builds.php | 2 +-
quickref.php | 6 ++--
18 files changed, 92 insertions(+), 40 deletions(-)
create mode 100644 include/file.inc
diff --git a/bin/news2html b/bin/news2html
index 661b9e0ecd..f342dd509c 100755
--- a/bin/news2html
+++ b/bin/news2html
@@ -11,7 +11,7 @@ if (count($_SERVER['argv']) < 2) {
}
$news_file = array_shift($_SERVER['argv']);
$version = array_shift($_SERVER['argv']);
-$changelog = @array_shift($_SERVER['argv']);
+$changelog = array_shift($_SERVER['argv']);
// find NEWS entry
$fp = fopen($news_file, "r");
diff --git a/cal.php b/cal.php
index 0ee2bc2e83..248d25a768 100644
--- a/cal.php
+++ b/cal.php
@@ -253,8 +253,11 @@ function display_events_for_day($day, $events): void
// Find a single event in the events file by ID
function load_event($id)
{
+ $path = "backend/events.csv";
+ if (!file_exists($path) || !is_readable($path)) { return false; }
+
// Open events CSV file, return on error
- $fp = @fopen("backend/events.csv",'r');
+ $fp = fopen($path,'r');
if (!$fp) { return false; }
// Read as we can, event by event
@@ -289,7 +292,10 @@ function load_events($from, $whole_month = false)
$events = $seen = [];
// Try to open the events file for reading, return if unable to
- $fp = @fopen("backend/events.csv",'r');
+ $path = "backend/events.csv";
+ if (!file_exists($path) || !is_readable($path)) { return false; }
+
+ $fp = fopen($path,'r');
if (!$fp) { return false; }
// For all events, read in the event and check it if fits our scope
@@ -357,7 +363,9 @@ function read_event($fp)
] = $linearr;
// Get info on recurring event
- @[$recur, $recur_day] = explode(":", $recur, 2);
+ $recurParts = explode(":", $recur, 2);
+ $recur = $recurParts[0];
+ $recur_day = $recurParts[1] ?? null;
// Return with SQL-resultset like array
return [
diff --git a/download-docs.php b/download-docs.php
index e4b3e27c46..bc3548d021 100644
--- a/download-docs.php
+++ b/download-docs.php
@@ -114,8 +114,8 @@
$link_to = "/distributions/manual/$filename";
// Try to get size and changed date
- $size = @filesize($filepath);
- $changed = @filemtime($filepath);
+ $size = file_get_size($filepath);
+ $changed = file_get_size($filepath);
// Size available, collect information
if ($size !== false) {
@@ -138,8 +138,8 @@
$actual_file = $_SERVER['DOCUMENT_ROOT'] . "/distributions/manual/php_manual_chm.zip";
if (file_exists($actual_file)) {
$link_to = "/get/php_manual_chm.zip/from/a/mirror";
- $size = @filesize($actual_file);
- $changed = @filemtime($actual_file);
+ $size = file_get_size($actual_file);
+ $changed = filemtime($actual_file);
if ($size !== FALSE) {
$files["en"]["zip"] = array(
$link_to,
diff --git a/images/elephpants.php b/images/elephpants.php
index b950c92386..5ecbc3b4c5 100644
--- a/images/elephpants.php
+++ b/images/elephpants.php
@@ -47,7 +47,7 @@
// read out photo metadata
$path = __DIR__ . '/elephpants';
-$json = @file_get_contents($path . '/flickr.json');
+$json = file_get_contents_if_exists($path . '/flickr.json');
$photos = json_decode($json, true);
// if no photo data, respond with an error.
diff --git a/include/do-download.inc b/include/do-download.inc
index b31f763b2c..f5cc05f27a 100644
--- a/include/do-download.inc
+++ b/include/do-download.inc
@@ -14,7 +14,7 @@ function get_actual_download_file($file)
// Find out what is the exact file requested
$found = false;
foreach ($possible_files as $name => $log) {
- if (@file_exists($_SERVER['DOCUMENT_ROOT'] . '/distributions/' . $name)) {
+ if (file_exists($_SERVER['DOCUMENT_ROOT'] . '/distributions/' . $name)) {
$found = $name;
break;
}
diff --git a/include/download-instructions/windows-downloads.php b/include/download-instructions/windows-downloads.php
index 89e7525bcc..aa5c10df24 100644
--- a/include/download-instructions/windows-downloads.php
+++ b/include/download-instructions/windows-downloads.php
@@ -1,7 +1,7 @@
' . "\n";
+ echo '' . "\n";
}
?>
' . "\n";
+ echo '' . "\n";
}
?>
diff --git a/include/get-download.inc b/include/get-download.inc
index 2aeb1aa896..efe2721e1c 100644
--- a/include/get-download.inc
+++ b/include/get-download.inc
@@ -18,7 +18,7 @@ $site_config = [
// Find out what is the exact file requested
$file = false;
foreach ($possible_files as $name) {
- if (@file_exists($_SERVER['DOCUMENT_ROOT'] . '/distributions/' . $name)) {
+ if (file_exists($_SERVER['DOCUMENT_ROOT'] . '/distributions/' . $name)) {
$file = $name;
break;
}
@@ -43,7 +43,7 @@ EOT;
// Set local file name
$local_file = $_SERVER['DOCUMENT_ROOT'] . '/distributions/' . $file;
// Try to get filesize to display
- $size = @filesize($local_file);
+ $size = file_get_size($local_file);
}
?>
diff --git a/include/header.inc b/include/header.inc
index 098f92924d..083f03b125 100644
--- a/include/header.inc
+++ b/include/header.inc
@@ -20,14 +20,14 @@ foreach($css_files as $filename) {
$filename = "/styles/$filename";
}
$path = dirname(__DIR__) . $filename;
- $CSS[$filename] = @filemtime($path);
+ $CSS[$filename] = filemtime($path);
}
$JS = [];
if (isset($config["js_files"])) {
foreach($config['js_files'] as $filename) {
$path = dirname(__DIR__) . '/' . $filename;
- $JS[$filename] = @filemtime($path);
+ $JS[$filename] = filemtime($path);
}
}
diff --git a/include/layout.inc b/include/layout.inc
index 02b6f733ca..7d108c6db7 100644
--- a/include/layout.inc
+++ b/include/layout.inc
@@ -94,8 +94,11 @@ function make_image($file, $alt = false, $align = false, $extras = false,
// If no / was provided at the start of $dir, add it
$webdir = $_SERVER['MYSITE'] . ($dir[0] == '/' ? '' : '/') . $dir;
- // Get width and height values if possible
- if ($addsize && ($size = @getimagesize($_SERVER['DOCUMENT_ROOT'] . "$dir/$file"))) {
+ // Get width and height values if possible
+ $filePath = $_SERVER['DOCUMENT_ROOT'] . "$dir/$file";
+ if (!file_exists($filePath)) {
+ $sizeparams = '';
+ } else if ($addsize && ($size = getimagesize($filePath))) {
$sizeparams = ' ' . trim($size[3]);
} else {
$sizeparams = '';
@@ -194,14 +197,14 @@ function download_link($file, $title): void
$local_file = "distributions/$file";
}
- if (@file_exists($local_file . ".asc")) {
+ if (file_exists($local_file . ".asc")) {
echo " ";
$sig_link = "/distributions/$file.asc";
echo make_link($sig_link, "(sig)");
}
// Try to get the size of the file
- $size = @filesize($local_file);
+ $size = file_get_size($local_file);
// Print out size in bytes (if size is
// less then 1Kb, or else in Kb)
diff --git a/include/manual-lookup.inc b/include/manual-lookup.inc
index 9d3ff4d141..6fdda45ce6 100644
--- a/include/manual-lookup.inc
+++ b/include/manual-lookup.inc
@@ -14,27 +14,27 @@ function tryprefix($lang, $keyword, $prefix)
// Try the keyword with the prefix
$try = "/manual/{$lang}/{$prefix}{$keyword}.php";
- if (@file_exists($_SERVER['DOCUMENT_ROOT'] . $try)) { return $try; }
+ if (file_exists($_SERVER['DOCUMENT_ROOT'] . $try)) { return $try; }
// Drop out spaces, and try that keyword (if different)
$nosp = str_replace(" ", "", $keyword);
if ($nosp != $keyword) {
$try = "/manual/{$lang}/{$prefix}{$nosp}.php";
- if (@file_exists($_SERVER['DOCUMENT_ROOT'] . $try)) { return $try; }
+ if (file_exists($_SERVER['DOCUMENT_ROOT'] . $try)) { return $try; }
}
// Replace spaces with hyphens, and try that (if different)
$dasp = str_replace(" ", "-", $keyword);
if ($dasp != $keyword) {
$try = "/manual/{$lang}/{$prefix}{$dasp}.php";
- if (@file_exists($_SERVER['DOCUMENT_ROOT'] . $try)) { return $try; }
+ if (file_exists($_SERVER['DOCUMENT_ROOT'] . $try)) { return $try; }
}
// Remove hyphens (and underscores), and try that (if different)
$noul = str_replace("-", "", $keyword);
if ($noul != $keyword) {
$try = "/manual/{$lang}/{$prefix}{$noul}.php";
- if (@file_exists($_SERVER['DOCUMENT_ROOT'] . $try)) { return $try; }
+ if (file_exists($_SERVER['DOCUMENT_ROOT'] . $try)) { return $try; }
}
// urldecode() (%5C == \) Replace namespace sperators, and try that (if different)
@@ -42,7 +42,7 @@ function tryprefix($lang, $keyword, $prefix)
$noul = str_replace("\\", "-", $keyword);
if ($noul != $keyword) {
$try = "/manual/{$lang}/{$prefix}{$noul}.php";
- if (@file_exists($_SERVER['DOCUMENT_ROOT'] . $try)) { return $try; }
+ if (file_exists($_SERVER['DOCUMENT_ROOT'] . $try)) { return $try; }
}
// Replace first - with a dot and try that (for mysqli_ type entries)
@@ -53,7 +53,7 @@ function tryprefix($lang, $keyword, $prefix)
$keyword[$pos] = '.';
$try = "/manual/{$lang}/{$prefix}{$keyword}.php";
- if (@file_exists($_SERVER['DOCUMENT_ROOT'] . $try)) { return $try; }
+ if (file_exists($_SERVER['DOCUMENT_ROOT'] . $try)) { return $try; }
}
}
diff --git a/include/prepend.inc b/include/prepend.inc
index 83e50b8260..6572e3ad55 100644
--- a/include/prepend.inc
+++ b/include/prepend.inc
@@ -79,6 +79,9 @@ $userPreferences = new UserPreferences();
// Load the My PHP.net settings before any includes
$userPreferences->load();
+// Import functions to deal with files in a safe manner
+include_once __DIR__ . '/file.inc';
+
// Site details (mirror site information)
include __DIR__ . '/site.inc';
diff --git a/include/shared-manual.inc b/include/shared-manual.inc
index 5d10ea5df0..a79c483b1a 100644
--- a/include/shared-manual.inc
+++ b/include/shared-manual.inc
@@ -101,11 +101,11 @@ function manual_notes_load(string $id): array
// Open the note file for reading and get the data (12KB)
// ..if it exists
- if (!file_exists($notes_file)) {
+// if (!file_exists($notes_file) || !is_readable($notes_file)) {
return [];
}
$notes = [];
- if ($fp = @fopen($notes_file, "r")) {
+ if ($fp = fopen($notes_file, "r")) {
while (!feof($fp)) {
$line = chop(fgets($fp, 12288));
if ($line == "") { continue; }
diff --git a/index.php b/index.php
index 9e24fc8c5f..5dd362f4a3 100644
--- a/index.php
+++ b/index.php
@@ -15,8 +15,11 @@
}
})($_SERVER['REQUEST_URI'] ?? '');
+// Import functions to deal with files in a safe manner
+include_once __DIR__ . '/include/file.inc';
+
// Get the modification date of this PHP file
-$timestamps = [@getlastmod()];
+$timestamps = [getlastmod()];
/*
The date of prepend.inc represents the age of ALL
@@ -25,13 +28,13 @@
the display of the index page). The cost of stat'ing
them all is prohibitive.
*/
-$timestamps[] = @filemtime("include/prepend.inc");
+$timestamps[] = file_get_mtime("include/prepend.inc");
// These are the only dynamic parts of the frontpage
-$timestamps[] = @filemtime("include/pregen-confs.inc");
-$timestamps[] = @filemtime("include/pregen-news.inc");
-$timestamps[] = @filemtime("include/version.inc");
-$timestamps[] = @filemtime("js/common.js");
+$timestamps[] = file_get_mtime("include/pregen-confs.inc");
+$timestamps[] = file_get_mtime("include/pregen-news.inc");
+$timestamps[] = file_get_mtime("include/version.inc");
+$timestamps[] = file_get_mtime("js/common.js");
// The latest of these modification dates is our real Last-Modified date
$timestamp = max($timestamps);
diff --git a/manual/phpfi2.php b/manual/phpfi2.php
index 691bbc6fac..a0c6ab47fa 100644
--- a/manual/phpfi2.php
+++ b/manual/phpfi2.php
@@ -2927,7 +2927,7 @@ function being called. You cannot create new global variables
messages appear on the web screen. This can be done by putting
the "@" character in front of the function call. ie.
- $err_code = @dbmopen($filename,"w");
+ $err_code = dbmopen($filename,"w");
The actual error message that would have been printed can be
diff --git a/pre-release-builds.php b/pre-release-builds.php
index 83ccd72099..e31701c0a1 100644
--- a/pre-release-builds.php
+++ b/pre-release-builds.php
@@ -120,7 +120,7 @@
$winQaReleases = [];
if (is_readable($winQaFile)) {
- $raw = @file_get_contents($winQaFile);
+ $raw = file_get_contents_if_exists($winQaFile);
$decoded = $raw ? json_decode($raw, true) : null;
if (is_array($decoded)) {
$allowedBranches = [];
diff --git a/quickref.php b/quickref.php
index 569937b416..de25cb1272 100644
--- a/quickref.php
+++ b/quickref.php
@@ -44,9 +44,9 @@ function quickref_table($functions, $sort = true): void
// Open directory, fall back to English,
// if there is no dir for that language
-$dirh = @opendir($_SERVER['DOCUMENT_ROOT'] . "/manual/$LANG");
-if (!$dirh) {
- error_noservice();
+$path = $_SERVER['DOCUMENT_ROOT'] . "/manual/$LANG";
+if (!is_dir($path) || !is_readable($path) || !$dirh = opendir($path)) {
+ error_noservice();
}
$functions = $maybe = $temp = $parts = [];
From 7db92d1946aa388e710740172eaf2b1bb9a8b8a4 Mon Sep 17 00:00:00 2001
From: Jordi Kroon
Date: Fri, 2 Jan 2026 01:15:00 +0100
Subject: [PATCH 2/2] docs: improve file.inc documentation and remove stray
braces
Add comprehensive documentation to the file header explaining the purpose
of helper functions and their safety checks. Remove trailing stray braces
that appear to be leftover code.
---
include/file.inc | 7 +++----
1 file changed, 3 insertions(+), 4 deletions(-)
diff --git a/include/file.inc b/include/file.inc
index e0c25625ab..c4f0e23de4 100644
--- a/include/file.inc
+++ b/include/file.inc
@@ -1,7 +1,9 @@