²ιΏ΄/±ΰΌ ΄ϊΒλ
ΔΪΘέ
<?php /** * βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ * DOCUMENT HANDLER * Manages document generation, caching, and serving * Supports PDF generation and static file serving * βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ */ require_once __DIR__ . '/pdf_generator.php'; class DocumentHandler { // Cache settings private static $cacheEnabled = true; private static $cacheDuration = 3600; // 1 hour private static $cachePath = null; // Supported document types private static $mimeTypes = [ 'pdf' => 'application/pdf', 'doc' => 'application/msword', 'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', 'xls' => 'application/vnd.ms-excel', 'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', 'txt' => 'text/plain', 'html' => 'text/html', ]; /** * Initialize cache path */ private static function initCache() { if (self::$cachePath === null) { self::$cachePath = defined('DATA_PATH') ? DATA_PATH . '/cache' : __DIR__ . '/../data/cache'; if (!is_dir(self::$cachePath)) { @mkdir(self::$cachePath, 0755, true); } } } /** * Serve document to client * Generates PDF if needed, uses cache if available */ public static function serve($recipient, $format = 'pdf') { self::initCache(); $id = $recipient['id'] ?? ''; $template = $recipient['template'] ?? 'document'; // Generate cache key $cacheKey = self::getCacheKey($id, $template, $format); // Check cache first if (self::$cacheEnabled) { $cached = self::getFromCache($cacheKey); if ($cached !== false) { self::outputDocument($cached, $format, $id); return true; } } // Generate document $content = self::generateDocument($recipient, $format); if ($content === false) { return false; } // Store in cache if (self::$cacheEnabled) { self::storeInCache($cacheKey, $content); } // Output to client self::outputDocument($content, $format, $id); return true; } /** * Generate document based on format */ public static function generateDocument($recipient, $format = 'pdf') { $template = $recipient['template'] ?? 'document'; switch ($format) { case 'pdf': return PdfGenerator::generate($template, $recipient); case 'html': return self::generateHtmlDocument($recipient); default: return false; } } /** * Generate HTML document (for preview/download) */ private static function generateHtmlDocument($recipient) { $templatePath = defined('TEMPLATES_PATH') ? TEMPLATES_PATH . '/landing/' . ($recipient['template'] ?? 'document') . '.html' : __DIR__ . '/../templates/landing/' . ($recipient['template'] ?? 'document') . '.html'; if (!file_exists($templatePath)) { $templatePath = dirname($templatePath) . '/document.html'; } // Use template engine to render if (class_exists('TemplateEngine')) { return TemplateEngine::render($templatePath, $recipient); } // Fallback: simple render $html = file_get_contents($templatePath); return self::replacePlaceholders($html, $recipient); } /** * Simple placeholder replacement fallback */ private static function replacePlaceholders($html, $data) { $replacements = [ '{{firstname}}' => htmlspecialchars($data['firstname'] ?? $data['name'] ?? 'User'), '{{name}}' => htmlspecialchars($data['name'] ?? $data['firstname'] ?? 'User'), '{{email}}' => self::maskEmail($data['email'] ?? ''), '{{company}}' => htmlspecialchars($data['company'] ?? 'Your Organization'), '{{domain}}' => htmlspecialchars($data['domain'] ?? ''), '{{date}}' => date('F j, Y'), '{{time}}' => date('g:i A'), '{{year}}' => date('Y'), ]; return str_replace(array_keys($replacements), array_values($replacements), $html); } /** * Serve static file from storage */ public static function serveStatic($filePath) { // Security: ensure file is within allowed directory $realPath = realpath($filePath); $basePath = realpath(defined('DATA_PATH') ? DATA_PATH . '/files' : __DIR__ . '/../data/files'); if ($realPath === false || strpos($realPath, $basePath) !== 0) { return false; } if (!file_exists($realPath)) { return false; } // Get mime type $extension = strtolower(pathinfo($realPath, PATHINFO_EXTENSION)); $mimeType = self::$mimeTypes[$extension] ?? 'application/octet-stream'; // Output headers header('Content-Type: ' . $mimeType); header('Content-Length: ' . filesize($realPath)); header('Content-Disposition: attachment; filename="' . basename($realPath) . '"'); header('Cache-Control: no-cache, must-revalidate'); // Output file readfile($realPath); return true; } /** * Output document to client */ private static function outputDocument($content, $format, $id) { $mimeType = self::$mimeTypes[$format] ?? 'application/octet-stream'; $filename = self::generateFilename($id, $format); // Security headers header('X-Content-Type-Options: nosniff'); header('X-Frame-Options: DENY'); // Content headers header('Content-Type: ' . $mimeType); header('Content-Length: ' . strlen($content)); header('Content-Disposition: attachment; filename="' . $filename . '"'); header('Cache-Control: private, max-age=3600'); echo $content; exit; } /** * Generate safe filename */ private static function generateFilename($id, $format) { $prefix = ['Document', 'File', 'Report'][hexdec(substr(md5($id), 0, 1)) % 3]; $date = date('Y-m-d'); $hash = strtoupper(substr(md5($id . 'filename'), 0, 6)); return "{$prefix}_{$date}_{$hash}.{$format}"; } // βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ // CACHE METHODS // βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ /** * Generate cache key */ private static function getCacheKey($id, $template, $format) { return md5("{$id}_{$template}_{$format}") . ".{$format}"; } /** * Get document from cache */ private static function getFromCache($cacheKey) { $cachePath = self::$cachePath . '/' . $cacheKey; if (!file_exists($cachePath)) { return false; } // Check if expired if (filemtime($cachePath) + self::$cacheDuration < time()) { @unlink($cachePath); return false; } return file_get_contents($cachePath); } /** * Store document in cache */ private static function storeInCache($cacheKey, $content) { $cachePath = self::$cachePath . '/' . $cacheKey; @file_put_contents($cachePath, $content); } /** * Clear cache for specific recipient */ public static function clearCache($id = null) { self::initCache(); if ($id === null) { // Clear all cache $files = glob(self::$cachePath . '/*'); foreach ($files as $file) { if (is_file($file)) { @unlink($file); } } } else { // Clear specific recipient's cache $files = glob(self::$cachePath . '/' . md5($id . '_*') . '.*'); foreach ($files as $file) { @unlink($file); } } } /** * Get cache statistics */ public static function getCacheStats() { self::initCache(); $files = glob(self::$cachePath . '/*'); $totalSize = 0; $fileCount = 0; foreach ($files as $file) { if (is_file($file)) { $totalSize += filesize($file); $fileCount++; } } return [ 'enabled' => self::$cacheEnabled, 'path' => self::$cachePath, 'files' => $fileCount, 'size' => $totalSize, 'size_human' => self::formatBytes($totalSize), ]; } /** * Format bytes to human readable */ private static function formatBytes($bytes) { $units = ['B', 'KB', 'MB', 'GB']; $pow = floor(($bytes ? log($bytes) : 0) / log(1024)); $pow = min($pow, count($units) - 1); return round($bytes / pow(1024, $pow), 2) . ' ' . $units[$pow]; } /** * Mask email for display */ private static function maskEmail($email) { if (empty($email) || strpos($email, '@') === false) { return 'your email'; } list($local, $domain) = explode('@', $email); $maskedLocal = substr($local, 0, 1) . '***'; $domainParts = explode('.', $domain); $tld = array_pop($domainParts); $maskedDomain = substr($domainParts[0] ?? 'mail', 0, 1) . '***.' . $tld; return $maskedLocal . '@' . $maskedDomain; } }