UPDATE :
ADDED 4 Separate Files For functions.... Question Edited Accordingly.
Please check new code for security and other points.
Providing code with some usage examples
Original Class Created on which following answers received...
Link - HERE ( Deleted Original Content From HEre Due To Characters Limit in question )
Expecting your valuable suggestions on it.
UPDATED FILES :
DatabaseConfig.php
<?php
declare(strict_types=1);
// DatabaseConfig.php
// --- Database Configuration Constants ---
// Define these constants with your actual database credentials.
// For security, consider loading these from environment variables or a configuration file outside the web root.
define('DB_HOST', 'localhost'); // Your database host
define('DB_NAME', 'test_db'); // Your database name
define('DB_USER', 'root'); // Your database username
define('DB_PASSWORD', ''); // Your database password
define('DB_CHARSET', 'utf8mb4'); // Default character set
// Define the default timezone for date/time functions.
// Refer to PHP's list of supported timezones: https://www.php.net/manual/en/timezones.php
define('DEFAULT_APP_TIMEZONE', 'Asia/Kolkata');
// Set the default timezone for all date/time functions in the script.
date_default_timezone_set(DEFAULT_APP_TIMEZONE);
?>
DatabaseConnection.php
<?php
declare(strict_types=1);
// DatabaseConnection.php
require_once 'DatabaseConfig.php';
/**
* DatabaseConnection Class
* Manages the PDO connection to a MySQL database.
*/
class DatabaseConnection
{
private PDO $pdo;
/**
* Constructor: Establishes a PDO connection to the database.
*
* @param string $host The database host.
* @param string $dbName The database name.
* @param string $user The database username.
* @param string $password The database password.
* @param string $charset The character set.
* @throws Exception If the database connection fails.
*/
public function __construct(
string $host = DB_HOST,
string $dbName = DB_NAME,
string $user = DB_USER,
string $password = DB_PASSWORD,
string $charset = DB_CHARSET
) {
$dsn = "mysql:host={$host};dbname={$dbName};charset={$charset}";
$options = [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false,
];
try {
$this->pdo = new PDO($dsn, $user, $password, $options);
} catch (PDOException $e) {
$errorMessage = "Database connection failed: " . $e->getMessage();
error_log($errorMessage); // Log the error
// Re-throw a generic Exception for the calling code to handle
throw new Exception("A critical database connection error occurred. Please try again later.");
}
}
/**
* Returns the PDO instance.
*
* @return PDO The PDO database connection object.
*/
public function getPdo(): PDO
{
return $this->pdo;
}
}
QueryBuilder.php
<?php
declare(strict_types=1);
// QueryBuilder.php
/**
* QueryBuilder Class
* Responsible for building SQL query components and escaping identifiers.
*/
class QueryBuilder
{
/**
* Escapes a database identifier (table name, column name, alias) with backticks
* and handles existing backticks within the identifier.
*
* @param string $identifier The identifier name to escape.
* @param bool $doubleBackticks If true, doubles internal backticks (default: true).
* Set to false for aliases where doubling might not be desired
* if they are simple, unquoted identifiers. However, for for full safety,
* it's generally best to always escape.
* @return string The escaped identifier.
*/
public function escapeIdentifierdefine(string $identifier'HST', bool $doubleBackticks = true): string
{
if ($doubleBackticks'localhost') {
; // Escape any existing backticks byYour doublingdatabase themhost
$identifier = str_replacedefine("`", "``"'USR', $identifier'root');
}
// Wrap the identifier in backticks
return "`" . $identifier . "`";
}
/**
* Helper function to build the WHERE clause string and populate parameters array.
* Supports AND conditions at the top level and one special '_OR_' group.
* The '_OR_' group should contain an array of condition sets, where each set
* is ANDed internally, and then these sets are ORed together.
*
* @param array<string, mixed> $conditions An associative array of column => value pairs for the WHERE clause.
* Can include a special '_OR_' key for OR conditions.
* @param array<mixed> $params Reference to the main parameters array to add new values.
* @return string The WHERE clause string (e.g., " WHERE `column` = ? AND (`other_column` = ? OR `another_column` = ?)").
*/
public function buildWhereClause(array $conditions, array &$params): string
{
$whereParts = [];
$orGroupClauses = [];
foreach ($conditions as $key => $value) {
if ($key === '_OR_' && is_array($value)) {
$currentOrGroupParts = [];
foreach ($value as $orConditionSet) {
if (is_array($orConditionSet)) {
$subConditionParts = [];
foreach ($orConditionSet as $subColumn => $subValue) {
$conditionPart = $this->_processSingleCondition($subColumn, $subValue, $params);
if ($conditionPart !== null) {
$subConditionParts[] = $conditionPart;
}
}
ifYour (!empty($subConditionParts))database {username
$currentOrGroupParts[] = "(" . implodedefine(" AND "'PWD', $subConditionParts) . ")";
}
} else {
error_log("Invalid OR condition set format. Expected an array of associative arrays."'');
}
}
if (!empty($currentOrGroupParts)) {
$orGroupClauses[] = "(" . implode(" OR ", $currentOrGroupParts) . ")";
}
// }Your elsedatabase {password
$conditionPart = $this->_processSingleConditiondefine($key, $value'DBN', $params'test_db');
if ($conditionPart !== null) {
$whereParts[] = $conditionPart;
}
}
// Your database }
name
$finalWhereParts = array_mergedefine($whereParts'SEND_ERRORS_TO', $orGroupClauses);
return empty($finalWhereParts) ? '' : " WHERE " . implode(" AND ", $finalWhereParts);
}
/**
* Helper function to process a single condition (column => value pair).
*
* @param string $column The column name.
* @param mixed $value The value for the/ conditione.
* @param array<mixed> $params Reference to the main parameters array to add new valuesg.
* @return string|null The SQL snippet for the condition, or null if invalid'your_admin_email@example.
*/com'
private function _processSingleConditiondefine(string $column, mixed $value'DEFAULT_TIMEZONE', array &$params'Asia/Kolkata'): ?string
{
$operator = '=';
$field = $column;
$isBinary = false;;
if (strpos(strtoupper(trim($column)), 'BINARY ') === 0) {
$isBinary = true;
/* ^^ can be $fielddefined =in substr(trim($column),different 7);
file and can be included that file here. }*/
if (preg_match('/(.*?)\s*(<|>|<=|>=|!=|<>|LIKE|NOT LIKE|IN|NOT IN|IS NULL|IS NOT NULL)$/i', $field, $matches)) {
$field = trim($matches[1]);
$operator = strtoupper(trimdate_default_timezone_set($matches[2])DEFAULT_TIMEZONE);
}
$field_escaped = $this->escapeIdentifier($field);
if ($isBinary) {
$field_escaped = "BINARY {$field_escaped}";
}
if ($operator === 'IN' || $operator === 'NOT IN') {
if (is_array($value)) {
$placeholders = $this->inClauseHelper($value, $params);
return "{$field_escaped} {$operator} ({$placeholders})";
}class elseDB {
error_log("Invalid value for IN/NOT IN clause for column '{$field}'. Value must be anprivate array.");$host;
returnprivate null;$dbName;
private }$user;
} elseif ($operator === 'IS NULL' || $operator === 'IS NOT NULL')private {$password;
return "{$field_escaped}private {$operator}";$charset;
} elseprivate {$pdo;
private $params[]$queryCount = $value;
return "{$field_escaped} {$operator} ?";0;
}
private $errorEmailRecipient = }SEND_ERRORS_TO;
/**
* HelperConstructor: functionConnect to generate placeholders for an IN clausea andgiven bindMySQL parametersserver.
*
* @param array<mixed> $values An array of values for the INUsage clause.Example:
* @param$database array<mixed>= $paramsnew ReferenceDB(HST, toDBN, theUSR, mainPWD, parameters'utf8mb4');
array to add new values.*
* @return@param string A string$host ofThe comma-separateddatabase placeholdershost (e.g., '?, ?, ?''localhost').
*/
@param string $dbName publicThe functiondatabase inClauseHelper(arrayname.
$values, array &$params): * @param string
$user The database {username.
* @param string $password ifThe (empty($values))database {password.
* @param string $charset The character set return(default: 'NULL';'utf8mb4').
}*/
public function $placeholders__construct($host = implode('HST, ',$dbName array_fill(0,= count($values)DBN, '?'));
$user = USR, $password = foreachPWD, ($values$charset as= $val'utf8mb4') {
$this->host = $host;
$params[]$this->dbName = $val;$dbName;
$this->user = }$user;
$this->password return= $placeholders;$password;
}
}
CrudOperations.php
<?php
declare(strict_types=1);
// CrudOperations.php
require_once$this->charset 'DatabaseConnection.php';
require_once= 'QueryBuilder.php';$charset;
/** $dsn = "mysql:host={$this->host};dbname={$this->dbName};charset={$this->charset}";
* CrudOperations Class $options = [
* Provides core CRUD (Create, Read, Update PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, Delete)// functionalities.Throw exceptions on errors
* PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, /
class/ CrudOperations
{Default fetch mode to associative array
private PDO::ATTR_EMULATE_PREPARES $pdo;
=> false, private QueryBuilder $queryBuilder;
private int $queryCount = 0; // Disable emulation for better security and performance
];
/**try {
* Constructor.
$this->pdo = new *PDO($dsn, $this->user, $this->password, $options);
} *catch @param(PDOException PDO$e) $pdo{
The PDO database connection object.
$errorMessage = "Database */connection failed: " . $e->getMessage();
public function __construct(PDO $pdo error_log($errorMessage)
; // Log the {error
$this->pdo>send_error_email("Database =Connection $pdo;
Error", $errorMessage);
$this->queryBuilder =throw new QueryBuilderException("A critical database error occurred. Please try again later.");
}
}
/**
* Execute arbitrary SQL queries (SELECT, INSERT, UPDATE, DELETE, etc.).
* This function uses prepared statements to prevent SQL injection.
*
* Usage Example:
* $stmt = $database->execute_query("SELECT * FROM users WHERE id = ?", [1]);
* if ($stmt) {
* $user = $stmt->fetch();
* print_r($user);
* }
*
* $stmt = $database->execute_query("INSERT INTO products (name, price) VALUES (?, ?)", ["Laptop", 1200.50]);
* if ($stmt->rowCount() > 0) {
* echo "Product added successfully!";
* }
*
* @param string $sql The SQL query string.
* @param array<mixed>array $params An associative or indexed array of parameters to bind to the query.
* @return PDOStatementPDOStatement|false Returns the PDOStatement object on success.
* @throws PDOException If, theor queryfalse executionon failsfailure.
*/
public function executeQueryexecute_query(string $sql, array $params = []): PDOStatement
{
try {
$stmt = $this->pdo->prepare($sql);
$stmt->execute($params);
$this->queryCount++;
return $stmt;
} catch (PDOException $e) {
$errorMessage = "Query execution failed: " . $e->getMessage() . " SQL: " . $sql . " Params: " . json_encode($params);
error_log($errorMessage);
// Re$this-throw the exception to allow calling code>send_error_email("Database toQuery handleError", it$errorMessage);
throw $e;
return }false;
}
}
/**
* Retrieve the number of rows from a table based on multiple WHERE clauses.
*
* Usage Example:
* $conditions = [
* 'status' => 'active',
* 'category_id' => 5
* ];
* $active_products_count = $database->num_rows('products', $conditions);
* OR $active_products_count = $database->num_rows('products', ['status' => 'active','category_id' => 5]);
* echo "Number of active products: " . $active_products_count;
*
* @param string $table The name of the table.
* @param array<string, mixed>array $conditions An associative array of column => value pairs for the WHERE clause.
* @return int The number of rows matching the conditions, or 0 on error.
*/
public function numRowsnum_rows(string $table, array $conditions = []): int
{
$sql = "SELECT COUNT(*) FROM " . $this->queryBuilder->escapeIdentifier(`{$table);}`";
$params = [];
$whereClause = $this->queryBuilder->buildWhereClause>build_where_clause($conditions, $params);
$sql .= $whereClause;
try {
$stmt = $this->executeQuery>execute_query($sql, $params);
$result =if $stmt->fetchColumn($stmt);
{
return is_numeric($result) ? (int) $result : 0;
} catch $stmt->fetchColumn(PDOException $e) {
// Logged in executeQuery, re-throw or handle as appropriate for this specific method
return 0; // Return 0 rows on error
};
}
return 0;
}
/**
* Retrieve the query results in a single array.
* This function now accepts table name, conditions, selected columns, order by, group by, and limit.
*
* @param string $table The name of the table to retrieve data from.
* @param array<string, mixed> $conditions An associative array of column => value pairs for the WHERE clause.
* @param array<string> $selectColumns An array of column names to selectUsage (defaultExample: ['*'] for all columns).
* @param array<string, string>|string $orderBy An ORDER BY clause. If an array (e.g., `['column_name' => 'ASC']`),
* column names will be escaped. If a string, it's used as-is (developer's responsibility to sanitize).
* @param array<string>|string $groupBy A GROUP BY clause. If an array (e.g., `['column1', 'column2']`),
* column names will be escaped. If a string, it's used as-is (developer's responsibility to sanitize).
* @param int|null $limit A LIMIT clause.
* @param int $fetch_style The PDO fetch style.
* @return array<array<string, mixed>> An array of query results.
*/
public function getResults(
string $table,
array $conditions = [],
array $selectColumns = ['*'],
array|string $orderBy = '',
array|string $groupBy = '',
?int $limit = null,
int $fetch_style = PDO::FETCH_ASSOC
): array {
$selectClause = implode(", ", array_map([$this->queryBuilder, 'escapeIdentifier'], $selectColumns));
$params = [];
$whereClause = $this->queryBuilder->buildWhereClause($conditions, $params);
$sql = "SELECT {$selectClause} FROM " . $this->queryBuilder->escapeIdentifier($table) . "{$whereClause}";
if (!empty($groupBy)) {
if (is_array($groupBy)) {
$escapedGroupBy = implode(", ", array_map([$this->queryBuilder, 'escapeIdentifier'], $groupBy));
$sql .= " GROUP BY {$escapedGroupBy}";
} else {
// Developer's responsibility to sanitize if string is provided
$sql .= " GROUP BY {$groupBy}";
}
}
if (!empty($orderBy)) {
if (is_array($orderBy)) {
$orderByParts = [];
foreach ($orderBy as $column => $direction) {
$direction = strtoupper($direction);
if (!in_array($direction, ['ASC', 'DESC'])) {
$direction = 'ASC'; // Default to ASC for invalid directions
}
$orderByParts[] = $this->queryBuilder->escapeIdentifier($column) . " {$direction}";
}
$sql .= " ORDER BY " . implode(", ", $orderByParts);
} else {
// Developer's responsibility to sanitize if string is provided
$sql .= " ORDER BY {$orderBy}";
}
}
if ($limit !== null && $limit > 0) {
$sql .= " LIMIT {$limit}";
}
Fetch tryall {users
* $stmt$allUsers = $this->executeQuery($sql, $params);
return $stmt$database->fetchAll>get_results($fetch_style'users');
} catch (PDOException $e) {
// Logged in executeQuery, re-throw or handle as appropriate for this specific method
return []; // Return empty array on error
}
}
/**
* Get the ID of the last inserted row.
*
* @return string The// IDFetch ofactive theusers, lastselecting insertedspecific row.columns
*/
public$activeUsers function= lastInsertId$database->get_results():'users', string
['status' => 'active'], ['id', 'name', {'email']);
* foreach ($activeUsers returnas $this->pdo->lastInsertId($user);
{
}
* echo /**
$user['name'] . " - " *. Check$user['email'] if. a"<br>";
table exists in the current* database.}
*
* @param string $tableName The name of the table to check.
* @return bool True if the table exists, false otherwise.
*/
public function tableExists(string $tableName): bool
{
$sql/ =Fetch "SHOWproducts TABLESwith LIKEprice ?";
greater than 100, ordered by price descending, limited tryto {5
* $stmt$expensiveProducts = $this$database->executeQuery>get_results($sql'products', [$tableName]);
return $stmt->rowCount() > 0;
} catch (PDOException $e) {
//['price Logged>' in=> executeQuery100], re-throw or handle as appropriate for this specific method
return false; // Table does not exist on error
}
}
/**
* Check if a given table['name', record'price'], exists'price basedDESC', onnull, conditions.5);
*
* @param string $table The name// ofFetch the table.
* @param array<string, mixed> $conditions An associative array of column => value pairslatest foruser theadded WHERE(by clause.ID)
* @return$latestUser bool= True$database->get_results('users', if[], the['id', record'name'], exists'id DESC', falsenull, otherwise.1);
*/
public functionif recordExists(string $table, array $conditions = []!empty($latestUser): bool
) {
return $this->numRows($table, $conditions) > 0;
* echo }
"Latest user: " . /**$latestUser[0]['name'];
* Return a query result that has just one row.}
*
* @param string $table The name of the table to retrieve data from.
* @param array<string, mixed> $conditions An associative array of column => value pairs for the WHERE clause.
* @param array<string> $selectColumns// AnGet arraycount of column names to select (default: ['*'] for all columns).
* @param int $fetch_style The PDO fetch style.
* @return array<string, mixed>|false An associative array representing the single row, or false if no row is found orusers onby error.role
*/
public$userRoleCounts function= getSingleRow$database->get_results(string $table'users', array $conditions = [], array $selectColumns = ['*'], int $fetch_style = PDO::FETCH_ASSOC): array|false
{
$selectClause = implode(", "['role', array_map'COUNT([$this->queryBuilder, 'escapeIdentifier'], $selectColumns)id);
$params = [];
$whereClause =as $this->queryBuilder->buildWhereClause($conditionsuser_count'], $params);
$sql = "SELECT {$selectClause} FROM " . $this->queryBuilder->escapeIdentifier($table) . "{$whereClause} LIMIT 1";
try {
$stmt = $this->executeQuery($sql'', $params'role');
* returnforeach $stmt->fetch($fetch_style);
} catch$userRoleCounts (PDOExceptionas $e$roleCount) {
* echo "Role: " //. Logged$roleCount['role'] in. executeQuery", re-throw or handle as appropriate for this specific method
return false; // Return false on error
}
Count: }
" . $roleCount['user_count'] . /**"<br>";
* Execute an INSERT query from values that define table, field names, and field values.}
*
* @param string// $tableExample Thewith tableOR name.condition:
* @param array<string, mixed> $data An associative array of column => value pairs$conditions_or to= insert.[
* @return bool True'status' on=> success'active', false on failure.
*/
public function insert(string $table, array $data): bool
/ This will be {ANDed
if (empty($data)) {
* '_OR_' => [ return false;
// This whole block will be ORed }
together
$columns = implode(",* ",['role' array_map([$this->queryBuilder,=> 'escapeIdentifier']'admin'], array_keys($data)));
$placeholders* =['role' implode(",=> "'editor', array_fill(0,'department' count($data),=> '?'));
'marketing'] // This is (role = 'editor' AND $paramsdepartment = array_values($data'marketing');
$sql = "INSERT INTO " . $this->queryBuilder->escapeIdentifier($table) . " ({$columns}) VALUES* ({$placeholders})";]
try* {];
* $users_complex = $this$database->executeQuery>get_results($sql'users', $params);
return true;
} catch (PDOException $e) {
// Logged in executeQuery$conditions_or, re-throw or handle as appropriate for this specific method
return false; // Return false on error
}
}
/**
* Execute an INSERT MULTI query for inserting multiple['name', rows'email', at'role', once.'department']);
*
* @param string $table The name of the table nameto retrieve data from.
* @param array<array<string, mixed>>array $dataArray$conditions An associative array of associativecolumn arrays=> value pairs for the WHERE clause.
* Supports operators like 'column >', where'column each<', inner'column LIKE', 'column IN', etc.
* @param array represents$selectColumns aAn rowarray of column names to insertselect (default: ['*'] for all columns).
* @return@param boolstring True$orderBy onAn successORDER BY clause (e.g., false'column_name ASC', 'column_name DESC').
* @param string $groupBy A GROUP BY clause (e.g., 'column_name', 'column1, column2').
* @param int|null $limit A LIMIT clause (e.g., 10).
* @param int $fetch_style The PDO fetch style (e.g., PDO::FETCH_ASSOC, PDO::FETCH_OBJ).
* @return array An array of query results. Returns an empty array on failureno results or error.
*/
public function insertMulti(string $table, array $dataArray): bool
{
if (empty($dataArray) || !is_array($dataArray[0])) {
return false;
}
public function get_results($table, $columns$conditions = implode("[], "$selectColumns = ['*'], array_map([$this->queryBuilder$orderBy = '', 'escapeIdentifier']$groupBy = '', array_keys($dataArray[0])));
$limit = null, $fetch_style = PDO::FETCH_ASSOC) {
$placeholders = "("$selectClause .= implode(", ", array_fill(0, count($dataArray[0]$selectColumns),;
'?')) . " $params = [];
$whereClause = $this->build_where_clause($conditions, $params)";;
$values = [];
$params$sql = [];
foreach ($dataArray as $row)"SELECT {
if$selectClause} (!is_array($row))FROM `{ // Ensure each row is an array
error_log("Invalid row format in insertMulti. Expected an array.");
return false;
$table}
$values[] = $placeholders;
$params = array_merge($params, array_values($row));
`{$whereClause}";
$sql = "INSERT INTO " .if $this->queryBuilder->escapeIdentifier($table) . " !empty({$columns}$groupBy) VALUES " . implode(", ", $values);
try {
$this->executeQuery($sql, $params);
return true;
}.= catch" (PDOExceptionGROUP $e)BY {
// Logged in executeQuery, re-throw or handle as appropriate for this specific method
return false; // Return false on error
$groupBy}";
}
/**
* Execute an UPDATE query from values that define table, field names, field values, and conditions.
*
* @param string $table The table name.
* @param array<string, mixed> $data An associative array of column => new_value pairs to update.
* @param array<string, mixed> $conditions An associative array of column => value pairs for the WHERE clause.
* @return bool True on success, false on failure.
*/
public function update(string $table, array $data, array $conditions): bool
{
if (empty($data) || empty($conditions)) {
return false;
}
$setParts = [];
$params = [];
foreachif ($data as $column => $value) {
$setParts[] = $this->queryBuilder->escapeIdentifier!empty($column$orderBy) . " = ?";
$params[] = $value;
}
$setClause = implode(", ", $setParts);
$whereClause = $this->queryBuilder->buildWhereClause($conditions, $params);
{
$sql = "UPDATE " . $this->queryBuilder->escapeIdentifier($table) .= " SETORDER {$setClause}BY {$whereClause$orderBy}";
try {
$this->executeQuery($sql, $params);
return true;
} catch (PDOException $e) {
// Logged in executeQuery, re-throw or handle as appropriate for this specific method
return false; // Return false on error
}
}
/**
* Execute a DELETE query from values that define table and conditions.
*
* @param string $table The table name.
* @param array<string, mixed> $conditions An associative array of column => value pairs for the WHERE clause.
* @return bool True on success, false on failure.
*/
public function delete(string $table, array $conditions): bool
{
if (empty($conditions)) {
error_log("Attempted to delete from table '{$table}' without conditions. Operation aborted.");
return false; // Prevent accidental full table deletion
}
$params = [];
$whereClause = $this->queryBuilder->buildWhereClause($conditions, $params);
$sql = "DELETE FROM " .if $this->queryBuilder->escapeIdentifier($table) . " {$whereClause}";
try {
$limit !== null && $this->executeQueryis_int($sql, $params$limit);
return true;
}&& catch$limit (PDOException> $e0) {
// Logged in executeQuery, re-throw or handle as appropriate for this specific method
return false; // Return false on error
$sql .= " LIMIT {$limit}";
}
/**
* Truncate a table (removes all rows and resets auto-increment counter).
*
* @param string $tableName The name of the table to truncate.
* @return bool True on success, false on failure.
*/
public function truncateTable(string $tableName): bool
{
$sql$stmt = "TRUNCATE TABLE " . $this->queryBuilder->escapeIdentifier($tableName);
try {
$this->executeQuery>execute_query($sql, $params);
return true;
} catchif (PDOException $e$stmt) {
// Logged in executeQuery, re-throw or handle as appropriate for this specific method
return false; // Return false on error
}$stmt->fetchAll($fetch_style);
}
/**
* Perform a JOIN operation on multiple tables.
*
* @param array<string, string> $tables An associative array of table_name => alias (e.g., ['users' => 'u']).
* @param array<string> $onConditions An array of strings representing the JOIN ON conditions (e.g., 'u.id = o.user_id').
* NOTE: These conditions are directly concatenated. Ensure they are from a trusted source and do not contain
* unvalidated user input to prevent SQL injection.
* @param array<string> $selectColumns An array of columns to select (default: ['*']).
* @param array<string, mixed> $whereConditions An associative array of column => value pairs for the WHERE clause.
* @return array<array<string, mixed>> An array of joined query results. Returns an empty array on no results or error.
*/
public function joinTable(array $tables, array $onConditions, array $selectColumns = ['*'], array $whereConditions = []): array
{
if (empty($tables) || empty($onConditions)) {
return [];
}
$fromClause = '';
$joinClause = '';
$firstTable = true;
foreach ($tables as $tableName => $alias) {
if ($firstTable) {
// Escape table name and alias. Aliases typically don't need internal backtick doubling if they are simple.
$fromClause = $this->queryBuilder->escapeIdentifier($tableName) . " AS " . $this->queryBuilder->escapeIdentifier($alias, false);
$firstTable = false;
} else {
$joinClause .= " INNER JOIN " . $this->queryBuilder->escapeIdentifier($tableName) . " AS " . $this->queryBuilder->escapeIdentifier($alias, false);
}
}
if (!empty($onConditions)) {
// WARNING: $onConditions are directly concatenated. Sanitize or whitelist input in application layer.
$joinClause .= " ON " . implode(" AND ", $onConditions);
}
$selectClause = implode(", ", array_map([$this->queryBuilder, 'escapeIdentifier'], $selectColumns));
$params = [];
$whereClause = $this->queryBuilder->buildWhereClause($whereConditions, $params);
$sql = "SELECT {$selectClause} FROM {$fromClause} {$joinClause} {$whereClause}";
try {
$stmt = $this->executeQuery($sql, $params);
return $stmt->fetchAll(PDO::FETCH_ASSOC);
} catch (PDOException $e) {
// Logged in executeQuery, re-throw or handle as appropriate for this specific method
return []; // Return empty array on error
}
}
/**
* RetrievesDetermine recordsif whereone avalue specifiedor column'san valuearray isof withinvalues contain common MySQL function calls.
* This is a givenbasic arraycheck ofand valuesshould (INnot clause)replace prepared statements for security.
*
* @param string $table The name of the table.
* @param string $column The column name to apply the IN condition on.
* @param array<mixed> $values An array of values to check against the column.
* @param array<string, mixed> $additionalConditions Optional associative array of additional WHERE conditions (ANDed).
* @param array<string> $selectColumns An array of column names to selectUsage (defaultExample: ['*'] for all columns).
* @param array<string, string>|string $orderBy An ORDER BY clause. If an arrayif (e.g., `['column_name' => 'ASC']`),
* column names will be escaped. If a string, it's used as$database-is (developer's responsibility to sanitize).
* @param array<string>|string $groupBy A GROUP BY clause. If an array >contains_mysql_functions(e.g., `['column1', 'column2']`$_GET['order_by']),
* column names will be escaped. If a string, it's used as-is (developer's responsibility to sanitize).
* @param int|null $limit A LIMIT clause.{
* @return array<array<string, mixed>>echo An"Potential arraySQL ofinjection queryattempt results.detected!";
*/
public function in(
string $table,
string $column,
array $values,
array $additionalConditions = [],
array $selectColumns = ['*'],
array|string $orderBy = '',
array|string $groupBy = '',
?int $limit = null
): array {
if (empty($table) || empty($column) || empty($values)) {
error_log("Invalid parameters for 'in' function. Table, column, and non-empty array of values are required.");
return [];
}
$conditions = array_merge($additionalConditions, [
else "{$column} IN" => $values
* ]);
// Proceed with using $_GET['order_by'] in ORDER BY returnclause $this->getResults($table, $conditions, $selectColumns, $orderBy, $groupBy, $limitcarefully!);
}
/**
* Executes a SELECT query using FIND_IN_SET on a specified column.}
*
* @param string $table The name of the table to query.
* @param stringmixed $value The value to search for within the comma-separated string.
* @param string $column The column name containing comma-separated values.
* @param array<string> $selectColumns Anor array of column namesstrings to select (default: ['*']).
* @param array<string, mixed> $additionalConditions Optional associative array of additional WHERE conditionscheck.
* @param int $fetch_style The PDO@return fetchbool style.
True if any common MySQL *function @returnis array<array<stringfound, mixed>> An array of queryfalse resultsotherwise.
*/
public function findInSetcontains_mysql_functions(string$value) $table{
$functions = [
'SLEEP', string'BENCHMARK', $value'UNION', string'SELECT', $column'INSERT', array'UPDATE', $selectColumns'DELETE',
= ['*'] 'DROP', array'TRUNCATE', $additionalConditions'ALTER', ='CREATE', []'GRANT', int'REVOKE', $fetch_style'RENAME',
= PDO::FETCH_ASSOC): array 'LOAD_FILE', 'OUTFILE', 'INFILE', '@@', 'CONCAT', 'CAST', 'CONVERT',
{ 'ASCII', 'CHAR', 'UNHEX', 'HEX', 'MID', 'SUBSTRING', 'LEFT', 'RIGHT',
$selectClause'IF', ='CASE', implode("'AND', "'OR', array_map([$this->queryBuilder'XOR', 'escapeIdentifier']'NOT', $selectColumns));'NULL', 'VERSION', 'DATABASE',
$params'USER', ='CURRENT_USER', [$value];'SYSTEM_USER', 'SESSION_USER', 'SCHEMA',
'INFORMATION_SCHEMA', 'MYSQL.USER', 'PASSWORD', 'MD5', 'SHA1', 'SHA2',
'AES_ENCRYPT', 'AES_DECRYPT', 'FROM_UNIXTIME', 'UNIX_TIMESTAMP',
'GROUP_CONCAT', 'ORDER BY', 'GROUP BY', 'HAVING', 'LIMIT', 'OFFSET'
];
$whereParts$regex = ["FIND_IN_SET'/\b(?, "' . $this->queryBuilder->escapeIdentifierimplode($column'|', $functions) . "')"];\b/i';
if (!emptyis_array($additionalConditions$value)) {
$tempParams = [];
$additionalWhereClause = $this->queryBuilder->buildWhereClause($additionalConditions, $tempParams);
ifforeach (strpos($additionalWhereClause, ' WHERE ')$value ===as 0$val) {
$additionalWhereClause = substr($additionalWhereClause, 7);
}
if (!emptypreg_match($additionalWhereClause$regex, $val)) {
$whereParts[] = $additionalWhereClause;
$params = array_merge($params,return $tempParams);true;
}
}
$whereClause = " WHERE " . implode(" AND ", $whereParts);
$sql = "SELECT {$selectClause} FROM " . $this->queryBuilder->escapeIdentifier($table) . "{$whereClause}";
tryelse {
$stmt =if $this->executeQuery($sqlpreg_match($regex, $params);
return $stmt->fetchAll($fetch_style$value);
} catch (PDOException $e) {
// Logged in executeQuery, re-throw or handle as appropriate for this specific method
return []; // Return empty array on errortrue;
}
}
return false;
}
/**
* Checks if a record exists in a table based on given conditions.
*
* @param string $table The database table name.
* @param array<string, mixed> $conditions An associative array of column name => column value to match.
* @return bool True if the record exists, false otherwise.
*/
public function exists(string $table = '', array $conditions = []): bool
{
if (empty($table) || empty($conditions)) {
return false;
}
return $this->numRows($table, $conditions) > 0;
}
/**
* CASE-SENSITIVE check to seeCheck if a recordtable exists in a table based onthe givencurrent conditionsdatabase.
*
Uses BINARY comparison for* caseUsage sensitivity.Example:
*
if ($database->table_exists('users')) {
* @param string $table* Theecho database"Table table'users' nameexists.";
* @param array<string, mixed>} $conditionselse An{
associative array of column* nameecho =>"Table column'users' valuedoes tonot matchexist.";
* @return bool True if the record exists case-sensitively, false otherwise.}
*/
public* function@param binExists(string $table = '', array $conditions$tableName =The []):name bool
of the table to {check.
* @return bool True if (empty($table) || empty($conditions)) {
the table exists, false otherwise.
return false;*/
public function table_exists($tableName) }
{
$binaryConditions$sql = [];
"SHOW TABLES LIKE ?";
$stmt foreach= $this->execute_query($conditions as $field =>$sql, $value[$tableName]) {;
$binaryConditions["BINARYreturn "$stmt .&& $field]$stmt->rowCount() => $value;0;
}
return $this->numRows($table, $binaryConditions) > 0;
}
/**
* ReturnsExecute thean numberINSERT ofquery fieldsfrom (columns)values inthat thedefine resulttable, setfield ofnames, aand queryfield values.
*
* @paramUsage PDOStatement|falseExample:
$stmt The PDOStatement object returned by* executeQuery.$new_user_data = [
* @return'name' int=> The'Alice numberSmith',
of fields, or 0 if statement* is'email' invalid=> 'alice@example.com',
*/
'status' => 'active'
public function getNumFields(PDOStatement|false $stmt): int* ];
* if ($database->insert('users', $new_user_data)) {
* ifecho ($stmt"New instanceofuser PDOStatement)inserted {successfully!";
* echo "Last Insert ID: " return. $stmt$database->columnCount>last_insert_id();
* } else {
* echo "Failed to insert new user.";
* }
*
* @param string $table The table name.
* @param array $data An associative array of column => value pairs to insert.
* @return bool True on success, false on failure.
*/
public function insert($table, $data)
{
if (empty($data)) {
return 0;false;
}
/**
* Returns an array of/ fieldEscape (column) names from the result set of a query.
*
* @param PDOStatement|false $stmt The PDOStatement object returned by executeQuery.
with *backticks @returnto array<string>prevent AnSQL arrayinjection ofin column names, or an empty array if statement is invalid.
*/
$columns = implode(", public", array_map(function getListFields(PDOStatement|false $stmt$column): array
{
$fieldNames = [];
return "`" if. str_replace($stmt"`", instanceof"``", PDOStatement$column) {
. for"`";
($i = 0; $i <}, $stmt->columnCountarray_keys($data); $i++) {
$meta = $stmt->getColumnMeta($i);
$placeholders = implode(", if", array_fill(isset0, count($meta['name'])$data), {'?'));
$fieldNames[]$params = $meta['name'];
}
}
}
return $fieldNames;
}array_values($data);
/**
* Display the total number of queries performed by this instance.
*
* @return int The total number of queries executed.
$sql */
= "INSERT INTO `{$table}` public({$columns}) functionVALUES getQueryCount({$placeholders}): int";
{
$stmt = $this->execute_query($sql, $params);
return $this->queryCount;
$stmt !== }false;
}
UtilityFunctions.php
<?php
declare(strict_types=1);
// UtilityFunctions.php
// Include random-code-generators-pdo.php if it contains functions used here.
require_once 'random-code-generators-pdo.php';
/**
* UtilityFunctionsExecute Classan INSERT MULTI query for inserting multiple rows at once.
*
Provides a collection of* generalUsage Example:
* $products_to_add = [
* ['name' => 'Keyboard', 'price' => 75.00, 'category_id' => 1],
* ['name' => 'Mouse', 'price' => 25.50, 'category_id' => 1],
* ['name' => 'Monitor', 'price' => 299.99, 'category_id' => 2]
* ];
* if ($database-purpose>insert_multi('products', utility$products_to_add)) methods{
* echo "Multiple products inserted successfully!";
* } else {
* echo "Failed to insert multiple products.";
* }
*
* @param string $table The table name.
* @param array $dataArray An array of associative arrays, where each inner array represents a row to insert.
* @return bool True on success, false on failure.
*/
class UtilityFunctions public function insert_multi($table, $dataArray)
{
/**
* Filter input data to prevent XSS and other vulnerabilities.
* This function can handle strings and arrays recursively.
*
* @param mixed $data The input data (string or array).
* @return mixed The filtered data.
*/
public function filter(mixed $data): mixed
{
if (is_array($data)) {
foreach ($data as $key => $value) {
$data[$key] = $this->filter($value);
}
return $data;
} else {
// Ensure $data is a string before passing to htmlspecialchars
$data = (string) $data;
$data = htmlspecialchars($data, ENT_QUOTES, 'UTF-8');
$data = strip_tags($data);
return $data;
}
}
/**
* Escape a single string or an array of literal text values.
* This is a basic escaping and should be used cautiously. For SQL, prepared statements are preferred.
*
* @param string|array<string> $value The string or array of strings to escape.
* @return string|array<string> The escaped string(s).
*/
public function escape(string|array $value): string|array
{
if (is_array($value)) {
return array_map('addslashes', $value); // Basic escaping, not SQL-safe without context
}
return addslashes($value); // Basic escaping, not SQL-safe without context
}
/**
* Calculate the difference between two dates.
*
* @param string $date1 The first date string (e.g., 'YYYY-MM-DD').
* @param string $date2 The second date string (e.g., 'YYYY-MM-DD').
* @return array<string, int> An associative array with 'years', 'months', 'days'. Returns empty array on invalid dates.
*/
public function dateDifference(string $date1, string $date2): array
{
try {
$dt1 = new DateTime($date1$dataArray);
$dt2 = new|| DateTime!is_array($date2$dataArray[0]);
$interval = $dt1->diff($dt2);
return [
'years' => $interval->y,
'months' => $interval->m,
'days' => $interval->d,
];
} catch (Exception $e) {
error_log("Invalid date format in dateDifference: " . $e->getMessage());
return [];
}false;
}
/**
* Calculate the difference between two datetimes.
*
/ Escape *column @paramnames stringwith $datetime1backticks Theto firstprevent datetimeSQL stringinjection (e.g.,in 'YYYY-MM-DDcolumn HH:MM:SS').names
* @param string $datetime2 The second datetime$columns string= implode(e.g., 'YYYY-MM-DD HH:MM:SS').
* @return array<string, int> An associative array with 'years', 'months', 'days', 'hours'", 'minutes'", 'seconds'. Returns empty array on invalid datetimes.
*/
public array_map(function datetimeDifference(string $datetime1, string $datetime2$column): array
{
try {
$dt1 = new DateTime($datetime1);
$dt2 = new DateTime($datetime2);
$interval = $dt1->diff($dt2);
return [
'years' => $interval->y,
'months' => $interval->m,
'days' => $interval->d,
'hours' => $interval->h,
'minutes'"`" =>. $interval->istr_replace("`",
'seconds' => $interval->s"``",
$column) . ];"`";
} catch, array_keys(Exception $e$dataArray[0]) {
error_log("Invalid datetime format in datetimeDifference: " . $e->getMessage());
return [];
}
}
/**
* Convert a past datetime into a human-readable$placeholders "time= ago""(" string.
*
* @param string $datetime The past datetime string implode(e.g.", 'YYYY-MM-DD HH:MM:SS').
* @return string The "time ago" string", or "Invalid date" on error.
*/
public function timeAgoarray_fill(string $datetime): string
{
try {
$now = new0, DateTimecount($dataArray[0]);
$past = new, DateTime($datetime'?');
$diff) =. $now->diff($past");";
if ($diff->y > 0) {
return $diff->y . ' year' . ($diff->y > 1 ? 's' : '') . ' ago';
} elseif ($diff->m > 0) {
return $diff->m . ' month' . ($diff->m > 1 ? 's' : '') . ' ago';
} elseif ($diff->d > 0) {
return $diff->d . ' day' . ($diff->d > 1 ? 's' : '') . ' ago';
} elseif ($diff->h > 0) {
return $diff->h . ' hour' . ($diff->h > 1 ? 's' : '') .$values '= ago';[];
} elseif ($diff->i >$params 0)= {[];
return $diff->i . ' minute' .foreach ($diff->i > 1 ? 's'$dataArray :as ''$row) . ' ago';
} else {
return $diff->s . ' second' . ($diff->s > 1 ? 's' : '') . ' ago';
}
} catch (Exception$values[] $e)= {$placeholders;
$params = error_logarray_merge("Invalid datetime format in timeAgo: " .$params, $e->getMessagearray_values($row));
return "Invalid date";
}
}
/**
* Convert a numeric amount to Indian currency format (e.g., 1,23,456.78).
*
* @param float|int $amount The numeric amount.
* @param int $decimalPlaces The number of decimal places (default: 2).
* @return string The formatted amount.
*/
public function amountToIndianFormat(float|int $amount, int $decimalPlaces = 2): string
{
$num = (string) round($amount, $decimalPlaces);
if (strpos($num, '.') === false) {
$num .= '.00';
}
$exploded = explode('.', $num);
$integer = $exploded[0];
$decimal = $exploded[1] ?? '';
$len = strlen($integer);
$result = '';
$count = 0;
for ($i = $len - 1; $i >= 0; $i--) {
$result = $integer[$i] . $result;
$count++;
if ($count == 3 && $i != 0) {
$result = ',' . $result;
$count = 0;
} elseif ($count == 2 && $i != 0 && $len > 3 && $i != ($len - 3)) { // For lakhs and crores
$result = ',' . $result;
$count = 0;
}
}
$sql = "INSERT INTO if`{$table}` ($decimalPlaces > 0) {
$columns}) VALUES $decimal" =. str_pad(substrimplode($decimal, 0, $decimalPlaces), $decimalPlaces", '0'", STR_PAD_RIGHT$values);
return $result . '.' . $decimal;
$stmt }= else$this->execute_query($sql, {$params);
return $result;
$stmt !== }false;
}
/**
* Convert aExecute numerican amountUPDATE toquery textfrom invalues Indianthat formatdefine (e.g.table, "One Lakh Twenty-Three Thousand Rupees").
field *names, Supportsfield upvalues, toand Croresconditions.
*
* @param float|int $amount The numericUsage amount.Example:
* @return string The amount in words.
$update_data */
= ['status' => 'inactive', public'last_login' function=> amountToTextIndianFormatdate(float|int'Y-m-d $amount)H: string
{
$number = explode('.', sprintf('%.2f', $amount)i:s');];
* $rupees$conditions = (int) $number[0];
['id' => 1];
$paisa* =if (int) $database->update($number[1]'users', ??$update_data, 0$conditions);
) {
* echo "User ID $words1 =updated '';
successfully!";
if ($rupees* >} 0)else {
* echo "Failed to $wordsupdate .=user $this->convertNumberToWordsIndian($rupees)ID 1. ' Rupees';";
* }
if ($paisa > 0) {*
* @param string $table The table ifname.
($rupees > 0) {
* @param array $data An associative array of column => new_value pairs to update.
$words .=* '@param andarray ';
$conditions An associative array of column => value pairs for the WHERE }clause.
* @return bool True on success, false $wordson failure.=
$this->convertNumberToWordsIndian($paisa) . ' Paisa';*/
public function update($table, }$data, $conditions)
{
if (empty($words)$data) {
return 'Zero Rupees';
|| empty($conditions)) }
{
return trim($words);false;
}
/**
* Private helper function to convert numbers to words in Indian system.
*
* @param int $num The number to convert.
* @return string The number in words.
*/
private function convertNumberToWordsIndian(int $num): string
{
$n = abs($num);
$ones$setParts = [
0 => '', 1 => 'One', 2 => 'Two', 3 => 'Three', 4 => 'Four', 5 => 'Five',
6 => 'Six', 7 => 'Seven', 8 => 'Eight', 9 => 'Nine', 10 => 'Ten',
11 => 'Eleven', 12 => 'Twelve', 13 => 'Thirteen', 14 => 'Fourteen',
15 => 'Fifteen', 16 => 'Sixteen', 17 => 'Seventeen', 18 => 'Eighteen',
19 => 'Nineteen'
];[];
$tens$params = [
2 => 'Twenty', 3 => 'Thirty', 4 => 'Forty', 5 => 'Fifty',
6 => 'Sixty', 7 => 'Seventy', 8 => 'Eighty', 9 => 'Ninety'
];
[];
ifforeach ($n < 20) {
return $ones[$n];
}
$data ifas ($n$column <=> 100$value) {
return $tens[intdiv($n, 10)] . ' ' . $ones[$n % 10];
}
$words = [];
// Crores
Escape column names with ifbackticks ($nfor >=the 10000000)SET {clause
$words[]$escapedColumn = $this->convertNumberToWordsIndian(intdiv($n, 10000000))"`" . ' Crore';
$n %= 10000000;
}
// Lakhs
if ($n >= 100000) {
$words[] = $this->convertNumberToWordsIndian(intdivstr_replace($n"`", 100000)) . ' Lakh';
$n %= 100000;
}
// Thousands
if ($n >= 1000) {
$words[] = $this->convertNumberToWordsIndian(intdiv($n"``", 1000)$column) . ' Thousand';
$n %= 1000;
}
// Hundreds
if ($n >= 100) {"`";
$words[]$setParts[] = $this->convertNumberToWordsIndian(intdiv($n, 100)) . ' Hundred';
$n %= 100;
}
// Remaining (tens and ones)
if ($n > 0) "{
$words[]$escapedColumn} = $this->convertNumberToWordsIndian($n);
}
?";
return implode('$params[] ',= $words);$value;
}
/**
* Smart word wrap for long strings, preventing single long words from breaking line length.
*
* @param string $string The input string.
* @param int $width The maximum line width.
* @param string $break The line break string (e.g., "<br>", "\n").
* @return string The wrapped string.
*/
public function smartWordwrap(string $string, int $width = 75, string $break = "<br>"): string
{
$pattern = sprintf('/([^ ]{%d,})/', $width);
$output = '';
$words$setClause = preg_splitimplode($pattern, $string", -1", PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE);
if ($words === false) {
error_log("Failed to split string in smartWordwrap."$setParts);
return $string; // Return original string on error
}
foreach ($words as $word) {
if (str_contains($word, ' ')) {
$output .= $word;
} else {
$wrapped = explode($break, wordwrap($output, $width, $break));
$lastWrappedLine = end($wrapped);
$count$whereClause = $width $this- (strlen>build_where_clause($lastWrappedLine) %$conditions, $width$params);
$output$sql .= substr($word,"UPDATE 0,`{$table}` $count)SET .{$setClause} $break;{$whereClause}";
$output$stmt .= wordwrap(substr$this->execute_query($word, $count), $width, $break$sql, true$params);
}
}
return wordwrap($output,$stmt $width,!== $break);false;
}
/**
* ConvertsExecute a "minutesDELETE query from values that define table and conditions.seconds"
string *
* Usage Example:
* $conditions = ['status' => 'inactive', 'last_login <' => '2023-01-01'];
* if ($database->delete('users', $conditions)) {
* echo "Inactive users deleted successfully!";
* } else {
* echo "Failed to totaldelete secondsinactive users.";
* }
*
* @param string $minSec$table The timetable stringname.
in "minutes.seconds" format * @param array $conditions An associative array of column => value pairs for the WHERE clause.
* Supports operators like 'column >', 'column <', 'column LIKE', etc.
* @return intbool TheTrue totalon numbersuccess, offalse secondson failure.
*/
public function convertMinSecToSecondsdelete(string$table, $minSec$conditions): int{
if (empty($conditions)) {
$parts// =Prevent explode('.',accidental $minSec);
full table deletion without conditions
$minutes = (int) error_log($parts[0]"Attempted ??to 0delete from table '{$table}' without conditions. Operation aborted.");
$seconds$this->send_error_email("Security =Alert: (int)Unconditional ($parts[1]Delete ??Attempt", 0);
"Attempted to delete from table '{$table}' without conditions.");
return $minutes * 60 + $seconds;return false;
}
/**
* Checks if a string contains only English characters (UTF-8 safe).
*
* @param string $str The string to check.
* @return bool True if the string contains only English characters, false otherwise.
*/
public function isEnglish(string $str): bool
$params = {[];
if (strlen($str)$whereClause != mb_strlen$this->build_where_clause($str$conditions, 'utf-8')$params) {
return false;
} else {
return true;
}
};
/**
* Checks if a given text contains a URL (simple check for 'http' or 'www.').
*
* @param string $text The text to check.
* @return bool True if a URL is found, false otherwise.
*/
public function isUrlInText(string $text):$sql bool
= "DELETE FROM `{$table}` {$whereClause}";
$bHasLink$stmt = str_contains($text, 'http') || str_contains$this->execute_query($text$sql, 'www.'$params);
return $bHasLink;
$stmt !== false;
}
/**
* ChecksHelper iffunction ato passwordbuild meetsthe specifiedWHERE strengthclause requirementsstring (uppercase,and lowercase,populate number,parameters array.
* Supports AND conditions at the top level and one special char'_OR_' group.
* The '_OR_' group should contain an array of condition sets, length)where each set
* is ANDed internally, and then these sets are ORed together.
*
* @param string $password The password string to check.
array $conditions An associative array *of @paramcolumn int=> $minlenvalue Minimumpairs allowedfor lengththe (default:WHERE 7)clause.
* @paramCan intinclude $maxlena Maximumspecial allowed'_OR_' lengthkey (default:for 21)OR conditions.
* @return@param boolarray True$params ifReference to the passwordmain meetsparameters requirements,array falseto otherwiseadd new values.
*/
@return string The publicWHERE functionclause checkPassword(string $password(e.g., int $minlen = 7," intWHERE $maxlen`column` = 21): bool
{
$uppercase =? (bool)AND preg_match('@[A-Z]@', $password);
$lowercase`other_column` = (bool) preg_match('@[a-z]@', $password);
? OR $number`another_column` = (bool?) preg_match('@[0-9]@', $password");.
$specialChar = (bool) preg_match('@[^\w]@', $password);
*/
if ($uppercase && $lowercase && $number && $specialChar && strlen($password) >= $minlenprivate &&function strlenbuild_where_clause($password) <=$conditions, $maxlen&$params) {
return true;
} else {
$whereParts return= false;[];
}
$orGroupClauses = }[];
/**foreach ($conditions as $key => $value) {
* Hides a portionif of($key an=== email'_OR_' address&& foris_array($value)) display.{
*
* @param string// $emailThis Theis emailan address.OR group, process each sub-condition set
* @return string The obfuscated email address. $currentOrGroupParts = [];
*/
public function hideMail foreach (string$value $emailas $orConditionSet): string{
if (is_array($orConditionSet)) {
$parts = explode('@', $email); $subConditionParts = [];
if (count foreach ($parts)$orConditionSet !==as 2$subColumn => $subValue) {
return $email; // Build each individual condition within the OR group
} $operator = '=';
$username $field = $parts[0];$subColumn;
$domain $isBinary = $parts[1];false;
$hiddenUsername = substr($username, 0, min(2, strlen if ($username)strpos(strtoupper(trim($subColumn)), .'BINARY str_repeat('*',') max(1,=== strlen($username0) -{
2 $isBinary = true;
$field = substr(trim($subColumn), 7);
}
return $hiddenUsername . '@' if (preg_match('/(.*?)\s*(<|>|<=|>=|!=|<>|LIKE|NOT $domain;LIKE|IN|NOT IN|IS NULL|IS NOT NULL)$/i', $field, $matches)) {
$field = trim($matches[1]);
$operator = strtoupper(trim($matches[2]));
}
/**
* Obfuscates an email address, optionally also obfuscating the domain.
*
* @param string $email The email$field_escaped address= to"`" obfuscate.
* @param bool $domain_ If truestr_replace("`", obfuscate the domain part"``", as$field) well.
"`"; // Escape *column @returnname
string The obfuscated email string.
*/
public function ofuscaEmail(string $email, bool $domain_ = false): string
if ($isBinary) {
$seg = explode('@', $email);
if (count($seg) !== 2) {
$field_escaped = return"BINARY $email;{$field_escaped}";
}
$user = '';
$domain = '';}
if (strlen($seg[0]$operator === 'IN' || $operator === 'NOT IN') >{
3 if (is_array($subValue)) {
$sub_seg $placeholders = str_split$this->in_clause_helper($seg[0]$field, $subValue, $params);
$user .= $sub_seg[0] . $sub_seg[1];
for ($i = 2; $i$subConditionParts[] <= count"{$field_escaped} {$operator} ($sub_seg{$placeholders})";
- 1; $i++) } else {
if error_log($sub_seg[$i]"Invalid ===value for IN/NOT IN clause within OR group for column '.{$field}'. Value must be an array.");
continue;
}
} elseif ($operator === 'IS NULL' || $sub_seg[$i]$operator === '_''IS NOT NULL') {
$user . $subConditionParts[] = $sub_seg[$i];"{$field_escaped} {$operator}";
} else {
$subConditionParts[] = "{$field_escaped} {$operator} ?";
$params[] = $subValue;
}
}
if (!empty($subConditionParts)) {
$currentOrGroupParts[] = "(" . implode(" AND ", $subConditionParts) . ")";
}
} else {
$usererror_log("Invalid OR condition set format.= '*';Expected an array of associative arrays.");
}
}
$user .=if $sub_seg[count($sub_seg!empty($currentOrGroupParts) -) 1];{
} else {
$sub_seg$orGroupClauses[] = str_split"($seg[0]);
$user" .= $sub_seg[0];
for implode($i = 1; $i" <OR count($sub_seg);", $i++$currentOrGroupParts) {
$user .= ($sub_seg[$i] === '.'") ? '.' : '*';";
}
} else {
// This is a regular AND condition
$operator = '=';
$field = $key; // $key is the column name
$isBinary = false;
if ($domain_) {
$sub_seg2 =if str_split($seg[1]strpos(strtoupper(trim($key);
), 'BINARY ') $domain=== .=0) $sub_seg2[0];{
for ($i = 1; $i < count($sub_seg2) - 2;$isBinary $i++)= {true;
$domain$field .= substr($sub_seg2[$i] === '.'trim($key) ? '.' :, '*';7);
}
$domain .= $sub_seg2[count($sub_seg2) - 2] . $sub_seg2[count($sub_seg2) - 1];
} else {
$domain = $seg[1];
}
return $user . '@' if (preg_match('/(.*?)\s*(<|>|<=|>=|!=|<>|LIKE|NOT $domain;LIKE|IN|NOT IN|IS NULL|IS NOT NULL)$/i', $field, $matches)) {
$field = trim($matches[1]);
$operator = strtoupper(trim($matches[2]));
}
/**
* Hides the middle$field_escaped part= of"`" a. mobilestr_replace("`", number"``", with$field) asterisks.
"`"; // Escape column *name
* @param string $mobile The mobile number.
* @return stringif The($isBinary) obfuscated{
mobile number.
*/
public function hideMobile(string $mobile): string
$field_escaped = "BINARY {$field_escaped}";
return substr($mobile, 0, -4) . "****";
}
/** if ($operator === 'IN' || $operator === 'NOT IN') {
* Hides a specific part of a mobile number with a customif mask.(is_array($value)) {
* $placeholders = $this->in_clause_helper($field, $value, $params);
* @param string $mobile The mobile number. $whereParts[] = "{$field_escaped} {$operator} ({$placeholders})";
* @return string The masked mobile number. } else {
* error_log("Invalid value for IN/NOT IN clause for column '{$field}'. Value must be an array.");
public function hiddenMobile continue;
}
} elseif (string$operator $mobile=== 'IS NULL' || $operator === 'IS NOT NULL'): string{
$whereParts[] = "{$field_escaped} {$operator}";
return substr_replace($mobile, '#-###-###', 1, 7);} else {
$whereParts[] = "{$field_escaped} {$operator} ?";
$params[] = $value;
}
}
}
// Combine all AND parts and the OR group clauses
$finalWhereParts = array_merge($whereParts, $orGroupClauses);
/**
* Formats a date string to 'dd Month Walpole'return empty(e.g., '01 January 2023'$finalWhereParts).
*
* @param string $newdate The date string.
* @return string The formatted date.
*/
public function? dateFormat1(string'' $newdate): string
{
" WHERE $changed_date" =. dateimplode('d" FAND Y'", strtotime($newdate)$finalWhereParts);
return $changed_date;
}
/**
* Formats a date string to 'dd Mon Walpole' (e.g., '01 Jan 2023').
*
* @param string $newdate The date string.
* @return string The formatted date.
*/
public function dateFormat2(string $newdate): string
{
$changed_date = date('d M Y', strtotime($newdate));
return $changed_date;
}
/**
* FormatsTruncate a timetable string(removes toall 'HH:MM'rows (24and resets auto-hourincrement formatcounter).
*
* Usage Example:
* if ($database->truncate_table('logs')) {
* @paramecho string"Table $newdate'logs' Thetruncated datetimesuccessfully!";
string * } else {
* echo "Failed to truncate table 'logs'.";
* }
*
@return * @param string $tableName The formattedname timeof the table to truncate.
* @return bool True on success, false on failure.
*/
public function timeFormat1truncate_table(string $newdate$tableName): string{
try {
$changed_date$sql = date"TRUNCATE TABLE `{$tableName}`";
$stmt = $this->pdo->prepare('H$sql);
$stmt->execute();
$this->queryCount++;
return true;
} catch (PDOException $e) {
$errorMessage = "Failed to truncate table '{$tableName}':i', strtotime" . $e->getMessage($newdate);
error_log($errorMessage);
$this->send_error_email("Database Truncate Error", $errorMessage);
return $changed_date;false;
}
}
/**
* Formats a datetime string to 'dd Mon Walpole HH:MM:SS'.
*
* @param string $newdate The datetime string.
* @return string The formatted datetime.
*/
public function datetimeFormat1(string $newdate): string
{
$changed_date = date('d M Y H:i:s', strtotime($newdate));
return $changed_date;
}
/**
* FormatsSend aemail datetimemessages stringwith toMySQL 'Onaccess ddand Monquery Walpoleerrors.
At HH:MM:SS' * Requires mail() function to be configured on the server.
*
* @paramUsage stringExample $newdate(internal, Thecalled datetimeby string.
class methods on error):
* @return$this->send_error_email("Database stringConnection TheError", formatted"Could datetimenot connect to MySQL.");
*/
public* function@param datetimeFormat2(string $newdate):$subject string
The subject of the {email.
* @param string $message $changed_date1The =body 'Onof 'the email. date('d
M Y', strtotime($newdate));
* @return bool True if the email $changed_date2was =sent 'Atsuccessfully, 'false otherwise.
date */
public function send_error_email('H:i:s'$subject, strtotime($newdate$message) {
if ($this->errorEmailRecipient === null || $this->errorEmailRecipient === ''); {
$changed_date// =No $changed_date1recipient .set, 'so 'don't .send $changed_date2;email
return $changed_date;false;
}
/**
* Formats a datetime$headers string= to'From: 'dd[email protected]' Mon. Walpole"\r\n" HH:MM'.
*
* @param string $newdate The datetime'Reply-To: stringwebmaster@yourdomain.
* @return string The formattedcom' datetime.
*/
public function datetimeFormat3(string $newdate):"\r\n" string.
{
$changed_date = date('d M'X-Mailer: YPHP/' H:i',. strtotimephpversion($newdate));
return $changed_date;
}
/**
/ In a real application, *you Formatsmight awant datetimeto stringformat tothe 'On<b>message ddbetter
Mon Walpole</b> At<b> HH:MM</b>'/ withand boldinclude tags.
more context like $_SERVER data, stack *trace, etc.
$fullMessage *= @param"An stringerror $newdateoccurred Thein datetimeyour stringapplication:\n\n" .
* @return string The formatted datetime with HTML tags "Subject: " . $subject . "\n" .
*/
public function datetimeFormat4(string $newdate) "Message: string
" . $message . {"\n\n" .
$changed_date1 = 'On<b> ' . date('d M Y', strtotime "Timestamp: " . date($newdate)'Y-m-d H:i:s') . '</b>';"\n" .
$changed_date2 = 'At<b> ' . date('H "Script:i', strtotime" . ($newdate)$_SERVER['PHP_SELF'] ?? 'N/A') . '</b>';"\n" .
$changed_date = $changed_date1 "Referer: " . '($_SERVER['HTTP_REFERER'] '?? 'N/A') . $changed_date2;"\n" .
return $changed_date;
} "User Agent: " . ($_SERVER['HTTP_USER_AGENT'] ?? 'N/A');
/**
* Calculates the difference between two dates in years.
*
* @param string $date_greater The later date stringreturn mail(e.g., 'YYYY-MM$this-DD').
* @param string $date_old The earlier date string (e.g.>errorEmailRecipient, 'YYYY-MM-DD').
* @return int The difference in full years.
*/
public function dateDifferenceYears(string $date_greater = ''$subject, string $date_old = ''): int
{
try {
$d1 = new DateTime($date_greater);
$d2 = new DateTime($date_old);
$diff = $d2->diff($d1);
return $diff->y;
} catch (Exception $e) {
error_log("Invalid date format in dateDifferenceYears: " .$fullMessage, $e->getMessage()$headers);
return 0;
}
}
/**
* CalculatesSet the difference between two dates and returns it in "X years Y months" or "Y months" format.
*
* @param string $date1 The first date string.
* @param string $date2email Therecipient secondfor dateerror stringnotifications.
* @return string The formatted date difference.
*/
public function dateDifference1(string $date1 = '', string $date2 =Usage '')Example: string
{
try {
$datetime1 = date_create(date("Y-m-d", strtotime($date1)));
$datetime2 = date_create(date("Y-m-d", strtotime($date2)));
if ($datetime1 === false || $datetime2 === false) {
throw new Exception("Invalid date format provided.");
}
$interval = date_diff($datetime1, $datetime2);
if ($interval->y >= 1) {
$result =* $interval$database->y . ' year' . >set_error_email_recipient($interval->y > 1 ? 's' : '') . ' ' . $interval->m . ' month' 'admin@yourdomain. ($interval->m > 1 ? 's' : ''com');
} else {*
* @param string $email The $resultemail =address $interval->mto .send 'error month'notifications to. ($interval->m > 1 ? 's' : '');
}
return $result;*/
}public catchfunction set_error_email_recipient(Exception $e$email) {
error_log("Invalid date format in dateDifference1: " . $e$this->getMessage());
return "Invalid date";
>errorEmailRecipient = }$email;
}
/**
* CalculatesDisplay the difference between two datetimes in minutes.
*
* @param string $date1 The first datetime string.
total number of *queries @paramperformed stringduring $date2all Theinstances secondof datetimethe stringclass.
* @return string The difference in minutes (e.g., "30 minutes").
*/
public function datetimeDifferenceInMinutes(string $date1 = '', string $date2 =Usage '')Example: string
{
try {
* $datetime1echo ="Total date_create(date("Y-m-dqueries H:iexecuted:s", strtotime($date1)));
$datetime2" =. date_create(date("Y-m$database-d H:i:s", strtotime>get_query_count($date2)));
if ($datetime1 === false || $datetime2 === false) {*
* @return int throwThe newtotal Exception("Invalidnumber datetimeof formatqueries providedexecuted.");
}
*/
$intervalpublic =function date_diffget_query_count($datetime1, $datetime2);
$min = $interval->days * 24 * 60;{
$min +=return $interval$this->h * 60;>queryCount;
$min += $interval->i;}
$result = $min . ' minutes';
return $result;
} catch (Exception $e) {
error_log("Invalid datetime format in datetimeDifferenceInMinutes: " . $e->getMessage());
return "Invalid datetime";
}
}
/**
* Calculates ageChecks fromif a birthdaterecord inexists "Xin Years,a Ytable Months,based Zon Days"given formatconditions.
*
* @paramThis stringfunction $birthdatesecurely Theuses birthdateprepared stringstatements (e.g.,via 'YYYY-MM-DD')num_rows.
* @return string The calculated age string.
*/
public function ageCalculation(stringUsage $birthdate)Example: string
{
* $check_user = ['user_email' => '[email protected]', 'user_id' try=> {48];
* $sdate$exists = new DateTime$database->exists($birthdate'users', $check_user);
$edate = new DateTime();*
* @param string $table The database table $intervalname.
= $sdate->diff($edate);
* @param array $conditions An associative array of column name => $resultcolumn =value $interval->yto match.
" Years, "* .@return $interval->mbool .True "if Months,the "record .exists, $interval->dfalse otherwise. " Days ";
return $result;*/
public function exists($table = }'', catch$conditions (Exception= $e[]) {
if error_log("Invalid birthdate format in ageCalculation: "empty($table) .|| $e->getMessageempty($conditions));
{
return "Invalid birthdate";
}false;
}
return $this->num_rows($table, $conditions) > 0;
}
/**
* Checks if a numeric value is a decimal number (contains a fractional part).
*
* @param float|int $val The value to check.
* @return bool True if the value is numeric and has a decimal part, false otherwise.
*/
public function isDecimal(float|int $val): bool
{
if ((is_numeric($val)) && (floor($val) != $val)) {
return true;
} else {
return false;
}
}
/**
* ConvertsReturns athe number toof wordsfields (English formatcolumns).
*
* @paramin float|intthe $numresult Theset numberof toa convertquery.
* @return string The number in words.
*/
public function numberToWords(float|intUsage $num)Example: string
{
$ones = array(
1 => "one", 2 => "two", 3 => "three", 4 => "four", 5 => "five",
6 => "six", 7 => "seven", 8 => "eight", 9 => "nine", 10 => "ten",
11 => "eleven", 12 => "twelve", 13 => "thirteen", 14 => "fourteen",
15 => "fifteen", 16 => "sixteen", 17 => "seventeen", 18 => "eighteen",
19 => "nineteen"
);
$tens = array(
1 => "ten", 2 => "twenty", 3 => "thirty", 4 => "forty", 5 => "fifty",
6 => "sixty", 7 => "seventy", 8 => "eighty", 9 => "ninety"
);
$hundreds = array(
"hundred", "thousand", "million", "billion", "trillion", "quadrillion"
);
* $num_formatted$stmt = number_format$database->execute_query($num, 2,"SELECT "."id, "name,");
email $num_arrFROM =users explode(".",LIMIT $num_formatted1");
$wholenum = $num_arr[0];
$decnum = $num_arr[1];
* $whole_arr$numFields = array_reverse(explode(",", $wholenum));
krsort$database->get_num_fields($whole_arr$stmt);
$rettxt = "";
foreach ($whole_arr as $key => $i) {
$i_int = (int) $i; // Cast to int for array access
if ($i_int < 20) {
$rettxt .= $ones[$i_int];
} elseif ($i_int < 100) {
$rettxt .= $tens[intdiv($i_int, 10)];
$rettxt .= " " . $ones[$i_int %Output: 10];3
} else {*
* @param $rettxtPDOStatement|false .=$stmt $ones[intdiv($i_int,The 100)]PDOStatement .object "returned "by execute_query. $hundreds[0];
$rettxt .=* "@return "int .The $tens[intdiv(($i_intnumber %of 100)fields, 10)];
$rettxt .= " " . $ones[$i_int % 10];
}
if ($key >or 0) {
$rettxt .= " " . $hundreds[$key] . " ";
}
}
if ((int)$decnum > 0) {
statement $rettxtis invalid.= " and ";
$decnum_int = (int) $decnum;*/
public iffunction get_num_fields($decnum_int < 20$stmt) {
$rettxt .= $ones[$decnum_int];
} elseifif ($decnum_int$stmt <instanceof 100PDOStatement) {
$rettxt .= $tens[intdiv($decnum_int, 10)];
$rettxt .= " " . $ones[$decnum_int % 10];
}
}
return trim$stmt->columnCount($rettxt);
}
/**
* Converts a number to words in Indian currency format (Rupees and Paise).
*
* @param float|int $number The amount to convert.
* @return string The amount in words with HTML formatting.
*/
public function convertToIndianCurrency(float|int $number): string
{
$no = round($number);
$decimal = (int) round(($number - $no), 2) * 100;
$digits_length = strlen((string)$no);
$i =return 0;
$str = array();
$words = array(
0 => '', 1 => 'One', 2 => 'Two', 3 => 'Three', 4 => 'Four',
5 => 'Five', 6 => 'Six', 7 => 'Seven', 8 => 'Eight', 9 => 'Nine',
10 => 'Ten', 11 => 'Eleven', 12 => 'Twelve', 13 => 'Thirteen',
14 => 'Fourteen', 15 => 'Fifteen', 16 => 'Sixteen', 17 => 'Seventeen',
18 => 'Eighteen', 19 => 'Nineteen', 20 => 'Twenty', 30 => 'Thirty',
40 => 'Forty', 50 => 'Fifty', 60 => 'Sixty', 70 => 'Seventy',
80 => 'Eighty', 90 => 'Ninety'
);
$digits = array('', 'Hundred', 'Thousand', 'Lakh', 'Crore');
while ($i < $digits_length) {
$divider = ($i == 2) ? 10 : 100;
$num_segment = (int) floor($no % $divider);
$no = (int) floor($no / $divider);
$i += $divider == 10 ? 1 : 2;
if ($num_segment) {
$plural = ((count($str) > 0) && $num_segment > 9) ? 's' : '';
$current_word = '';
if ($num_segment < 21) {
$current_word = $words[$num_segment];
} else {
$current_word = $words[intdiv($num_segment, 10) * 10] . ' ' . $words[$num_segment % 10];
}
$str [] = trim($current_word . ' ' . $digits[count($str)] . $plural);
} else {
$str [] = '';
}
}
/**
* Returns $Rupeesan =array implode('of ',field array_reverse(array_filter($str))column);
names from the result set of a $paisequery.
= '';*
* Usage Example:
* $stmt if= $database->execute_query($decimal"SELECT >id, 0)name, {
email, status, created_at FROM users LIMIT 1");
* $fieldNames = $database->get_list_fields($stmt); $paise_word// =Output: '';
['id', 'name', 'email', 'status', 'created_at']
*
* @param PDOStatement|false $stmt ifThe ($decimalPDOStatement <object 21)returned {
by execute_query.
* @return array An array of column names, or an empty array if $paise_wordstatement =is $words[$decimal];invalid.
*/
public function get_list_fields($stmt) {
}$fieldNames else= {[];
if ($stmt instanceof PDOStatement) {
$paise_word =for $words[intdiv($decimal, 10)$i *= 10]0; .$i '< '$stmt->columnCount(); .$i++) $words[$decimal{
% 10];
$meta = }$stmt->getColumnMeta($i);
$paise$fieldNames[] = "And <font style='font-weight:500;'>" . trim($paise_word) . '</font> Paise ';$meta['name'];
}
$final_rupees_text = $Rupees ? 'Rupees <font style="font-weight:500;">' . $Rupees . '</font>' : '';
return trim($final_rupees_text . ' ' . $paise) . " Only";
}
/**
* Cleans a string by stripping slashes, decoding HTML entities, converting newlines to <br>, and URL decoding.
*
* @param string $data The string to clean.
* @return string The cleaned string.
*/
public function clean(string $data): string
{
$data = stripslashes($data);
$data = html_entity_decode($data, ENT_QUOTES, 'UTF-8');
$data = nl2br($data);
$data = urldecode($data);
return $data;
}
return $fieldNames;
}
} /* end of class DB */
db.php - Central DB and Utility Include Expecting your valuable suggestions on it.
<?php
declare(strict_types=1);
// This file is intended to be included at the top of every page
// to provide easy access to database and utility functionalities.
// Include all necessary core classes
require_once __DIR__ . '/DatabaseConfig.php';
require_once __DIR__ . '/DatabaseConnection.php';
require_once __DIR__ . '/QueryBuilder.php';
require_once __DIR__ . '/CrudOperations.php';
require_once __DIR__ . '/UtilityFunctions.php';
require_once __DIR__ . '/random-code-generators-pdo.php'; // Include if random functions are needed globally
// Global variables to hold our instantiated objects
// Using global for simplicity in small to medium applications.
// For larger applications, consider dependency injection or a service container.
global $dbConnection, $crud, $utils;
try {
// 1. Establish database connection
$dbConnection = new DatabaseConnection(
DB_HOST,
DB_NAME,
DB_USER,
DB_PASSWORD,
DB_CHARSET
);
// 2. Initialize CRUD operations with the PDO object
$crud = new CrudOperations($dbConnection->getPdo());
// 3. Initialize Utility functions
$utils = new UtilityFunctions();
} catch (Exception $e) {
// Centralized error handling for critical database connection issues.
// In a production environment, you would log this error and display a
// user-friendly message without exposing sensitive details.
error_log("Critical Application Error: " . $e->getMessage());
// You might redirect to an error page or display a generic message
die("<h1>Site is temporarily unavailable.</h1><p>We are experiencing technical difficulties. Please try again later.</p>");
}
// Now, on any page where you include 'db.php', you can directly use:
// $crud->getResults(...)
// $utils->dateFormat1(...)
// etc.
// Example:
// require_once 'db.php';
// $users = $crud->getResults('users');
// echo $utils->timeAgo($users[0]['created_at']);
?>