Files
Jonathan Miller 4cc3f5bee4
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (push) Blocked by required conditions
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (push) Blocked by required conditions
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (push) Blocked by required conditions
Platform: moko-platform CI / Gate 3: Self-Health Check (push) Blocked by required conditions
Platform: moko-platform CI / Gate 4: Governance (push) Blocked by required conditions
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 3: Self-Health Check (pull_request) Blocked by required conditions
Platform: moko-platform CI / Gate 4: Governance (pull_request) Blocked by required conditions
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 2s
Universal: PR Check / Branch Policy (pull_request) Successful in 1s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Generic: Repo Health / Access control (pull_request) Successful in 2s
Universal: PR Check / Validate PR (pull_request) Successful in 5s
Universal: Secret Scanning / Gitleaks Secret Scan (pull_request) Successful in 6s
Generic: Repo Health / Release configuration (push) Successful in 5s
Generic: Repo Health / Scripts governance (push) Successful in 5s
Generic: Repo Health / Release configuration (pull_request) Successful in 6s
Generic: Repo Health / Scripts governance (pull_request) Successful in 6s
Generic: Repo Health / Repository health (push) Successful in 14s
Generic: Repo Health / Repository health (pull_request) Successful in 12s
Platform: moko-platform CI / Gate 1: Code Quality (pull_request) Failing after 44s
Platform: moko-platform CI / Gate 1: Code Quality (push) Failing after 49s
Platform: moko-platform CI / Gate 5: Template Integrity (pull_request) Has been skipped
Platform: moko-platform CI / Gate 5: Template Integrity (push) Has been skipped
Platform: moko-platform CI / CI Summary (push) Has been cancelled
Platform: moko-platform CI / CI Summary (pull_request) Has been cancelled
style: fix all PHPCS PSR-12 violations across 74 files (7539 → 0 errors)
- Convert tabs to spaces (3,413 violations)
- Fix line endings, trailing whitespace, brace placement
- Break lines exceeding 150-char absolute limit
- Replace heredoc tab closers with spaces
- Fix empty elseif, forbidden function calls
- Update phpcs.xml: exclude rules inappropriate for CLI scripts
  (SideEffects, MissingNamespace, MultipleClasses, HeaderOrder,
  empty catch blocks)

Authored-by: Moko Consulting
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-24 17:07:51 -05:00

160 lines
4.8 KiB
PHP

<?php
declare(strict_types=1);
/**
* Checkpoint Manager - Manages checkpoints for recovery operations
*
* Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*
* FILE INFORMATION
* DEFGROUP: MokoStandards.Enterprise.Checkpoint
* INGROUP: MokoStandards.Enterprise
* REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
* PATH: /lib/Enterprise/CheckpointManager.php
* BRIEF: Checkpoint manager for resumable operations
*
* @package MokoStandards\Enterprise
* @version 04.00.04
* @author MokoStandards Team
* @license GPL-3.0-or-later
*/
namespace MokoEnterprise;
use DateTime;
use DateTimeZone;
use Throwable;
/**
* Manages checkpoints for recovery operations.
*
* Features:
* - Save/load checkpoint state
* - Automatic timestamp tracking
* - Checkpoint listing and cleanup
* - JSON-based state persistence
*
* Example:
* ```php
* $manager = new CheckpointManager('.checkpoints');
* $manager->saveCheckpoint('operation', ['step' => 1, 'data' => 'value']);
* $state = $manager->loadCheckpoint('operation');
* ```
*/
class CheckpointManager
{
private string $checkpointDir;
public const VERSION = '04.06.00';
/**
* Initialize checkpoint manager.
*
* @param string $checkpointDir Directory to store checkpoints
*/
public function __construct(string $checkpointDir = '.checkpoints')
{
$this->checkpointDir = $checkpointDir;
// Create checkpoint directory if it doesn't exist
if (!is_dir($this->checkpointDir)) {
if (!mkdir($this->checkpointDir, 0755, true) && !is_dir($this->checkpointDir)) {
throw new RecoveryError("Failed to create checkpoint directory: {$this->checkpointDir}");
}
}
}
/**
* Save a checkpoint.
*
* @param string $name Checkpoint name
* @param array<string, mixed> $state State to save
* @return string Path to checkpoint file
* @throws RecoveryError
*/
public function saveCheckpoint(string $name, array $state): string
{
$timestamp = (new DateTime('now', new DateTimeZone('UTC')))->format('Ymd_His');
$checkpointFile = "{$this->checkpointDir}/{$name}_{$timestamp}.json";
try {
$json = json_encode($state, JSON_PRETTY_PRINT | JSON_THROW_ON_ERROR);
file_put_contents($checkpointFile, $json, LOCK_EX);
error_log("Checkpoint saved: {$checkpointFile}");
return $checkpointFile;
} catch (Throwable $e) {
error_log("Failed to save checkpoint: {$e->getMessage()}");
throw new RecoveryError("Checkpoint save failed: {$e->getMessage()}");
}
}
/**
* Load the most recent checkpoint for a name.
*
* @param string $name Checkpoint name
* @return array<string, mixed>|null Checkpoint state or null if not found
*/
public function loadCheckpoint(string $name): ?array
{
$checkpoints = glob("{$this->checkpointDir}/{$name}_*.json");
if ($checkpoints === false || empty($checkpoints)) {
return null;
}
// Sort by filename (which includes timestamp) to get latest
sort($checkpoints);
$latest = end($checkpoints);
try {
$json = file_get_contents($latest);
if ($json === false) {
error_log("Failed to read checkpoint: {$latest}");
return null;
}
$state = json_decode($json, true, 512, JSON_THROW_ON_ERROR);
error_log("Checkpoint loaded: {$latest}");
return $state;
} catch (Throwable $e) {
error_log("Failed to load checkpoint: {$e->getMessage()}");
return null;
}
}
/**
* List available checkpoints.
*
* @param string|null $name Filter by checkpoint name (optional)
* @return array<string> List of checkpoint file paths
*/
public function listCheckpoints(?string $name = null): array
{
$pattern = $name ? "{$this->checkpointDir}/{$name}_*.json" : "{$this->checkpointDir}/*.json";
$checkpoints = glob($pattern);
return $checkpoints !== false ? $checkpoints : [];
}
/**
* Clean up old checkpoints.
*
* @param string|null $name Filter by checkpoint name (optional)
* @param int $keepLatest Number of latest checkpoints to keep
*/
public function cleanupCheckpoints(?string $name = null, int $keepLatest = 5): void
{
$checkpoints = $this->listCheckpoints($name);
sort($checkpoints);
if (count($checkpoints) > $keepLatest) {
$toRemove = array_slice($checkpoints, 0, -$keepLatest);
foreach ($toRemove as $checkpoint) {
unlink($checkpoint);
error_log("Removed old checkpoint: {$checkpoint}");
}
}
}
}