Add PHP 7.4+ type hints to helpers, models, and middleware

Added strict typing with parameter types, return types, and property
types across all core classes:
- helpers: Database, ErrorHandler, CacheHelper
- models: TicketModel, UserModel, WorkflowModel, TemplateModel, UserPreferencesModel
- middleware: RateLimitMiddleware, CsrfMiddleware, SecurityHeadersMiddleware

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-29 11:04:36 -05:00
parent 8a8b1b0258
commit 37be81b3e2
11 changed files with 118 additions and 119 deletions

View File

@@ -3,9 +3,9 @@
* TemplateModel - Handles ticket template operations
*/
class TemplateModel {
private $conn;
private mysqli $conn;
public function __construct($conn) {
public function __construct(mysqli $conn) {
$this->conn = $conn;
}
@@ -14,7 +14,7 @@ class TemplateModel {
*
* @return array Array of template records
*/
public function getAllTemplates() {
public function getAllTemplates(): array {
$sql = "SELECT * FROM ticket_templates WHERE is_active = TRUE ORDER BY template_name";
$result = $this->conn->query($sql);
@@ -31,7 +31,7 @@ class TemplateModel {
* @param int $templateId Template ID
* @return array|null Template record or null if not found
*/
public function getTemplateById($templateId) {
public function getTemplateById(int $templateId): ?array {
$sql = "SELECT * FROM ticket_templates WHERE template_id = ? AND is_active = TRUE";
$stmt = $this->conn->prepare($sql);
$stmt->bind_param("i", $templateId);
@@ -50,7 +50,7 @@ class TemplateModel {
* @param int $createdBy User ID creating the template
* @return bool Success status
*/
public function createTemplate($data, $createdBy) {
public function createTemplate(array $data, int $createdBy): bool {
$sql = "INSERT INTO ticket_templates (template_name, title_template, description_template,
category, type, default_priority, created_by)
VALUES (?, ?, ?, ?, ?, ?, ?)";
@@ -77,7 +77,7 @@ class TemplateModel {
* @param array $data Template data to update
* @return bool Success status
*/
public function updateTemplate($templateId, $data) {
public function updateTemplate(int $templateId, array $data): bool {
$sql = "UPDATE ticket_templates SET
template_name = ?,
title_template = ?,
@@ -108,7 +108,7 @@ class TemplateModel {
* @param int $templateId Template ID
* @return bool Success status
*/
public function deactivateTemplate($templateId) {
public function deactivateTemplate(int $templateId): bool {
$sql = "UPDATE ticket_templates SET is_active = FALSE WHERE template_id = ?";
$stmt = $this->conn->prepare($sql);
$stmt->bind_param("i", $templateId);

View File

@@ -1,12 +1,12 @@
<?php
class TicketModel {
private $conn;
public function __construct($conn) {
private mysqli $conn;
public function __construct(mysqli $conn) {
$this->conn = $conn;
}
public function getTicketById($id) {
public function getTicketById(int $id): ?array {
$sql = "SELECT t.*,
u_created.username as creator_username,
u_created.display_name as creator_display_name,
@@ -31,7 +31,7 @@ class TicketModel {
return $result->fetch_assoc();
}
public function getTicketComments($ticketId) {
public function getTicketComments(int $ticketId): array {
$sql = "SELECT * FROM ticket_comments WHERE ticket_id = ? ORDER BY created_at DESC";
$stmt = $this->conn->prepare($sql);
$stmt->bind_param("i", $ticketId);
@@ -46,7 +46,7 @@ class TicketModel {
return $comments;
}
public function getAllTickets($page = 1, $limit = 15, $status = 'Open', $sortColumn = 'ticket_id', $sortDirection = 'desc', $category = null, $type = null, $search = null, $filters = []) {
public function getAllTickets(int $page = 1, int $limit = 15, ?string $status = 'Open', string $sortColumn = 'ticket_id', string $sortDirection = 'desc', ?string $category = null, ?string $type = null, ?string $search = null, array $filters = []): array {
// Calculate offset
$offset = ($page - 1) * $limit;
@@ -222,7 +222,7 @@ class TicketModel {
];
}
public function updateTicket($ticketData, $updatedBy = null) {
public function updateTicket(array $ticketData, ?int $updatedBy = null): bool {
$sql = "UPDATE tickets SET
title = ?,
priority = ?,
@@ -257,7 +257,7 @@ class TicketModel {
return $result;
}
public function createTicket($ticketData, $createdBy = null) {
public function createTicket(array $ticketData, ?int $createdBy = null): array {
// Generate unique ticket ID (9-digit format with leading zeros)
// Loop until we find an ID that doesn't exist to prevent collisions
$maxAttempts = 10;
@@ -347,7 +347,7 @@ class TicketModel {
}
}
public function addComment($ticketId, $commentData) {
public function addComment(int $ticketId, array $commentData): array {
$sql = "INSERT INTO ticket_comments (ticket_id, user_name, comment_text, markdown_enabled)
VALUES (?, ?, ?, ?)";
@@ -387,7 +387,7 @@ class TicketModel {
* @param int $assignedBy User ID performing the assignment
* @return bool Success status
*/
public function assignTicket($ticketId, $userId, $assignedBy) {
public function assignTicket(int $ticketId, int $userId, int $assignedBy): bool {
$sql = "UPDATE tickets SET assigned_to = ?, updated_by = ?, updated_at = NOW() WHERE ticket_id = ?";
$stmt = $this->conn->prepare($sql);
$stmt->bind_param("iii", $userId, $assignedBy, $ticketId);
@@ -403,7 +403,7 @@ class TicketModel {
* @param int $updatedBy User ID performing the unassignment
* @return bool Success status
*/
public function unassignTicket($ticketId, $updatedBy) {
public function unassignTicket(int $ticketId, int $updatedBy): bool {
$sql = "UPDATE tickets SET assigned_to = NULL, updated_by = ?, updated_at = NOW() WHERE ticket_id = ?";
$stmt = $this->conn->prepare($sql);
$stmt->bind_param("ii", $updatedBy, $ticketId);
@@ -419,7 +419,7 @@ class TicketModel {
* @param array $ticketIds Array of ticket IDs
* @return array Associative array keyed by ticket_id
*/
public function getTicketsByIds($ticketIds) {
public function getTicketsByIds(array $ticketIds): array {
if (empty($ticketIds)) {
return [];
}
@@ -465,7 +465,7 @@ class TicketModel {
* @param array $user The user data (must include user_id, is_admin, groups)
* @return bool True if user can access the ticket
*/
public function canUserAccessTicket($ticket, $user) {
public function canUserAccessTicket(array $ticket, array $user): bool {
// Admins can access all tickets
if (!empty($user['is_admin'])) {
return true;
@@ -505,7 +505,7 @@ class TicketModel {
* @param array $user The current user
* @return array ['sql' => string, 'params' => array, 'types' => string]
*/
public function getVisibilityFilter($user) {
public function getVisibilityFilter(array $user): array {
// Admins see all tickets
if (!empty($user['is_admin'])) {
return ['sql' => '1=1', 'params' => [], 'types' => ''];
@@ -558,7 +558,7 @@ class TicketModel {
* @param int $updatedBy User ID
* @return bool
*/
public function updateVisibility($ticketId, $visibility, $visibilityGroups, $updatedBy) {
public function updateVisibility(int $ticketId, string $visibility, ?string $visibilityGroups, int $updatedBy): bool {
$allowedVisibilities = ['public', 'internal', 'confidential'];
if (!in_array($visibility, $allowedVisibilities)) {
$visibility = 'public';

View File

@@ -3,18 +3,18 @@
* UserModel - Handles user authentication and management
*/
class UserModel {
private $conn;
private static $userCache = []; // ['key' => ['data' => ..., 'expires' => timestamp]]
private static $cacheTTL = 300; // 5 minutes
private mysqli $conn;
private static array $userCache = []; // ['key' => ['data' => ..., 'expires' => timestamp]]
private static int $cacheTTL = 300; // 5 minutes
public function __construct($conn) {
public function __construct(mysqli $conn) {
$this->conn = $conn;
}
/**
* Get cached user data if not expired
*/
private static function getCached($key) {
private static function getCached(string $key): ?array {
if (isset(self::$userCache[$key])) {
$cached = self::$userCache[$key];
if ($cached['expires'] > time()) {
@@ -29,7 +29,7 @@ class UserModel {
/**
* Store user data in cache with expiration
*/
private static function setCached($key, $data) {
private static function setCached(string $key, array $data): void {
self::$userCache[$key] = [
'data' => $data,
'expires' => time() + self::$cacheTTL
@@ -39,7 +39,7 @@ class UserModel {
/**
* Invalidate specific user cache entry
*/
public static function invalidateCache($userId = null, $username = null) {
public static function invalidateCache(?int $userId = null, ?string $username = null): void {
if ($userId !== null) {
unset(self::$userCache["user_id_$userId"]);
}
@@ -57,7 +57,7 @@ class UserModel {
* @param string $groups Comma-separated groups from Remote-Groups header
* @return array User data array
*/
public function syncUserFromAuthelia($username, $displayName = '', $email = '', $groups = '') {
public function syncUserFromAuthelia(string $username, string $displayName = '', string $email = '', string $groups = ''): array {
// Check cache first
$cacheKey = "user_$username";
$cached = self::getCached($cacheKey);
@@ -122,7 +122,7 @@ class UserModel {
*
* @return array|null System user data or null if not found
*/
public function getSystemUser() {
public function getSystemUser(): ?array {
// Check cache first
$cached = self::getCached('system');
if ($cached !== null) {
@@ -150,7 +150,7 @@ class UserModel {
* @param int $userId User ID
* @return array|null User data or null if not found
*/
public function getUserById($userId) {
public function getUserById(int $userId): ?array {
// Check cache first
$cacheKey = "user_id_$userId";
$cached = self::getCached($cacheKey);
@@ -180,7 +180,7 @@ class UserModel {
* @param string $username Username
* @return array|null User data or null if not found
*/
public function getUserByUsername($username) {
public function getUserByUsername(string $username): ?array {
// Check cache first
$cacheKey = "user_$username";
$cached = self::getCached($cacheKey);
@@ -210,7 +210,7 @@ class UserModel {
* @param string $groups Comma-separated group names
* @return bool True if user is in admin group
*/
private function checkAdminStatus($groups) {
private function checkAdminStatus(string $groups): bool {
if (empty($groups)) {
return false;
}
@@ -226,7 +226,7 @@ class UserModel {
* @param array $user User data array
* @return bool True if user is admin
*/
public function isAdmin($user) {
public function isAdmin(array $user): bool {
return isset($user['is_admin']) && $user['is_admin'] == 1;
}
@@ -237,7 +237,7 @@ class UserModel {
* @param array $requiredGroups Array of required group names
* @return bool True if user is in at least one required group
*/
public function hasGroupAccess($user, $requiredGroups = ['admin', 'employee']) {
public function hasGroupAccess(array $user, array $requiredGroups = ['admin', 'employee']): bool {
if (empty($user['groups'])) {
return false;
}
@@ -253,7 +253,7 @@ class UserModel {
*
* @return array Array of user records
*/
public function getAllUsers() {
public function getAllUsers(): array {
$stmt = $this->conn->prepare("SELECT * FROM users ORDER BY created_at DESC");
$stmt->execute();
$result = $stmt->get_result();
@@ -273,7 +273,7 @@ class UserModel {
*
* @return array Array of unique group names
*/
public function getAllGroups() {
public function getAllGroups(): array {
$stmt = $this->conn->prepare("SELECT DISTINCT groups FROM users WHERE groups IS NOT NULL AND groups != ''");
$stmt->execute();
$result = $stmt->get_result();

View File

@@ -6,11 +6,11 @@
require_once dirname(__DIR__) . '/helpers/CacheHelper.php';
class UserPreferencesModel {
private $conn;
private static $CACHE_PREFIX = 'user_prefs';
private static $CACHE_TTL = 300; // 5 minutes
private mysqli $conn;
private static string $CACHE_PREFIX = 'user_prefs';
private static int $CACHE_TTL = 300; // 5 minutes
public function __construct($conn) {
public function __construct(mysqli $conn) {
$this->conn = $conn;
}
@@ -19,7 +19,7 @@ class UserPreferencesModel {
* @param int $userId User ID
* @return array Associative array of preference_key => preference_value
*/
public function getUserPreferences($userId) {
public function getUserPreferences(int $userId): array {
return CacheHelper::remember(self::$CACHE_PREFIX, $userId, function() use ($userId) {
$sql = "SELECT preference_key, preference_value
FROM user_preferences
@@ -45,7 +45,7 @@ class UserPreferencesModel {
* @param string $value Preference value
* @return bool Success status
*/
public function setPreference($userId, $key, $value) {
public function setPreference(int $userId, string $key, string $value): bool {
$sql = "INSERT INTO user_preferences (user_id, preference_key, preference_value)
VALUES (?, ?, ?)
ON DUPLICATE KEY UPDATE preference_value = VALUES(preference_value)";
@@ -69,7 +69,7 @@ class UserPreferencesModel {
* @param mixed $default Default value if preference doesn't exist
* @return mixed Preference value or default
*/
public function getPreference($userId, $key, $default = null) {
public function getPreference(int $userId, string $key, $default = null) {
$prefs = $this->getUserPreferences($userId);
return $prefs[$key] ?? $default;
}
@@ -80,7 +80,7 @@ class UserPreferencesModel {
* @param string $key Preference key
* @return bool Success status
*/
public function deletePreference($userId, $key) {
public function deletePreference(int $userId, string $key): bool {
$sql = "DELETE FROM user_preferences
WHERE user_id = ? AND preference_key = ?";
$stmt = $this->conn->prepare($sql);
@@ -101,7 +101,7 @@ class UserPreferencesModel {
* @param int $userId User ID
* @return bool Success status
*/
public function deleteAllPreferences($userId) {
public function deleteAllPreferences(int $userId): bool {
$sql = "DELETE FROM user_preferences WHERE user_id = ?";
$stmt = $this->conn->prepare($sql);
$stmt->bind_param("i", $userId);
@@ -119,7 +119,7 @@ class UserPreferencesModel {
/**
* Clear all user preferences cache
*/
public static function clearCache() {
public static function clearCache(): void {
CacheHelper::delete(self::$CACHE_PREFIX);
}
}

View File

@@ -7,11 +7,11 @@
require_once dirname(__DIR__) . '/helpers/CacheHelper.php';
class WorkflowModel {
private $conn;
private static $CACHE_PREFIX = 'workflow';
private static $CACHE_TTL = 600; // 10 minutes
private mysqli $conn;
private static string $CACHE_PREFIX = 'workflow';
private static int $CACHE_TTL = 600; // 10 minutes
public function __construct($conn) {
public function __construct(mysqli $conn) {
$this->conn = $conn;
}
@@ -20,7 +20,7 @@ class WorkflowModel {
*
* @return array All active transitions indexed by from_status
*/
private function getAllTransitions() {
private function getAllTransitions(): array {
return CacheHelper::remember(self::$CACHE_PREFIX, 'all_transitions', function() {
$sql = "SELECT from_status, to_status, requires_comment, requires_admin
FROM status_transitions
@@ -50,7 +50,7 @@ class WorkflowModel {
* @param string $currentStatus Current ticket status
* @return array Array of allowed transitions with requirements
*/
public function getAllowedTransitions($currentStatus) {
public function getAllowedTransitions(string $currentStatus): array {
$allTransitions = $this->getAllTransitions();
if (!isset($allTransitions[$currentStatus])) {
@@ -68,7 +68,7 @@ class WorkflowModel {
* @param bool $isAdmin Whether user is admin
* @return bool True if transition is allowed
*/
public function isTransitionAllowed($fromStatus, $toStatus, $isAdmin = false) {
public function isTransitionAllowed(string $fromStatus, string $toStatus, bool $isAdmin = false): bool {
// Allow same status (no change)
if ($fromStatus === $toStatus) {
return true;
@@ -94,7 +94,7 @@ class WorkflowModel {
*
* @return array Array of unique status values
*/
public function getAllStatuses() {
public function getAllStatuses(): array {
return CacheHelper::remember(self::$CACHE_PREFIX, 'all_statuses', function() {
$sql = "SELECT DISTINCT from_status as status FROM status_transitions
UNION
@@ -118,7 +118,7 @@ class WorkflowModel {
* @param string $toStatus Desired status
* @return array|null Transition requirements or null if not found
*/
public function getTransitionRequirements($fromStatus, $toStatus) {
public function getTransitionRequirements(string $fromStatus, string $toStatus): ?array {
$allTransitions = $this->getAllTransitions();
if (!isset($allTransitions[$fromStatus][$toStatus])) {
@@ -135,7 +135,7 @@ class WorkflowModel {
/**
* Clear workflow cache (call when transitions are modified)
*/
public static function clearCache() {
public static function clearCache(): void {
CacheHelper::delete(self::$CACHE_PREFIX);
}
}