"SECRET_EXAMPLE"]);
}
// === Security settings ===
// Block duration in seconds (48 hours by default)
if (!defined('BLOCK_DURATION_SECONDS')) {
define('BLOCK_DURATION_SECONDS', 48 * 3600); // Duration of IP block after failures
}
} else {
// If the file was accessed directly, deny access
error_deny();
}
/**
* Sends a standard 403 Forbidden HTTP response with an HTML message
* and terminates the script.
*/
function error_deny() : void {
// Set HTTP response status code to 403
http_response_code(403);
// Explicitly send HTTP headers for 403 Forbidden and HTML content type
header('HTTP/1.1 403 Forbidden');
header('Content-Type: text/html; charset=utf-8');
// Output a simple HTML page explaining the forbidden access
echo "\n";
echo "
\n";
echo "403 Forbidden\n";
echo "\n";
echo "\n";
echo "Forbidden
\n";
echo "You do not have permission to access this document.\n";
echo "\n";
echo "
\n";
echo "\n";
echo "Web Server at " . $_SERVER['SERVER_NAME'] . "\n";
echo "\n";
echo "\n";
echo "\n";
// Exit the script with status code 403
exit(403);
}
/**
* Retrieves a parameter from the request (POST takes precedence over GET).
* If the parameter is not found in either, returns the provided default value.
*
* @param string $name The name of the parameter to retrieve
* @param mixed $def The default value to return if parameter is not found
* @return mixed The value of the parameter or the default value
*/
function getQuery(string $name, mixed $def): mixed {
$val = $_POST[$name] ?? null;
if ($val === null) {
$val = $_GET[$name] ?? $def;
}
return $val;
}
/**
* Checks if a given value is considered "empty".
* - Returns true for null, empty strings, empty arrays, 0, "0", etc. (like PHP's `empty()`)
* - Additionally, for strings, also trims whitespace before checking emptiness
*
* @param mixed $val The value to evaluate
* @return bool True if the value is empty or whitespace-only, false otherwise
*/
function isEmpty(mixed $val): bool {
// Handle typical empty values (null, "", 0, [], false, etc.)
if (empty($val)) {
return true;
}
// If it's a string, trim and check again
if (is_string($val)) {
if (trim($val) === '') {
return true;
}
}
return false;
}
/**
* Loads and parses a JSON file into an associative array.
*
* Checks if the file exists and is readable before attempting to decode it.
* If the JSON is invalid, an error message is printed.
* If decoding fails or the result is not an array, an empty array is returned.
*
* @param string $filename Path to the JSON file
* @return array Parsed associative array or empty array on error
*/
function loadJson(string $filename): array {
$result = [];
// Attempt to read and decode the file if it exists and is readable
if (file_exists($filename) && is_readable($filename)) {
$result = json_decode(file_get_contents($filename), true);
// Log an error if JSON is invalid
if (json_last_error() !== JSON_ERROR_NONE) {
echo "Invalid JSON in $filename: " . json_last_error_msg() . "\n";
}
}
// Ensure the result is an array
if (!is_array($result)) {
$result = [];
}
return $result;
}
/**
* Saves a PHP array to a JSON file in pretty format.
*
* - Ensures the target file is writable if it already exists.
* - Automatically replaces null or empty input with an empty array.
* - Returns true on success, false on any failure.
*
* @param string $filename The path to the JSON file to write
* @param array|null $val The data to save (null or empty → [])
* @return bool True if saved successfully, false otherwise
*/
function saveJson(string $filename, ?array $val): bool {
// If file exists, check for write permissions
if (file_exists($filename)) {
if (!is_writable($filename)) {
echo "No access to write in $filename\n";
return false;
}
}
// Default to empty array if null or empty
if (isEmpty($val)) {
$val = [];
}
// Encode to JSON
$encoded = json_encode($val, JSON_PRETTY_PRINT);
if ($encoded === false) {
echo "Failed to encode $filename: " . json_last_error_msg() . "\n";
return false;
}
// Save to file
if (file_put_contents($filename, $encoded) === false) {
echo "Failed to write to $filename\n";
return false;
}
return true;
}