style: auto-fix 1340 phpcs PSR-12 violations via phpcbf; exclude MissingNamespace and SideEffects
Lint / PHP (phpcs PSR-12) (push) Failing after 29s
Lint / JS (eslint) (push) Successful in 12s

This commit is contained in:
2026-04-13 20:56:10 -04:00
parent b6df647921
commit c90bdc8ac8
80 changed files with 1674 additions and 1092 deletions
+47 -27
View File
@@ -1,12 +1,16 @@
<?php
class TicketModel {
class TicketModel
{
private mysqli $conn;
public function __construct(mysqli $conn) {
public function __construct(mysqli $conn)
{
$this->conn = $conn;
}
public function getTicketById(int $id): ?array {
public function getTicketById(int $id): ?array
{
$sql = "SELECT t.*,
u_created.username as creator_username,
u_created.display_name as creator_display_name,
@@ -30,8 +34,9 @@ class TicketModel {
return $result->fetch_assoc();
}
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 $user = null): array {
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 $user = null): array
{
// Calculate offset
$offset = ($page - 1) * $limit;
@@ -162,12 +167,12 @@ class TicketModel {
$paramTypes .= 'i';
}
}
$whereClause = '';
if (!empty($whereConditions)) {
$whereClause = 'WHERE ' . implode(' AND ', $whereConditions);
}
// Validate sort column to prevent SQL injection
$allowedColumns = ['ticket_id', 'title', 'status', 'priority', 'category', 'type', 'created_at', 'updated_at', 'created_by', 'assigned_to'];
if (!in_array($sortColumn, $allowedColumns)) {
@@ -230,7 +235,7 @@ class TicketModel {
'current_page' => $page
];
}
/**
* Update a ticket with optional optimistic locking
*
@@ -239,7 +244,8 @@ class TicketModel {
* @param string|null $expectedUpdatedAt If provided, update will fail if ticket was modified since this timestamp
* @return array ['success' => bool, 'error' => string|null, 'conflict' => bool]
*/
public function updateTicket(array $ticketData, ?int $updatedBy = null, ?string $expectedUpdatedAt = null): array {
public function updateTicket(array $ticketData, ?int $updatedBy = null, ?string $expectedUpdatedAt = null): array
{
// closed_at: set on close (preserve if already set), clear on reopen
$closedAtClause = "closed_at = CASE WHEN ? = 'Closed' THEN COALESCE(closed_at, NOW()) ELSE NULL END";
@@ -332,8 +338,9 @@ class TicketModel {
return ['success' => true, 'error' => null, 'conflict' => false];
}
public function createTicket(array $ticketData, ?int $createdBy = null): array {
public function createTicket(array $ticketData, ?int $createdBy = null): array
{
// Generate unique ticket ID (9-digit format with leading zeros)
// Uses cryptographically secure random numbers for better distribution
// Includes exponential backoff and fallback for reliability under high load
@@ -486,16 +493,17 @@ class TicketModel {
}
}
public function addComment(int $ticketId, array $commentData): array {
public function addComment(int $ticketId, array $commentData): array
{
$sql = "INSERT INTO ticket_comments (ticket_id, user_name, comment_text, markdown_enabled)
VALUES (?, ?, ?, ?)";
$stmt = $this->conn->prepare($sql);
// Set default username
$username = $commentData['user_name'] ?? 'User';
$markdownEnabled = $commentData['markdown_enabled'] ? 1 : 0;
$stmt->bind_param(
"issi",
$ticketId,
@@ -503,7 +511,7 @@ class TicketModel {
$commentData['comment_text'],
$markdownEnabled
);
if ($stmt->execute()) {
return [
'success' => true,
@@ -526,7 +534,8 @@ class TicketModel {
* @param int $assignedBy User ID performing the assignment
* @return bool Success status
*/
public function assignTicket(int $ticketId, int $userId, int $assignedBy): bool {
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);
@@ -542,7 +551,8 @@ class TicketModel {
* @param int $updatedBy User ID performing the unassignment
* @return bool Success status
*/
public function unassignTicket(int $ticketId, int $updatedBy): bool {
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);
@@ -558,7 +568,8 @@ class TicketModel {
* @param array $ticketIds Array of ticket IDs
* @return array Associative array keyed by ticket_id
*/
public function getTicketsByIds(array $ticketIds): array {
public function getTicketsByIds(array $ticketIds): array
{
if (empty($ticketIds)) {
return [];
}
@@ -604,7 +615,8 @@ 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(array $ticket, array $user): bool {
public function canUserAccessTicket(array $ticket, array $user): bool
{
// Admins can access all tickets
if (!empty($user['is_admin'])) {
return true;
@@ -644,7 +656,8 @@ class TicketModel {
* @param array $user The current user
* @return array ['sql' => string, 'params' => array, 'types' => string]
*/
public function getVisibilityFilter(array $user): array {
public function getVisibilityFilter(array $user): array
{
// Admins see all tickets
if (!empty($user['is_admin'])) {
return ['sql' => '1=1', 'params' => [], 'types' => ''];
@@ -697,7 +710,8 @@ class TicketModel {
* @param int $updatedBy User ID
* @return bool
*/
public function updateVisibility(int $ticketId, string $visibility, ?string $visibilityGroups, int $updatedBy): bool {
public function updateVisibility(int $ticketId, string $visibility, ?string $visibilityGroups, int $updatedBy): bool
{
$allowedVisibilities = ['public', 'internal', 'confidential'];
if (!in_array($visibility, $allowedVisibilities)) {
$visibility = 'public';
@@ -728,7 +742,8 @@ class TicketModel {
* @param string $ticketId Ticket ID
* @return bool Success status
*/
public function deleteTicket(string $ticketId): bool {
public function deleteTicket(string $ticketId): bool
{
// Collect attachment filenames before deleting DB rows
$attachmentFiles = [];
$attStmt = $this->conn->prepare("SELECT filename FROM ticket_attachments WHERE ticket_id = ?");
@@ -754,7 +769,9 @@ class TicketModel {
foreach ($children as $sql) {
try {
$stmt = $this->conn->prepare($sql);
if (!$stmt) continue;
if (!$stmt) {
continue;
}
// ticket_dependencies uses two placeholders
if (strpos($sql, 'depends_on_id') !== false) {
$stmt->bind_param('ss', $ticketId, $ticketId);
@@ -772,7 +789,9 @@ class TicketModel {
}
$stmt = $this->conn->prepare("DELETE FROM tickets WHERE ticket_id = ?");
if (!$stmt) return false;
if (!$stmt) {
return false;
}
$stmt->bind_param('s', $ticketId);
$result = $stmt->execute();
$affected = $stmt->affected_rows;
@@ -802,7 +821,8 @@ class TicketModel {
* Check whether the FULLTEXT index on tickets(title, description) exists.
* Result is cached for the process lifetime (static).
*/
private function hasFulltextIndex(): bool {
private function hasFulltextIndex(): bool
{
static $result = null;
if ($result !== null) {
return $result;
@@ -817,4 +837,4 @@ class TicketModel {
$result = $r && (int)$r->fetch_assoc()['cnt'] > 0;
return $result;
}
}
}