- Created DatabaseService.php with full OOP database abstraction layer
- Methods: select(), selectOne(), insert(), update(), delete(), execute(), count(), exists()
- Transaction support: beginTransaction(), commit(), rollback()
- Error handling: getLastError(), getLastQuery() for debugging
- Type-safe parameter binding with prepared statements
- Updated connection.php to initialize $db service
- Available globally as $db variable after connection.php include
- Foundation for migrating from procedural $conn queries
- create_bar_tab.php: Replaced mysqli_real_escape_string() and procedural mysqli_query/mysqli_num_rows/mysqli_error with OOP prepared statements
- submit_order.php: Replaced mysqli_real_escape_string() and procedural mysqli_query/mysqli_error with OOP prepared statements
- fetch_drinks.php: Replaced mysqli_real_escape_string() and procedural mysqli_query/mysqli_fetch_assoc with OOP prepared statements
- comment_box.php: Removed mysqli_real_escape_string(), added CSRF token validation for comment submission
All files now use consistent OOP MySQLi approach with proper parameter binding. Fixes PHP 8.1+ compatibility and improves security against multi-byte character injection.
Converted queries in:
- functions.php:
* getTripCount() - Hardcoded query
* getAvailableSpaces() - Two queries using $trip_id parameter (HIGH PRIORITY)
- blog.php:
* Main blog list query - Hardcoded 'published' status
- course_details.php:
* Driver training courses query - Hardcoded course type
- driver_training.php:
* Future driver training dates query - Hardcoded course type
- events.php:
* Upcoming events query - Hardcoded date comparison
- index.php:
* Featured trips query - Hardcoded published status
All queries now use proper parameter binding via prepared statements.
Next: Convert remaining 15+ safe hardcoded queries for consistency.
- Updated 39 pages from old header01.php and header02.php includes
- All pages now use single configurable header.php with $headerStyle variable
- Light style (default): Most pages (login, register, trips, courses, etc.)
- Dark style: Coming from header01 original usage
Pages updated:
- Admin pages: admin_*.php (10 files)
- Booking pages: bookings.php, campsite_booking.php, etc.
- Content pages: blog.php, blog_details.php, contact.php, events.php, etc.
- User pages: account_settings.php, membership*.php, register.php, etc.
- Utility pages: 404.php, payment_confirmation.php, reset_password.php, etc.
All pages now maintain single header template source - easier to update navigation, styles, and functionality across the entire site.
- index.php: Changed from header01.php to new unified header.php with dark style
- about.php: Changed from header02.php to new unified header.php with light style
- Both pages now use single configurable header template
- Eliminates dependency on separate header files
Test these pages in browser to verify header renders correctly before updating remaining pages
- Single source of truth for header code (consolidates header01.php and header02.php)
- Configurable styling via $headerStyle variable ('dark' or 'light')
- Conditional CSS and asset loading based on style
- Logo and text colors automatically switch based on style
- Eliminates 95% code duplication between two header files
- JavaScript consolidated for profile menu and dropdowns
- Navigation menu maintained in one place for easier updates
Usage:
$headerStyle = 'dark'; // Dark header with white text
require_once("header.php");
OR
$headerStyle = 'light'; // Light header with dark text
require_once("header.php");
Next: Update all pages from header01.php/header02.php to use this new template
Changed countRecentFailedAttempts() to use MySQL DATE_SUB(NOW(), INTERVAL ? MINUTE)
instead of PHP-calculated cutoff time. This ensures consistent datetime comparison
on the database server without timezone mismatches between PHP and MySQL.
This fixes the issue where the AND attempted_at condition was filtering out all
recent attempts due to timestamp comparison inconsistencies.
The countRecentFailedAttempts() function was requiring BOTH email AND ip_address to match, which caused failed attempts from different IPs to not count together. This prevented account lockout from working properly.
Changed to count failed attempts by email only. IP address is still recorded for audit purposes but doesn't affect the failed attempt count.
This ensures:
- Failed attempts accumulate correctly regardless of IP changes
- Accounts lock after 5 failed attempts within 15 minutes
- Prevents attackers from bypassing by changing IP
Updated forms with hidden CSRF token fields:
- comment_box.php - Comment form
- course_details.php - Course booking form
- campsites.php - Campsite addition modal form
- bar_tabs.php - Bar tab creation modal form
- membership_application.php - Membership application form
Updated backend processors with CSRF validation:
- create_bar_tab.php - Bar tab AJAX processor
- add_campsite.php - Campsite form processor
- submit_order.php - Order submission processor
All forms now require validated CSRF tokens before processing, preventing cross-site request forgery attacks.