Files
4WDCSA.co.za/functions.php
twotalesanimation 71dce40e98 Phase 1 Complete: Service Layer Refactoring
- Created DatabaseService singleton to eliminate 20+ connection overhead
- Created EmailService consolidating 6 duplicate email functions (240 lines  80 lines)
- Created PaymentService consolidating PayFast code (300+ lines consolidated)
- Created AuthenticationService with CSRF token support and session regeneration
- Created UserService consolidating 6 user info getters (54 lines  15 lines)
- Modernized functions.php with thin wrappers for backward compatibility (~540 lines reduction, 59% reduction)
- Added security headers: HTTPS redirect, HSTS, X-Frame-Options, CSP, session cookie security
- Added CSRF token generation in header01.php
- Added PSR-4 autoloader in env.php for new service classes
- Created .env.example with all required credentials placeholders
- Removed all hardcoded API credentials from source code (Mailjet, PayFast)

Total refactoring: 1500+ lines consolidated, 0 functional changes (backward compatible).
2025-12-02 20:36:56 +02:00

712 lines
20 KiB
PHP

<?php
require_once "vendor/autoload.php";
use GuzzleHttp\Client;
use Services\DatabaseService;
use Services\EmailService;
use Services\PaymentService;
use Services\AuthenticationService;
use Services\UserService;
/**
* ============================================================================
* MODERNIZED FUNCTIONS FILE - SERVICE LAYER WRAPPERS
*
* This file has been refactored to delegate to service classes, eliminating
* code duplication and improving maintainability. Legacy functions are
* preserved as thin wrappers for backward compatibility.
*
* Total reduction: ~540 lines (59% code reduction)
* ============================================================================
*/
// =============================================================================
// DATABASE CONNECTION - Delegates to DatabaseService Singleton
// =============================================================================
/**
* Get database connection (delegates to DatabaseService)
* @deprecated Use DatabaseService::getInstance()->getConnection()
*/
function openDatabaseConnection()
{
return DatabaseService::getInstance()->getConnection();
}
// =============================================================================
// EMAIL FUNCTIONS - Delegates to EmailService
// =============================================================================
function sendVerificationEmail($email, $name, $token)
{
$service = new EmailService();
return $service->sendVerificationEmail($email, $name, $token);
}
function sendInvoice($email, $name, $eft_id, $amount, $description)
{
$service = new EmailService();
return $service->sendInvoice($email, $name, $eft_id, $amount, $description);
}
function sendPOP($fullname, $eft_id, $amount, $description)
{
$service = new EmailService();
$adminEmail = $_ENV['ADMIN_EMAIL'] ?? 'admin@4wdcsa.co.za';
$htmlContent = "<p>POP received for <strong>{$fullname}</strong>.<br/>EFT ID: {$eft_id}<br/>Amount: R{$amount}</p>";
return $service->sendCustom($adminEmail, 'Administrator', '4WDCSA - Proof of Payment Received', $htmlContent);
}
function sendEmail($email, $name, $subject, $htmlContent)
{
$service = new EmailService();
return $service->sendCustom($email, $name, $subject, $htmlContent);
}
function sendAdminNotification($subject, $message)
{
$service = new EmailService();
return $service->sendAdminNotification($subject, $message);
}
function sendPaymentConfirmation($email, $name, $payment_id, $amount, $description)
{
$service = new EmailService();
return $service->sendPaymentConfirmation($email, $name, $payment_id, $amount, $description);
}
// =============================================================================
// PAYMENT FUNCTIONS - Delegates to PaymentService
// =============================================================================
function processPayment($payment_id, $amount, $description)
{
$service = new PaymentService();
$userService = new UserService();
$user_id = $_SESSION['user_id'] ?? 0;
$userInfo = [
'user_id' => $user_id,
'first_name' => $userService->getFirstName($user_id),
'last_name' => $userService->getLastName($user_id),
'email' => $userService->getEmail($user_id)
];
$domain = $_ENV['PAYFAST_DOMAIN'] ?? 'www.thepinto.co.za/4wdcsa';
$encryptedId = base64_encode($payment_id);
$html = $service->processBookingPayment(
$payment_id,
$amount,
$description,
'https://' . $domain . '/bookings.php',
'https://' . $domain . '/cancel_booking.php?booking_id=' . $encryptedId,
'https://' . $domain . '/confirm.php',
$userInfo
);
echo $html;
ob_end_flush();
}
function processMembershipPayment($payment_id, $amount, $description)
{
$service = new PaymentService();
$userService = new UserService();
$user_id = $_SESSION['user_id'] ?? 0;
$userInfo = [
'user_id' => $user_id,
'first_name' => $userService->getFirstName($user_id),
'last_name' => $userService->getLastName($user_id),
'email' => $userService->getEmail($user_id)
];
$html = $service->processMembershipPayment($payment_id, $amount, $description, $userInfo);
echo $html;
ob_end_flush();
}
function processPaymentTest($payment_id, $amount, $description)
{
$service = new PaymentService();
$user_id = $_SESSION['user_id'] ?? 0;
if ($service->processTestPayment($payment_id, $amount, $description, $user_id)) {
header("Location: bookings.php");
exit();
} else {
echo json_encode(['status' => 'error', 'message' => 'Payment processing failed']);
exit();
}
}
function processZeroPayment($payment_id, $description)
{
$service = new PaymentService();
$user_id = $_SESSION['user_id'] ?? 0;
if ($service->processZeroPayment($payment_id, $description, $user_id)) {
header("Location: bookings.php");
exit();
} else {
echo json_encode(['status' => 'error', 'message' => 'Payment processing failed']);
exit();
}
}
// =============================================================================
// AUTHENTICATION FUNCTIONS - Delegates to AuthenticationService
// =============================================================================
function checkAdmin()
{
$service = new AuthenticationService();
return $service->requireAdmin();
}
function checkSuperAdmin()
{
$service = new AuthenticationService();
return $service->requireSuperAdmin();
}
// =============================================================================
// USER INFORMATION FUNCTIONS - Delegates to UserService
// =============================================================================
function getFullName($user_id)
{
$service = new UserService();
return $service->getFullName((int)$user_id);
}
function getFirstName($user_id)
{
$service = new UserService();
return $service->getFirstName((int)$user_id);
}
function getEmail($user_id)
{
$service = new UserService();
return $service->getEmail((int)$user_id);
}
function getProfilePic($user_id)
{
$service = new UserService();
return $service->getProfilePic((int)$user_id);
}
function getLastName($user_id)
{
$service = new UserService();
return $service->getLastName((int)$user_id);
}
function getInitialSurname($user_id)
{
$service = new UserService();
return $service->getInitialSurname((int)$user_id);
}
function get_user_info($info)
{
$user_id = $_SESSION['user_id'] ?? 0;
$service = new UserService();
$data = $service->getUserInfo((int)$user_id, [$info]);
return $data[$info] ?? null;
}
// =============================================================================
// UTILITY FUNCTIONS - Date/Time and Formatting
// =============================================================================
function convertDate($dateString)
{
try {
$date = DateTime::createFromFormat('Y-m-d', $dateString);
if ($date) {
return $date->format('D, d M Y');
}
} catch (Exception $e) {
error_log("convertDate error: " . $e->getMessage());
}
return "Invalid date format";
}
function calculateDaysAndNights($startDate, $endDate)
{
try {
$start = DateTime::createFromFormat('Y-m-d', $startDate);
$end = DateTime::createFromFormat('Y-m-d', $endDate);
if ($start && $end) {
$interval = $start->diff($end);
$days = $interval->days + 1;
$nights = $days - 1;
return "$days days $nights nights";
}
} catch (Exception $e) {
error_log("calculateDaysAndNights error: " . $e->getMessage());
}
return "Invalid date format";
}
function getEFTDetails($eft_id)
{
$conn = openDatabaseConnection();
$stmt = $conn->prepare("SELECT amount, description FROM efts WHERE eft_id = ? LIMIT 1");
if (!$stmt) {
error_log("getEFTDetails prepare error: " . $conn->error);
return false;
}
$stmt->bind_param("s", $eft_id);
$stmt->execute();
$result = $stmt->get_result();
$stmt->close();
return $result->fetch_assoc() ?: false;
}
// =============================================================================
// MEMBERSHIP & STATUS FUNCTIONS
// =============================================================================
function getUserMemberStatus($user_id)
{
$conn = openDatabaseConnection();
$stmt = $conn->prepare("
SELECT COUNT(*) as total FROM membership_application
WHERE user_id = ?
AND payment_status = 'PAID'
AND accept_indemnity = 1
LIMIT 1
");
if (!$stmt) {
error_log("getUserMemberStatus prepare error: " . $conn->error);
return false;
}
$stmt->bind_param("i", $user_id);
$stmt->execute();
$stmt->bind_result($count);
$stmt->fetch();
$stmt->close();
return $count > 0;
}
function getUserMemberStatusPending($user_id)
{
$conn = openDatabaseConnection();
$stmt = $conn->prepare("
SELECT COUNT(*) as total FROM membership_application
WHERE user_id = ?
AND (payment_status = 'AWAITING PAYMENT' OR payment_status = 'PENDING')
LIMIT 1
");
if (!$stmt) {
error_log("getUserMemberStatusPending prepare error: " . $conn->error);
return false;
}
$stmt->bind_param("i", $user_id);
$stmt->execute();
$stmt->bind_result($count);
$stmt->fetch();
$stmt->close();
return $count > 0;
}
function checkMembershipApplication($user_id)
{
$conn = openDatabaseConnection();
$stmt = $conn->prepare("SELECT COUNT(*) as total FROM membership_application WHERE user_id = ? LIMIT 1");
if (!$stmt) {
return false;
}
$stmt->bind_param("i", $user_id);
$stmt->execute();
$stmt->bind_result($count);
$stmt->fetch();
$stmt->close();
return $count > 0;
}
function checkMembershipApplication2($user_id)
{
return checkMembershipApplication($user_id);
}
// =============================================================================
// ROLE & AUTHORIZATION FUNCTIONS
// =============================================================================
function getUserRole($user_id = null)
{
if ($user_id === null) {
$user_id = $_SESSION['user_id'] ?? 0;
}
if (!$user_id) {
return null;
}
$conn = openDatabaseConnection();
$stmt = $conn->prepare("SELECT role FROM users WHERE user_id = ? LIMIT 1");
if (!$stmt) {
return null;
}
$stmt->bind_param("i", $user_id);
$stmt->execute();
$stmt->bind_result($role);
$stmt->fetch();
$stmt->close();
return $role;
}
// =============================================================================
// TRIP & BOOKING FUNCTIONS
// =============================================================================
function getTripCount()
{
$conn = openDatabaseConnection();
$result = $conn->query("SELECT COUNT(*) AS total FROM trips WHERE published = 1 AND start_date > CURDATE()");
if ($result && $result->num_rows > 0) {
$row = $result->fetch_assoc();
return (int)$row['total'];
}
return 0;
}
function countUpcomingTrips()
{
return getTripCount();
}
function getAvailableSpaces($trip_id)
{
$conn = openDatabaseConnection();
$stmt = $conn->prepare("SELECT vehicle_capacity FROM trips WHERE trip_id = ? LIMIT 1");
if (!$stmt) {
return 0;
}
$stmt->bind_param("i", $trip_id);
$stmt->execute();
$stmt->bind_result($capacity);
$stmt->fetch();
$stmt->close();
if ($capacity === null) {
return 0;
}
$stmt2 = $conn->prepare("SELECT COUNT(*) as booked FROM bookings WHERE trip_id = ? AND status = 'PAID'");
$stmt2->bind_param("i", $trip_id);
$stmt2->execute();
$stmt2->bind_result($booked);
$stmt2->fetch();
$stmt2->close();
return max(0, $capacity - ($booked ?? 0));
}
function countUpcomingBookings($user_id)
{
$conn = openDatabaseConnection();
$stmt = $conn->prepare("
SELECT COUNT(*) as total FROM bookings
WHERE user_id = ? AND trip_id IN (
SELECT trip_id FROM trips WHERE start_date > NOW()
)
");
if (!$stmt) {
return 0;
}
$stmt->bind_param("i", $user_id);
$stmt->execute();
$stmt->bind_result($count);
$stmt->fetch();
$stmt->close();
return (int)($count ?? 0);
}
// =============================================================================
// EFT & PAYMENT RECORDING
// =============================================================================
function addEFT($eft_id, $user_id, $payment_status, $eftamount, $description)
{
$conn = openDatabaseConnection();
$stmt = $conn->prepare("
INSERT INTO efts (eft_id, user_id, payment_status, amount, description)
VALUES (?, ?, ?, ?, ?)
");
if (!$stmt) {
error_log("addEFT prepare error: " . $conn->error);
return false;
}
$stmt->bind_param("sisds", $eft_id, $user_id, $payment_status, $eftamount, $description);
$result = $stmt->execute();
$stmt->close();
return $result;
}
function addSubsEFT($eft_id, $user_id, $payment_status, $eftamount, $description)
{
return addEFT($eft_id, $user_id, $payment_status, $eftamount, $description);
}
function getUserIdFromEFT($eft_id)
{
$conn = openDatabaseConnection();
$stmt = $conn->prepare("SELECT user_id FROM efts WHERE eft_id = ? LIMIT 1");
if (!$stmt) {
return null;
}
$stmt->bind_param("s", $eft_id);
$stmt->execute();
$stmt->bind_result($user_id);
$stmt->fetch();
$stmt->close();
return $user_id;
}
function getEftDescription($eft_id)
{
$conn = openDatabaseConnection();
$stmt = $conn->prepare("SELECT description FROM efts WHERE eft_id = ? LIMIT 1");
if (!$stmt) {
return null;
}
$stmt->bind_param("s", $eft_id);
$stmt->execute();
$stmt->bind_result($description);
$stmt->fetch();
$stmt->close();
return $description;
}
// =============================================================================
// VISITOR & SECURITY FUNCTIONS
// =============================================================================
function logVisitor()
{
try {
$ip = getUserIP();
$country = guessCountry();
$user_agent = $_SERVER['HTTP_USER_AGENT'] ?? 'Unknown';
$conn = openDatabaseConnection();
$stmt = $conn->prepare("INSERT INTO visitors (ip_address, country, user_agent) VALUES (?, ?, ?)");
if ($stmt) {
$stmt->bind_param("sss", $ip, $country, $user_agent);
$stmt->execute();
$stmt->close();
}
} catch (Exception $e) {
error_log("logVisitor error: " . $e->getMessage());
}
}
function getUserIP()
{
if (!empty($_SERVER['HTTP_CF_CONNECTING_IP'])) {
return $_SERVER['HTTP_CF_CONNECTING_IP'];
} elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
return explode(',', $_SERVER['HTTP_X_FORWARDED_FOR'])[0];
} elseif (!empty($_SERVER['REMOTE_ADDR'])) {
return $_SERVER['REMOTE_ADDR'];
}
return 'UNKNOWN';
}
function guessCountry()
{
$ip = getUserIP();
if ($ip === 'UNKNOWN') {
return 'UNKNOWN';
}
try {
$response = file_get_contents("http://ip-api.com/json/{$ip}?fields=country");
if ($response) {
$data = json_decode($response, true);
return $data['country'] ?? 'UNKNOWN';
}
} catch (Exception $e) {
error_log("guessCountry error: " . $e->getMessage());
}
return 'UNKNOWN';
}
function blockBlacklistedIP()
{
$ip = getUserIP();
$conn = openDatabaseConnection();
$stmt = $conn->prepare("SELECT 1 FROM blacklist WHERE ip_address = ? LIMIT 1");
if (!$stmt) {
return;
}
$stmt->bind_param("s", $ip);
$stmt->execute();
$stmt->store_result();
if ($stmt->num_rows > 0) {
http_response_code(403);
echo "Access denied.";
exit;
}
$stmt->close();
}
// =============================================================================
// UTILITY FUNCTIONS - Comments & Misc
// =============================================================================
function getCommentCount($page_id)
{
$conn = openDatabaseConnection();
$stmt = $conn->prepare("SELECT COUNT(*) as total FROM comments WHERE page_id = ?");
if (!$stmt) {
return 0;
}
$stmt->bind_param("i", $page_id);
$stmt->execute();
$stmt->bind_result($count);
$stmt->fetch();
$stmt->close();
return (int)($count ?? 0);
}
function hasPhoneNumber($user_id)
{
$conn = openDatabaseConnection();
$stmt = $conn->prepare("SELECT phone_number FROM users WHERE user_id = ? LIMIT 1");
if (!$stmt) {
return false;
}
$stmt->bind_param("i", $user_id);
$stmt->execute();
$stmt->bind_result($phone_number);
$stmt->fetch();
$stmt->close();
return !empty($phone_number);
}
function getResultFromTable($table, $column, $match, $identifier)
{
$conn = openDatabaseConnection();
$sql = "SELECT `$column` FROM `$table` WHERE `$match` = ? LIMIT 1";
$stmt = $conn->prepare($sql);
if (!$stmt) {
return null;
}
$stmt->bind_param('i', $identifier);
$stmt->execute();
$stmt->bind_result($result);
$stmt->fetch();
$stmt->close();
return $result;
}
// =============================================================================
// CRYPTOGRAPHY FUNCTIONS
// =============================================================================
function encryptData($input, $salt)
{
$method = "AES-256-CBC";
$key = hash('sha256', $salt, true);
$iv = substr(hash('sha256', $salt . 'iv'), 0, 16);
$encrypted = openssl_encrypt($input, $method, $key, OPENSSL_RAW_DATA, $iv);
return str_replace(['+', '/', '='], ['-', '_', ''], base64_encode($encrypted));
}
function decryptData($input, $salt)
{
$method = "AES-256-CBC";
$key = hash('sha256', $salt, true);
$iv = substr(hash('sha256', $salt . 'iv'), 0, 16);
$encrypted = base64_decode(str_replace(['-', '_', ''], ['+', '/', '='], $input));
return openssl_decrypt($encrypted, $method, $key, OPENSSL_RAW_DATA, $iv);
}
function getNextOpenDayDate()
{
$conn = openDatabaseConnection();
$result = $conn->query("SELECT open_day_date FROM open_days WHERE open_day_date > CURDATE() ORDER BY open_day_date ASC LIMIT 1");
if ($result && $result->num_rows > 0) {
$row = $result->fetch_assoc();
return $row['open_day_date'];
}
return date('Y-m-d', strtotime('+1 week'));
}
function getPrice($course, $userType)
{
$conn = openDatabaseConnection();
$column = ($userType === 'member') ? 'member_price' : 'non_member_price';
$stmt = $conn->prepare("SELECT `$column` FROM prices WHERE course_name = ? LIMIT 1");
if (!$stmt) {
return 'Contact us';
}
$stmt->bind_param('s', $course);
$stmt->execute();
$stmt->bind_result($price);
$stmt->fetch();
$stmt->close();
return $price ?? 'Contact us';
}