Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
35 / 35
100.00% covered (success)
100.00%
4 / 4
CRAP
100.00% covered (success)
100.00%
1 / 1
CleanupCronLogsTask
100.00% covered (success)
100.00%
34 / 34
100.00% covered (success)
100.00%
4 / 4
8
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 run
100.00% covered (success)
100.00%
19 / 19
100.00% covered (success)
100.00%
1 / 1
3
 runViaApi
100.00% covered (success)
100.00%
13 / 13
100.00% covered (success)
100.00%
1 / 1
3
 resolvePdo
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2
3declare(strict_types=1);
4
5/** @license For full copyright and license information, please see the LICENSE.md file. */
6
7namespace App\Core\Cron;
8
9defined('AMMONLY_APP') || exit('Direct script access is forbidden.');
10
11use App\Core\Api\ApiClientInterface;
12use App\Core\Cron\Presentation\Api\CronApiController;
13use App\Core\Database\MultiDbRouter;
14use App\Shared\Infrastructure\Config\InstallerConfigLoader;
15use Nyholm\Psr7\Factory\Psr17Factory;
16use PDO;
17
18/**
19 * Cleanup Old Cron Logs Background Task.
20 *
21 * Implements CronTaskInterface to purge aged cron logs via REST API or direct SQL.
22 *
23 * @package App\Core\Cron
24 */
25final readonly class CleanupCronLogsTask implements CronTaskInterface
26{
27    /**
28     * CleanupCronLogsTask constructor.
29     *
30     * @param CronApiController|ApiClientInterface|PDO|null $source API controller, client, or database connection.
31     * @param string $tablePrefix Database table prefix.
32     * @param int $retentionDays Number of days of cron logs to preserve.
33     */
34    public function __construct(
35        private CronApiController|ApiClientInterface|PDO|null $source = null,
36        private string $tablePrefix = 'a_',
37        private int $retentionDays = 30
38    ) {
39    }
40
41    /**
42     * {@inheritdoc}
43     */
44    public function run(): string
45    {
46        $retentionDays = max(1, $this->retentionDays);
47        $source = $this->source ?? $this->resolvePdo();
48
49        $apiResult = $this->runViaApi($source, $retentionDays);
50        if ($apiResult !== null) {
51            return $apiResult;
52        }
53
54        if ($source instanceof PDO) {
55            $tableName = $this->tablePrefix . 'logs_cron_records';
56            $cutoffDate = date('Y-m-d H:i:s', time() - ($retentionDays * 86400));
57
58            $sql = sprintf('DELETE FROM `%s` WHERE `created_at` < :cutoff', $tableName);
59            $stmt = $source->prepare($sql);
60            $stmt->execute([':cutoff' => $cutoffDate]);
61            $deletedCount = $stmt->rowCount();
62
63            return sprintf(
64                'Cleaned up %d old cron execution log(s) older than %d day(s) (Cutoff: %s).',
65                $deletedCount,
66                $retentionDays,
67                $cutoffDate
68            );
69        }
70
71        return 'Cleanup skipped: No database or API connection available.';
72    }
73
74    /**
75     * Runs cleanup via API controller or client if available.
76     *
77     * @param mixed $source Source handler.
78     * @param int $retentionDays Days to retain.
79     * @return string|null Result message or null.
80     */
81    private function runViaApi(mixed $source, int $retentionDays): ?string
82    {
83        if ($source instanceof CronApiController) {
84            $factory = new Psr17Factory();
85            $req = $factory->createServerRequest('POST', '/api/v1/cron/tasks/cleanup-logs')
86                ->withParsedBody(['retention_days' => $retentionDays]);
87            $response = $source->cleanupLogs($req);
88            $data = (array)json_decode((string)$response->getBody(), true);
89            return (string)($data['message'] ?? 'Cleanup executed via API.');
90        }
91
92        if ($source instanceof ApiClientInterface) {
93            $data = $source->post('/api/v1/cron/tasks/cleanup-logs', [
94                'retention_days' => $retentionDays,
95            ]);
96            return (string)($data['message'] ?? 'Cleanup executed via API.');
97        }
98
99        return null;
100    }
101
102    /**
103     * Resolves fallback PDO connection from installer configuration.
104     */
105    private function resolvePdo(): ?PDO
106    {
107        return \App\Core\Database\FallbackPdoResolver::resolveDefaultConnection();
108    }
109}