PHP Classes

File: scripts/file_hatch.php

Recommend this page to a friend!
  Packages of Matthew Asham   Binkterm PHP   scripts/file_hatch.php   Download  
File: scripts/file_hatch.php
Role: Example script
Content type: text/plain
Description: Example script
Class: Binkterm PHP
Bulletin board system based on the Web
Author: By
Last change:
Date: 6 days ago
Size: 7,634 bytes
 

Contents

Class file image Download
#!/usr/bin/env php
<?php
/**
 * Re-hatch an existing file by regenerating outbound TIC files and copying the
 * file back into data/outbound for re-sending.
 *
 * Usage:
 * php scripts/file_hatch.php --file-id=123
 * php scripts/file_hatch.php --area-id=456
 * php scripts/file_hatch.php FILENAME.ZIP AREATAG [--domain=DOMAIN]
 *
 * Lookup modes:
 * --file-id=ID Re-hatch a file by files.id
 * --area-id=ID Re-hatch all approved files in a file area
 * FILENAME AREATAG Re-hatch the most recent matching file in area
 *
 * Options:
 * --domain=DOMAIN File area domain when using filename lookup
 * --allow-nonapproved Allow rehatching files not in approved status
 */

require_once __DIR__ . '/../vendor/autoload.php';
require_once
__DIR__ . '/../src/functions.php';

use
BinktermPHP\Database;
use
BinktermPHP\FileAreaManager;
use
BinktermPHP\TicFileGenerator;

function
printUsage(): void
{
    echo
"Usage:\n";
    echo
" php scripts/file_hatch.php --file-id=123\n";
    echo
" php scripts/file_hatch.php --area-id=456\n";
    echo
" php scripts/file_hatch.php FILENAME.ZIP AREATAG [--domain=DOMAIN]\n";
    echo
"\n";
    echo
"Options:\n";
    echo
" --file-id=ID Re-hatch a file by files.id\n";
    echo
" --area-id=ID Re-hatch all approved files in a file area\n";
    echo
" --domain=DOMAIN File area domain for filename lookup\n";
    echo
" --allow-nonapproved Allow rehatching files not in approved status\n";
}

function
parseArgs(array $argv): array
{
   
$args = [
       
'file_id' => null,
       
'area_id' => null,
       
'filename' => null,
       
'area_tag' => null,
       
'domain' => '',
       
'allow_nonapproved' => false,
    ];

   
$positional = [];
    foreach (
array_slice($argv, 1) as $arg) {
        if (
str_starts_with($arg, '--file-id=')) {
           
$args['file_id'] = (int) substr($arg, strlen('--file-id='));
        } elseif (
str_starts_with($arg, '--area-id=')) {
           
$args['area_id'] = (int) substr($arg, strlen('--area-id='));
        } elseif (
str_starts_with($arg, '--domain=')) {
           
$args['domain'] = substr($arg, strlen('--domain='));
        } elseif (
$arg === '--allow-nonapproved') {
           
$args['allow_nonapproved'] = true;
        } elseif (!
str_starts_with($arg, '--')) {
           
$positional[] = $arg;
        }
    }

    if (
$args['file_id'] === null && $args['area_id'] === null) {
       
$args['filename'] = $positional[0] ?? null;
       
$args['area_tag'] = isset($positional[1]) ? strtoupper($positional[1]) : null;
    }

    return
$args;
}

function
findFileByName(PDO $db, int $fileAreaId, string $filename): ?array
{
   
$stmt = $db->prepare("
        SELECT *
        FROM files
        WHERE file_area_id = ?
          AND LOWER(filename) = LOWER(?)
        ORDER BY id DESC
        LIMIT 1
    "
);
   
$stmt->execute([$fileAreaId, $filename]);
   
$row = $stmt->fetch(PDO::FETCH_ASSOC);
    return
$row ?: null;
}

$args = parseArgs($argv);

if ((
$args['file_id'] ?? null) === null && ($args['area_id'] ?? null) === null && (!$args['filename'] || !$args['area_tag'])) {
   
printUsage();
    exit(
1);
}

$db = Database::getInstance()->getPdo();
$manager = new FileAreaManager();

try {
   
// Area-level rehatch: rehatch all approved files in the area
   
if (($args['area_id'] ?? null) !== null) {
       
$fileArea = $manager->getFileAreaById((int) $args['area_id']);
        if (!
$fileArea) {
            throw new
RuntimeException("File area ID {$args['area_id']} not found");
        }

        if (!empty(
$fileArea['is_local'])) {
            throw new
RuntimeException("File area {$fileArea['tag']} is local-only; no TICs will be generated");
        }

        if (!empty(
$fileArea['is_private'])) {
            throw new
RuntimeException("File area {$fileArea['tag']} is private; no TICs will be generated");
        }

       
$files = $manager->getFiles((int) $fileArea['id']);

        if (empty(
$files)) {
            echo
"No approved files found in area {$fileArea['tag']}.\n";
            exit(
0);
        }

       
$generator = new TicFileGenerator();
       
$totalTics = 0;
       
$errors = 0;

        echo
"Re-hatching {$fileArea['tag']}@{$fileArea['domain']} (" . count($files) . " file(s))...\n";

        foreach (
$files as $file) {
            try {
               
$sourcePath = $manager->resolveFilePath($file);
                if (!
is_file($sourcePath)) {
                    echo
" SKIP {$file['filename']} (not on disk)\n";
                    continue;
                }

               
$createdTics = $generator->createTicFilesForUplinks($file, $fileArea);
               
$totalTics += count($createdTics);
                echo
" OK {$file['filename']} (" . count($createdTics) . " TIC(s))\n";
            } catch (
Throwable $e) {
                echo
" ERROR {$file['filename']}: " . $e->getMessage() . "\n";
               
$errors++;
            }
        }

        echo
"\nDone. {$totalTics} TIC file(s) queued, {$errors} error(s).\n";
        exit(
$errors > 0 ? 1 : 0);
    }

    if (
$args['file_id'] !== null) {
       
$file = $manager->getFileById((int) $args['file_id']);
        if (!
$file) {
            throw new
RuntimeException("File ID {$args['file_id']} not found");
        }

       
$fileArea = $manager->getFileAreaById((int) $file['file_area_id']);
        if (!
$fileArea) {
            throw new
RuntimeException("File area {$file['file_area_id']} not found");
        }
    } else {
       
$fileArea = $manager->getFileAreaByTag($args['area_tag'], $args['domain']);
        if (!
$fileArea) {
           
$domainText = $args['domain'] !== '' ? " in domain '{$args['domain']}'" : '';
            throw new
RuntimeException("File area '{$args['area_tag']}' not found{$domainText}");
        }

       
$file = findFileByName($db, (int) $fileArea['id'], (string) $args['filename']);
        if (!
$file) {
            throw new
RuntimeException("File '{$args['filename']}' not found in area {$fileArea['tag']}");
        }
    }

    if (!
$args['allow_nonapproved'] && ($file['status'] ?? '') !== 'approved') {
        throw new
RuntimeException("File status is '{$file['status']}' not 'approved' (use --allow-nonapproved to override)");
    }

    if (!empty(
$fileArea['is_local'])) {
        throw new
RuntimeException("File area {$fileArea['tag']} is local-only; no TICs will be generated");
    }

    if (!empty(
$fileArea['is_private'])) {
        throw new
RuntimeException("File area {$fileArea['tag']} is private; no TICs will be generated");
    }

   
$sourcePath = $manager->resolveFilePath($file);
    if (!
is_file($sourcePath)) {
        throw new
RuntimeException("Source file not found on disk: {$sourcePath}");
    }

   
$generator = new TicFileGenerator();
   
$createdTics = $generator->createTicFilesForUplinks($file, $fileArea);

    if (
count($createdTics) === 0) {
        throw new
RuntimeException("No TIC files were created. Check that domain '{$fileArea['domain']}' has configured uplinks.");
    }

    echo
"Re-hatched file:\n";
    echo
" ID: {$file['id']}\n";
    echo
" Filename: {$file['filename']}\n";
    echo
" Area: {$fileArea['tag']}@{$fileArea['domain']}\n";
    echo
" Source: {$sourcePath}\n";
    echo
"\n";
    echo
"Queued in data/outbound:\n";
    echo
" File: {$file['filename']}\n";
    foreach (
$createdTics as $ticPath) {
        echo
" TIC: " . basename($ticPath) . "\n";
    }

    exit(
0);
} catch (
Throwable $e) {
   
fwrite(STDERR, "Error: " . $e->getMessage() . "\n");
    exit(
1);
}