Add strict types and type hints across API
Apply strict_types and extensive type declarations throughout the API and models, improving type safety and error handling. Key changes: add declare(strict_types=1) to many files; convert properties, method parameters and return values to typed signatures (PDO, arrays, ints, strings, bools, nullables); switch exception handling to Throwable in index and Router; improve Router, controllers and model method signatures and nullability handling; refine file/image serving security checks and headers in ImageController; strengthen Database typing and initialization methods; return explicit types from BaseModel CRUD helpers and counting; update Media/Cast/Adult/Game/Console/Settings controllers and models to use typed methods, better validation, and clearer update/create return types. Also add AGENTS.md (agent skills index), update README with Swagger/OpenAPI usage instructions, and add /.windsurf to .gitignore. These changes aim to harden runtime correctness, make intended contracts explicit, and prepare the codebase for easier maintenance and static analysis.
This commit is contained in:
@@ -1,27 +1,29 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/MediaType.php';
|
||||
require_once __DIR__ . '/../services/ImageHandler.php';
|
||||
|
||||
class Game extends MediaType {
|
||||
private $imageHandler;
|
||||
private $isUpdate = false;
|
||||
private $mediaId = null;
|
||||
|
||||
public function __construct($pdo) {
|
||||
private ImageHandler $imageHandler;
|
||||
private bool $isUpdate = false;
|
||||
private ?int $mediaId = null;
|
||||
|
||||
public function __construct(PDO $pdo) {
|
||||
parent::__construct($pdo);
|
||||
$this->imageHandler = new ImageHandler();
|
||||
}
|
||||
|
||||
protected function getType() {
|
||||
|
||||
protected function getType(): string {
|
||||
return 'Game';
|
||||
}
|
||||
|
||||
protected function getTypeSpecificFields() {
|
||||
|
||||
protected function getTypeSpecificFields(): array {
|
||||
return [];
|
||||
}
|
||||
|
||||
protected function validateTypeSpecificFields($data) {
|
||||
|
||||
protected function validateTypeSpecificFields(array $data): array {
|
||||
// Game spezifische Validierung
|
||||
if (isset($data['hasIcon'])) {
|
||||
$data['hasIcon'] = is_numeric($data['hasIcon']) ? (int)$data['hasIcon'] : 0;
|
||||
@@ -47,11 +49,11 @@ class Game extends MediaType {
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Process poster field - convert base64 to file path
|
||||
*/
|
||||
protected function processImageField($data, $type) {
|
||||
protected function processImageField(array $data, string $type): array {
|
||||
if ($this->isUpdate && $this->mediaId && isset($data[$type]) && !empty($data[$type])) {
|
||||
$currentMedia = $this->findById($this->mediaId);
|
||||
if ($currentMedia && !empty($currentMedia[$type])) {
|
||||
@@ -73,7 +75,7 @@ class Game extends MediaType {
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function createWithRelations($data) {
|
||||
public function createWithRelations(array $data): int {
|
||||
// Typ setzen
|
||||
$data['type'] = 'Game';
|
||||
|
||||
@@ -152,7 +154,7 @@ class Game extends MediaType {
|
||||
return $mediaId;
|
||||
}
|
||||
|
||||
public function updateWithRelations($id, $data) {
|
||||
public function updateWithRelations(int $id, array $data): bool {
|
||||
// Set update flag and mediaId for image replacement
|
||||
$this->isUpdate = true;
|
||||
$this->mediaId = $id;
|
||||
@@ -236,7 +238,7 @@ class Game extends MediaType {
|
||||
return true;
|
||||
}
|
||||
|
||||
public function getWithGameInfo($id) {
|
||||
public function getWithGameInfo(?int $id): ?array {
|
||||
$media = parent::getWithRelations($id);
|
||||
if (!$media) {
|
||||
return null;
|
||||
@@ -264,7 +266,7 @@ class Game extends MediaType {
|
||||
return $media;
|
||||
}
|
||||
|
||||
public function getGameInfoForList($mediaId) {
|
||||
public function getGameInfoForList(?int $mediaId): ?array {
|
||||
// Media-Games Daten abrufen (ohne vollständige Relationen für Performance)
|
||||
$mediaGameId = $this->getMediaGameId($mediaId);
|
||||
if (!$mediaGameId) {
|
||||
@@ -281,7 +283,7 @@ class Game extends MediaType {
|
||||
return $gameInfo;
|
||||
}
|
||||
|
||||
public static function interpolateQuery($query, $params) {
|
||||
public static function interpolateQuery(string $query, array $params): string {
|
||||
$keys = array();
|
||||
|
||||
# build a regular expression for each parameter
|
||||
@@ -300,7 +302,7 @@ class Game extends MediaType {
|
||||
return $query;
|
||||
}
|
||||
|
||||
protected function createMediaGame($data) {
|
||||
protected function createMediaGame(array $data): string {
|
||||
$stmt = $this->pdo->prepare("
|
||||
INSERT INTO media_games (media_id, sortingName, notes, completionStatus, source, gameId, pluginId,
|
||||
isInstalled, installDirectory, installSize, hidden, favorite, playCount,
|
||||
@@ -337,7 +339,7 @@ class Game extends MediaType {
|
||||
return $this->pdo->lastInsertId();
|
||||
}
|
||||
|
||||
protected function updateMediaGame($mediaId, $data) {
|
||||
protected function updateMediaGame(int $mediaId, array $data): void {
|
||||
$setClause = [];
|
||||
$params = [];
|
||||
|
||||
@@ -354,14 +356,14 @@ class Game extends MediaType {
|
||||
$stmt->execute($params);
|
||||
}
|
||||
|
||||
protected function getMediaGameId($mediaId) {
|
||||
protected function getMediaGameId(?int $mediaId): ?int {
|
||||
$stmt = $this->pdo->prepare("SELECT id FROM media_games WHERE media_id = ?");
|
||||
$stmt->execute([$mediaId]);
|
||||
$result = $stmt->fetch();
|
||||
return $result ? $result['id'] : null;
|
||||
}
|
||||
|
||||
protected function getMediaGameData($mediaGameId) {
|
||||
protected function getMediaGameData(?int $mediaGameId): array {
|
||||
$stmt = $this->pdo->prepare("SELECT * FROM media_games WHERE id = ?");
|
||||
$stmt->execute([$mediaGameId]);
|
||||
$data = $stmt->fetch();
|
||||
@@ -373,7 +375,7 @@ class Game extends MediaType {
|
||||
return $data ?: [];
|
||||
}
|
||||
|
||||
protected function saveAchievements($mediaGameId, $achievements) {
|
||||
protected function saveAchievements(string $mediaGameId, array $achievements): void {
|
||||
$stmt = $this->pdo->prepare("
|
||||
INSERT INTO achievements (media_game_id, name, description, icon, unlocked, unlocked_date)
|
||||
VALUES (?, ?, ?, ?, ?, ?)
|
||||
@@ -396,26 +398,26 @@ class Game extends MediaType {
|
||||
}
|
||||
}
|
||||
|
||||
protected function getAchievements($mediaGameId) {
|
||||
protected function getAchievements(string $mediaGameId): array {
|
||||
$stmt = $this->pdo->prepare("SELECT * FROM achievements WHERE media_game_id = ?");
|
||||
$stmt->execute([$mediaGameId]);
|
||||
return $stmt->fetchAll();
|
||||
}
|
||||
|
||||
protected function saveGameRelation($table, $mediaGameId, $items, $field) {
|
||||
protected function saveGameRelation(string $table, string $mediaGameId, array $items, string $field): void {
|
||||
$stmt = $this->pdo->prepare("INSERT INTO $table (media_game_id, $field) VALUES (?, ?)");
|
||||
foreach ($items as $item) {
|
||||
$stmt->execute([$mediaGameId, $item]);
|
||||
}
|
||||
}
|
||||
|
||||
protected function consoleExists($name) {
|
||||
protected function consoleExists(string $name): bool {
|
||||
$stmt = $this->pdo->prepare("SELECT id FROM media WHERE type = 'Console' AND title = ?");
|
||||
$stmt->execute([$name]);
|
||||
return $stmt->fetch() !== false;
|
||||
}
|
||||
|
||||
protected function createConsole($name) {
|
||||
protected function createConsole(string $name): string {
|
||||
$stmt = $this->pdo->prepare("
|
||||
INSERT INTO media (title, cleanname, type, createdAt, updatedAt)
|
||||
VALUES (?, ?, 'Console', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
|
||||
@@ -424,7 +426,7 @@ class Game extends MediaType {
|
||||
return $this->pdo->lastInsertId();
|
||||
}
|
||||
|
||||
protected function saveGameRelationWithConsole($table, $mediaGameId, $items, $field) {
|
||||
protected function saveGameRelationWithConsole(string $table, string $mediaGameId, array $items, string $field): void {
|
||||
$stmt = $this->pdo->prepare("INSERT INTO $table (media_game_id, $field) VALUES (?, ?)");
|
||||
foreach ($items as $item) {
|
||||
// Check if console exists, create if not
|
||||
@@ -435,7 +437,7 @@ class Game extends MediaType {
|
||||
}
|
||||
}
|
||||
|
||||
protected function getGameRelation($table, $mediaGameId, $field) {
|
||||
protected function getGameRelation(string $table, string $mediaGameId, string $field): array {
|
||||
$stmt = $this->pdo->prepare("SELECT $field FROM $table WHERE media_game_id = ?");
|
||||
$stmt->execute([$mediaGameId]);
|
||||
$items = $stmt->fetchAll();
|
||||
@@ -444,7 +446,7 @@ class Game extends MediaType {
|
||||
}, $items);
|
||||
}
|
||||
|
||||
protected function saveLinks($mediaGameId, $links) {
|
||||
protected function saveLinks(string $mediaGameId, array $links): void {
|
||||
$stmt = $this->pdo->prepare("INSERT INTO game_links (media_game_id, name, url) VALUES (?, ?, ?)");
|
||||
foreach ($links as $link) {
|
||||
$stmt->execute([
|
||||
@@ -455,13 +457,13 @@ class Game extends MediaType {
|
||||
}
|
||||
}
|
||||
|
||||
protected function getLinks($mediaGameId) {
|
||||
protected function getLinks(string $mediaGameId): array {
|
||||
$stmt = $this->pdo->prepare("SELECT name, url FROM game_links WHERE media_game_id = ?");
|
||||
$stmt->execute([$mediaGameId]);
|
||||
return $stmt->fetchAll();
|
||||
}
|
||||
|
||||
public function search($filters = [], $page = 1, $limit = 20) {
|
||||
public function search(array $filters = [], int $page = 1, int $limit = 20): array {
|
||||
// Nur Games suchen
|
||||
$filters['type'] = 'Game';
|
||||
return parent::search($filters, $page, $limit);
|
||||
|
||||
Reference in New Issue
Block a user