16b7e227cSYamadaMiz<?php 247595f9cSYamadaMiz 347595f9cSYamadaMizuse dokuwiki\Extension\ActionPlugin; 447595f9cSYamadaMizuse dokuwiki\Extension\EventHandler; 547595f9cSYamadaMizuse dokuwiki\Extension\Event; 647595f9cSYamadaMiz 7f9af2148SYamadaMiz/** 8b68e3724SYamadaMiz * DokuWiki Plugin Mizar Verifiable Docs (Action Component) 9f9af2148SYamadaMiz * 10f9af2148SYamadaMiz * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html 11*42c7ffb2SYamadaMiz * @author 12f9af2148SYamadaMiz */ 13f9af2148SYamadaMiz 14b68e3724SYamadaMizclass action_plugin_mizarverifiabledocs extends ActionPlugin 1547595f9cSYamadaMiz{ 1647595f9cSYamadaMiz /** 174754b0a7SYamadaMiz * Registers a callback function for a given event 1847595f9cSYamadaMiz * 1947595f9cSYamadaMiz * @param EventHandler $controller DokuWiki's event controller object 2047595f9cSYamadaMiz * @return void 2147595f9cSYamadaMiz */ 2247595f9cSYamadaMiz public function register(EventHandler $controller) 2347595f9cSYamadaMiz { 246b7e227cSYamadaMiz $controller->register_hook('AJAX_CALL_UNKNOWN', 'BEFORE', $this, 'handle_ajax_call'); 256b7e227cSYamadaMiz } 266b7e227cSYamadaMiz 2747595f9cSYamadaMiz /** 2847595f9cSYamadaMiz * Handles AJAX requests 2947595f9cSYamadaMiz * 3047595f9cSYamadaMiz * @param Event $event 3147595f9cSYamadaMiz * @param $param 3247595f9cSYamadaMiz */ 3347595f9cSYamadaMiz public function handle_ajax_call(Event $event, $param) 3447595f9cSYamadaMiz { 356b7e227cSYamadaMiz unset($param); // 未使用のパラメータを無効化 3647595f9cSYamadaMiz 3747595f9cSYamadaMiz switch ($event->data) { 3847595f9cSYamadaMiz case 'clear_temp_files': 3947595f9cSYamadaMiz $event->preventDefault(); 4047595f9cSYamadaMiz $event->stopPropagation(); 41*42c7ffb2SYamadaMiz $this->clearTempFiles(); 4247595f9cSYamadaMiz break; 4347595f9cSYamadaMiz case 'source_sse': 446b7e227cSYamadaMiz $event->preventDefault(); 456b7e227cSYamadaMiz $event->stopPropagation(); 466b7e227cSYamadaMiz $this->handleSourceSSERequest(); 4747595f9cSYamadaMiz break; 4847595f9cSYamadaMiz case 'source_compile': 496b7e227cSYamadaMiz $event->preventDefault(); 506b7e227cSYamadaMiz $event->stopPropagation(); 516b7e227cSYamadaMiz $this->handleSourceCompileRequest(); 5247595f9cSYamadaMiz break; 5347595f9cSYamadaMiz case 'view_compile': 546b7e227cSYamadaMiz $event->preventDefault(); 556b7e227cSYamadaMiz $event->stopPropagation(); 566b7e227cSYamadaMiz $this->handleViewCompileRequest(); 5747595f9cSYamadaMiz break; 5847595f9cSYamadaMiz case 'view_sse': 596b7e227cSYamadaMiz $event->preventDefault(); 606b7e227cSYamadaMiz $event->stopPropagation(); 616b7e227cSYamadaMiz $this->handleViewSSERequest(); 6247595f9cSYamadaMiz break; 639fc5dc4bSYamadaMiz case 'create_combined_file': 649fc5dc4bSYamadaMiz $event->preventDefault(); 659fc5dc4bSYamadaMiz $event->stopPropagation(); 669fc5dc4bSYamadaMiz $this->handle_create_combined_file(); 679fc5dc4bSYamadaMiz break; 686b7e227cSYamadaMiz } 696b7e227cSYamadaMiz } 706b7e227cSYamadaMiz 716b7e227cSYamadaMiz // source用のコンパイルリクエスト処理 7247595f9cSYamadaMiz private function handleSourceCompileRequest() 7347595f9cSYamadaMiz { 746b7e227cSYamadaMiz global $INPUT; 756b7e227cSYamadaMiz $pageContent = $INPUT->post->str('content'); 766b7e227cSYamadaMiz $mizarData = $this->extractMizarContent($pageContent); 776b7e227cSYamadaMiz 78*42c7ffb2SYamadaMiz // エラーチェックを追加 796b7e227cSYamadaMiz if ($mizarData === null) { 806b7e227cSYamadaMiz $this->sendAjaxResponse(false, 'Mizar content not found'); 816b7e227cSYamadaMiz return; 82*42c7ffb2SYamadaMiz } elseif (isset($mizarData['error'])) { 83*42c7ffb2SYamadaMiz $this->sendAjaxResponse(false, $mizarData['error']); 84*42c7ffb2SYamadaMiz return; 856b7e227cSYamadaMiz } 866b7e227cSYamadaMiz 876b7e227cSYamadaMiz $filePath = $this->saveMizarContent($mizarData); 886b7e227cSYamadaMiz 896b7e227cSYamadaMiz session_start(); 906b7e227cSYamadaMiz $_SESSION['source_filepath'] = $filePath; 916b7e227cSYamadaMiz 926b7e227cSYamadaMiz $this->sendAjaxResponse(true, 'Mizar content processed successfully'); 936b7e227cSYamadaMiz } 946b7e227cSYamadaMiz 956b7e227cSYamadaMiz // source用のSSEリクエスト処理 9647595f9cSYamadaMiz private function handleSourceSSERequest() 9747595f9cSYamadaMiz { 986b7e227cSYamadaMiz header('Content-Type: text/event-stream'); 996b7e227cSYamadaMiz header('Cache-Control: no-cache'); 1006b7e227cSYamadaMiz 1016b7e227cSYamadaMiz session_start(); 1026b7e227cSYamadaMiz if (!isset($_SESSION['source_filepath'])) { 1036b7e227cSYamadaMiz echo "data: Mizar file path not found in session\n\n"; 1046b7e227cSYamadaMiz ob_flush(); 1056b7e227cSYamadaMiz flush(); 1066b7e227cSYamadaMiz return; 1076b7e227cSYamadaMiz } 1086b7e227cSYamadaMiz 1096b7e227cSYamadaMiz $filePath = $_SESSION['source_filepath']; 1106b7e227cSYamadaMiz $this->streamSourceOutput($filePath); 1116b7e227cSYamadaMiz 1126b7e227cSYamadaMiz echo "event: end\n"; 1136b7e227cSYamadaMiz echo "data: Compilation complete\n\n"; 1146b7e227cSYamadaMiz ob_flush(); 1156b7e227cSYamadaMiz flush(); 1166b7e227cSYamadaMiz } 1176b7e227cSYamadaMiz 11847595f9cSYamadaMiz // view用のコンパイルリクエスト処理 11947595f9cSYamadaMiz private function handleViewCompileRequest() 12047595f9cSYamadaMiz { 12147595f9cSYamadaMiz global $INPUT; 12247595f9cSYamadaMiz $content = $INPUT->post->str('content'); 12347595f9cSYamadaMiz 12447595f9cSYamadaMiz $filePath = $this->createTempFile($content); 12547595f9cSYamadaMiz 12647595f9cSYamadaMiz session_start(); 12747595f9cSYamadaMiz $_SESSION['view_filepath'] = $filePath; 12847595f9cSYamadaMiz 12947595f9cSYamadaMiz $this->sendAjaxResponse(true, 'Mizar content processed successfully'); 13047595f9cSYamadaMiz } 13147595f9cSYamadaMiz 13247595f9cSYamadaMiz // view用のSSEリクエスト処理 13347595f9cSYamadaMiz private function handleViewSSERequest() 13447595f9cSYamadaMiz { 13547595f9cSYamadaMiz header('Content-Type: text/event-stream'); 13647595f9cSYamadaMiz header('Cache-Control: no-cache'); 13747595f9cSYamadaMiz 13847595f9cSYamadaMiz session_start(); 13947595f9cSYamadaMiz if (!isset($_SESSION['view_filepath'])) { 14047595f9cSYamadaMiz echo "data: Mizar file path not found in session\n\n"; 14147595f9cSYamadaMiz ob_flush(); 14247595f9cSYamadaMiz flush(); 14347595f9cSYamadaMiz return; 14447595f9cSYamadaMiz } 14547595f9cSYamadaMiz 14647595f9cSYamadaMiz $filePath = $_SESSION['view_filepath']; 1479fc5dc4bSYamadaMiz $this->streamViewCompileOutput($filePath); 14847595f9cSYamadaMiz 14947595f9cSYamadaMiz echo "event: end\n"; 15047595f9cSYamadaMiz echo "data: Compilation complete\n\n"; 15147595f9cSYamadaMiz ob_flush(); 15247595f9cSYamadaMiz flush(); 15347595f9cSYamadaMiz } 15447595f9cSYamadaMiz 15547595f9cSYamadaMiz // Mizarコンテンツの抽出 15647595f9cSYamadaMiz private function extractMizarContent($pageContent) 15747595f9cSYamadaMiz { 1586b7e227cSYamadaMiz $pattern = '/<mizar\s+([^>]+)>(.*?)<\/mizar>/s'; 1596b7e227cSYamadaMiz preg_match_all($pattern, $pageContent, $matches, PREG_SET_ORDER); 1606b7e227cSYamadaMiz 1616b7e227cSYamadaMiz if (empty($matches)) { 16247595f9cSYamadaMiz return null; 1636b7e227cSYamadaMiz } 1646b7e227cSYamadaMiz 165*42c7ffb2SYamadaMiz // 最初のファイル名を取得し、拡張子を除去 16647595f9cSYamadaMiz $fileName = trim($matches[0][1]); 167*42c7ffb2SYamadaMiz $fileNameWithoutExt = preg_replace('/\.miz$/i', '', $fileName); 168*42c7ffb2SYamadaMiz 169*42c7ffb2SYamadaMiz // ファイル名のバリデーションを追加 170*42c7ffb2SYamadaMiz if (!$this->isValidFileName($fileNameWithoutExt)) { 171*42c7ffb2SYamadaMiz return ['error' => "Invalid characters in file name: '{$fileNameWithoutExt}'. Only letters, numbers, underscores (_), and apostrophes (') are allowed, up to 8 characters."]; 172*42c7ffb2SYamadaMiz } 173*42c7ffb2SYamadaMiz 1746b7e227cSYamadaMiz $combinedContent = ''; 1756b7e227cSYamadaMiz 1766b7e227cSYamadaMiz foreach ($matches as $match) { 177*42c7ffb2SYamadaMiz $currentFileName = trim($match[1]); 178*42c7ffb2SYamadaMiz $currentFileNameWithoutExt = preg_replace('/\.miz$/i', '', $currentFileName); 179*42c7ffb2SYamadaMiz 180*42c7ffb2SYamadaMiz if ($currentFileNameWithoutExt !== $fileNameWithoutExt) { 181*42c7ffb2SYamadaMiz return ['error' => "File name mismatch in <mizar> tags: '{$fileNameWithoutExt}' and '{$currentFileNameWithoutExt}'"]; 1826b7e227cSYamadaMiz } 183*42c7ffb2SYamadaMiz 184*42c7ffb2SYamadaMiz // バリデーションを各ファイル名にも適用 185*42c7ffb2SYamadaMiz if (!$this->isValidFileName($currentFileNameWithoutExt)) { 186*42c7ffb2SYamadaMiz return ['error' => "Invalid characters in file name: '{$currentFileNameWithoutExt}'. Only letters, numbers, underscores (_), and apostrophes (') are allowed, up to 8 characters."]; 187*42c7ffb2SYamadaMiz } 188*42c7ffb2SYamadaMiz 1896b7e227cSYamadaMiz $combinedContent .= trim($match[2]) . "\n"; 1906b7e227cSYamadaMiz } 1916b7e227cSYamadaMiz 192*42c7ffb2SYamadaMiz // ファイル名に拡張子を付加 193*42c7ffb2SYamadaMiz $fullFileName = $fileNameWithoutExt . '.miz'; 194*42c7ffb2SYamadaMiz 195*42c7ffb2SYamadaMiz return ['fileName' => $fullFileName, 'content' => $combinedContent]; 196*42c7ffb2SYamadaMiz } 197*42c7ffb2SYamadaMiz 198*42c7ffb2SYamadaMiz // ファイル名のバリデーション関数を追加 199*42c7ffb2SYamadaMiz private function isValidFileName($fileName) 200*42c7ffb2SYamadaMiz { 201*42c7ffb2SYamadaMiz // ファイル名の長さをチェック(最大8文字) 202*42c7ffb2SYamadaMiz if (strlen($fileName) > 8) { 203*42c7ffb2SYamadaMiz return false; 204*42c7ffb2SYamadaMiz } 205*42c7ffb2SYamadaMiz 206*42c7ffb2SYamadaMiz // 許可される文字のみを含むかチェック 207*42c7ffb2SYamadaMiz if (!preg_match('/^[A-Za-z0-9_\']+$/', $fileName)) { 208*42c7ffb2SYamadaMiz return false; 209*42c7ffb2SYamadaMiz } 210*42c7ffb2SYamadaMiz 211*42c7ffb2SYamadaMiz return true; 2126b7e227cSYamadaMiz } 2136b7e227cSYamadaMiz 21447595f9cSYamadaMiz // Mizarコンテンツの保存 21547595f9cSYamadaMiz private function saveMizarContent($mizarData) 21647595f9cSYamadaMiz { 2176b7e227cSYamadaMiz $workPath = rtrim($this->getConf('mizar_work_dir'), '/\\'); 2186b7e227cSYamadaMiz $filePath = $workPath . "/TEXT/" . $mizarData['fileName']; 2196b7e227cSYamadaMiz file_put_contents($filePath, $mizarData['content']); 2206b7e227cSYamadaMiz return $filePath; 2216b7e227cSYamadaMiz } 2226b7e227cSYamadaMiz 22347595f9cSYamadaMiz // source用の出力をストリーム 22447595f9cSYamadaMiz private function streamSourceOutput($filePath) 22547595f9cSYamadaMiz { 2266b7e227cSYamadaMiz $workPath = rtrim($this->getConf('mizar_work_dir'), '/\\'); 2276b7e227cSYamadaMiz chdir($workPath); 2286b7e227cSYamadaMiz 2296b7e227cSYamadaMiz $command = "miz2prel " . escapeshellarg($filePath); 2306b7e227cSYamadaMiz $process = proc_open($command, array(1 => array("pipe", "w")), $pipes); 2316b7e227cSYamadaMiz 2326b7e227cSYamadaMiz if (is_resource($process)) { 2336b7e227cSYamadaMiz while ($line = fgets($pipes[1])) { 2346b7e227cSYamadaMiz echo "data: " . $line . "\n\n"; 2356b7e227cSYamadaMiz ob_flush(); 2366b7e227cSYamadaMiz flush(); 2376b7e227cSYamadaMiz } 2386b7e227cSYamadaMiz fclose($pipes[1]); 2399fc5dc4bSYamadaMiz 2409fc5dc4bSYamadaMiz // エラー処理の追加 2419fc5dc4bSYamadaMiz $errFilename = str_replace('.miz', '.err', $filePath); 2429fc5dc4bSYamadaMiz if ($this->handleCompilationErrors($errFilename, rtrim($this->getConf('mizar_share_dir'), '/\\') . '/mizar.msg')) { 2439fc5dc4bSYamadaMiz // エラーがあった場合は処理を終了 2449fc5dc4bSYamadaMiz proc_close($process); 2459fc5dc4bSYamadaMiz return; 2469fc5dc4bSYamadaMiz } 2479fc5dc4bSYamadaMiz 2486b7e227cSYamadaMiz proc_close($process); 2496b7e227cSYamadaMiz } 2506b7e227cSYamadaMiz } 2516b7e227cSYamadaMiz 25247595f9cSYamadaMiz // view用の一時ファイル作成 25347595f9cSYamadaMiz private function createTempFile($content) 25447595f9cSYamadaMiz { 2556b7e227cSYamadaMiz $workPath = rtrim($this->getConf('mizar_work_dir'), '/\\') . '/TEXT/'; 25647595f9cSYamadaMiz $uniqueName = str_replace('.', '_', uniqid('tmp', true)); 2576b7e227cSYamadaMiz $tempFilename = $workPath . $uniqueName . ".miz"; 2586b7e227cSYamadaMiz file_put_contents($tempFilename, $content); 2596b7e227cSYamadaMiz return $tempFilename; 2606b7e227cSYamadaMiz } 2616b7e227cSYamadaMiz 262*42c7ffb2SYamadaMiz // 一時ファイルの削除 26347595f9cSYamadaMiz private function clearTempFiles() 26447595f9cSYamadaMiz { 26547595f9cSYamadaMiz $workPath = rtrim($this->getConf('mizar_work_dir'), '/\\') . '/TEXT/'; 26647595f9cSYamadaMiz $files = glob($workPath . '*'); // TEXTフォルダ内のすべてのファイルを取得 26747595f9cSYamadaMiz 26847595f9cSYamadaMiz $errors = []; 26947595f9cSYamadaMiz foreach ($files as $file) { 27047595f9cSYamadaMiz if (is_file($file)) { 27147595f9cSYamadaMiz // ファイルが使用中かどうか確認 27247595f9cSYamadaMiz if (!$this->is_file_locked($file)) { 27347595f9cSYamadaMiz $retries = 3; // 最大3回リトライ 27447595f9cSYamadaMiz while ($retries > 0) { 27547595f9cSYamadaMiz if (unlink($file)) { 27647595f9cSYamadaMiz break; // 削除成功 27747595f9cSYamadaMiz } 2785cbf3a53SYamadaMiz $errors[] = "Error deleting $file: " . error_get_last()['message']; 27947595f9cSYamadaMiz $retries--; 28047595f9cSYamadaMiz sleep(1); // 1秒待ってリトライ 28147595f9cSYamadaMiz } 28247595f9cSYamadaMiz if ($retries === 0) { 28347595f9cSYamadaMiz $errors[] = "Failed to delete: $file"; // 削除失敗 28447595f9cSYamadaMiz } 28547595f9cSYamadaMiz } else { 28647595f9cSYamadaMiz $errors[] = "File is locked: $file"; // ファイルがロックされている 28747595f9cSYamadaMiz } 28847595f9cSYamadaMiz } 28947595f9cSYamadaMiz } 29047595f9cSYamadaMiz 29147595f9cSYamadaMiz if (empty($errors)) { 29247595f9cSYamadaMiz $this->sendAjaxResponse(true, 'Temporary files cleared successfully'); 29347595f9cSYamadaMiz } else { 29447595f9cSYamadaMiz $this->sendAjaxResponse(false, 'Some files could not be deleted', $errors); 29547595f9cSYamadaMiz } 29647595f9cSYamadaMiz } 29747595f9cSYamadaMiz 29847595f9cSYamadaMiz // ファイルがロックされているかをチェックする関数 29947595f9cSYamadaMiz private function is_file_locked($file) 30047595f9cSYamadaMiz { 30147595f9cSYamadaMiz $fileHandle = @fopen($file, "r+"); 30247595f9cSYamadaMiz 30347595f9cSYamadaMiz if ($fileHandle === false) { 30447595f9cSYamadaMiz return true; // ファイルが開けない、つまりロックされているかアクセス権がない 30547595f9cSYamadaMiz } 30647595f9cSYamadaMiz 30747595f9cSYamadaMiz $locked = !flock($fileHandle, LOCK_EX | LOCK_NB); // ロックの取得を試みる(非ブロッキングモード) 30847595f9cSYamadaMiz 30947595f9cSYamadaMiz fclose($fileHandle); 31047595f9cSYamadaMiz return $locked; // ロックが取得できなければファイルはロックされている 31147595f9cSYamadaMiz } 31247595f9cSYamadaMiz 3139fc5dc4bSYamadaMiz // View用コンパイル出力のストリーム 3149fc5dc4bSYamadaMiz private function streamViewCompileOutput($filePath) 31547595f9cSYamadaMiz { 3166b7e227cSYamadaMiz $workPath = $this->getConf('mizar_work_dir'); 3176b7e227cSYamadaMiz $sharePath = rtrim($this->getConf('mizar_share_dir'), '/\\') . '/'; 3186b7e227cSYamadaMiz 3196b7e227cSYamadaMiz chdir($workPath); 3206b7e227cSYamadaMiz 3219fc5dc4bSYamadaMiz $errFilename = str_replace('.miz', '.err', $filePath); 3226b7e227cSYamadaMiz $command = "makeenv " . escapeshellarg($filePath); 3236b7e227cSYamadaMiz $process = proc_open($command, array(1 => array("pipe", "w"), 2 => array("pipe", "w")), $pipes); 3246b7e227cSYamadaMiz 3256b7e227cSYamadaMiz if (is_resource($process)) { 3266b7e227cSYamadaMiz while ($line = fgets($pipes[1])) { 3276b7e227cSYamadaMiz echo "data: " . mb_convert_encoding($line, 'UTF-8', 'SJIS') . "\n\n"; 3286b7e227cSYamadaMiz ob_flush(); 3296b7e227cSYamadaMiz flush(); 3306b7e227cSYamadaMiz } 3316b7e227cSYamadaMiz fclose($pipes[1]); 3326b7e227cSYamadaMiz 3336b7e227cSYamadaMiz while ($line = fgets($pipes[2])) { 3346b7e227cSYamadaMiz echo "data: ERROR: " . mb_convert_encoding($line, 'UTF-8', 'SJIS') . "\n\n"; 3356b7e227cSYamadaMiz ob_flush(); 3366b7e227cSYamadaMiz flush(); 3376b7e227cSYamadaMiz } 3386b7e227cSYamadaMiz fclose($pipes[2]); 3396b7e227cSYamadaMiz proc_close($process); 3406b7e227cSYamadaMiz 3411174571dSYamadaMiz // makeenvのエラー処理 3429fc5dc4bSYamadaMiz if ($this->handleCompilationErrors($errFilename, $sharePath . '/mizar.msg')) { 3436b7e227cSYamadaMiz return; 3446b7e227cSYamadaMiz } 3456b7e227cSYamadaMiz 3466b7e227cSYamadaMiz // verifierの実行 3476b7e227cSYamadaMiz $exePath = rtrim($this->getConf('mizar_exe_dir'), '/\\') . '/'; 3486b7e227cSYamadaMiz $verifierPath = escapeshellarg($exePath . "verifier"); 3496b7e227cSYamadaMiz if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') { 3506b7e227cSYamadaMiz $verifierPath .= ".exe"; 3516b7e227cSYamadaMiz } 3526b7e227cSYamadaMiz $verifierCommand = $verifierPath . " -q -l " . escapeshellarg("TEXT/" . basename($filePath)); 3536b7e227cSYamadaMiz 3546b7e227cSYamadaMiz $verifierProcess = proc_open($verifierCommand, array(1 => array("pipe", "w"), 2 => array("pipe", "w")), $verifierPipes); 3556b7e227cSYamadaMiz 3566b7e227cSYamadaMiz if (is_resource($verifierProcess)) { 3576b7e227cSYamadaMiz while ($line = fgets($verifierPipes[1])) { 3586b7e227cSYamadaMiz echo "data: " . mb_convert_encoding($line, 'UTF-8', 'SJIS') . "\n\n"; 3596b7e227cSYamadaMiz ob_flush(); 3606b7e227cSYamadaMiz flush(); 3616b7e227cSYamadaMiz } 3626b7e227cSYamadaMiz fclose($verifierPipes[1]); 3636b7e227cSYamadaMiz 3646b7e227cSYamadaMiz while ($line = fgets($verifierPipes[2])) { 3656b7e227cSYamadaMiz echo "data: ERROR: " . mb_convert_encoding($line, 'UTF-8', 'SJIS') . "\n\n"; 3666b7e227cSYamadaMiz ob_flush(); 3676b7e227cSYamadaMiz flush(); 3686b7e227cSYamadaMiz } 3696b7e227cSYamadaMiz fclose($verifierPipes[2]); 3706b7e227cSYamadaMiz proc_close($verifierProcess); 3711174571dSYamadaMiz 3721174571dSYamadaMiz // verifierのエラー処理 3731174571dSYamadaMiz if ($this->handleCompilationErrors($errFilename, $sharePath . '/mizar.msg')) { 3741174571dSYamadaMiz return; 3751174571dSYamadaMiz } 3766b7e227cSYamadaMiz } else { 3776b7e227cSYamadaMiz echo "data: ERROR: Failed to execute verifier command.\n\n"; 3786b7e227cSYamadaMiz ob_flush(); 3796b7e227cSYamadaMiz flush(); 3806b7e227cSYamadaMiz } 3816b7e227cSYamadaMiz } else { 3826b7e227cSYamadaMiz echo "data: ERROR: Failed to execute makeenv command.\n\n"; 3836b7e227cSYamadaMiz ob_flush(); 3846b7e227cSYamadaMiz flush(); 3856b7e227cSYamadaMiz } 3866b7e227cSYamadaMiz } 3876b7e227cSYamadaMiz 38847595f9cSYamadaMiz private function getMizarErrorMessages($mizarMsgFile) 38947595f9cSYamadaMiz { 3906b7e227cSYamadaMiz $errorMessages = []; 3916b7e227cSYamadaMiz $lines = file($mizarMsgFile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES); 3926b7e227cSYamadaMiz 3936b7e227cSYamadaMiz $isReadingErrorMsg = false; 3946b7e227cSYamadaMiz $key = 0; 3956b7e227cSYamadaMiz 3966b7e227cSYamadaMiz foreach ($lines as $line) { 3976b7e227cSYamadaMiz if (preg_match('/# (\d+)/', $line, $matches)) { 3986b7e227cSYamadaMiz $isReadingErrorMsg = true; 3996b7e227cSYamadaMiz $key = intval($matches[1]); 4006b7e227cSYamadaMiz } elseif ($isReadingErrorMsg) { 4016b7e227cSYamadaMiz $errorMessages[$key] = $line; 4026b7e227cSYamadaMiz $isReadingErrorMsg = false; 4036b7e227cSYamadaMiz } 4046b7e227cSYamadaMiz } 4056b7e227cSYamadaMiz 4066b7e227cSYamadaMiz return $errorMessages; 4076b7e227cSYamadaMiz } 4086b7e227cSYamadaMiz 40947595f9cSYamadaMiz private function sendAjaxResponse($success, $message, $data = '') 41047595f9cSYamadaMiz { 4116b7e227cSYamadaMiz header('Content-Type: application/json'); 4126b7e227cSYamadaMiz echo json_encode(['success' => $success, 'message' => $message, 'data' => $data]); 4136b7e227cSYamadaMiz exit; 4146b7e227cSYamadaMiz } 4159fc5dc4bSYamadaMiz 4169fc5dc4bSYamadaMiz private function handleCompilationErrors($errFilename, $mizarMsgFilePath) 4179fc5dc4bSYamadaMiz { 4189fc5dc4bSYamadaMiz if (file_exists($errFilename)) { 4199fc5dc4bSYamadaMiz $errors = []; 4209fc5dc4bSYamadaMiz $errorLines = file($errFilename, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES); 4219fc5dc4bSYamadaMiz foreach ($errorLines as $errorLine) { 4229fc5dc4bSYamadaMiz if (preg_match('/(\d+)\s+(\d+)\s+(\d+)/', $errorLine, $matches)) { 4239fc5dc4bSYamadaMiz $errorCode = intval($matches[3]); 4249fc5dc4bSYamadaMiz $errors[] = [ 4259fc5dc4bSYamadaMiz 'code' => $errorCode, 4269fc5dc4bSYamadaMiz 'line' => intval($matches[1]), 4279fc5dc4bSYamadaMiz 'column' => intval($matches[2]), 4289fc5dc4bSYamadaMiz 'message' => $this->getMizarErrorMessages($mizarMsgFilePath)[$errorCode] ?? 'Unknown error' 4299fc5dc4bSYamadaMiz ]; 4309fc5dc4bSYamadaMiz } 4319fc5dc4bSYamadaMiz } 4329fc5dc4bSYamadaMiz if (!empty($errors)) { 4339fc5dc4bSYamadaMiz echo "event: compileErrors\n"; 4349fc5dc4bSYamadaMiz echo "data: " . json_encode($errors) . "\n\n"; 4359fc5dc4bSYamadaMiz ob_flush(); 4369fc5dc4bSYamadaMiz flush(); 4379fc5dc4bSYamadaMiz return true; 4389fc5dc4bSYamadaMiz } 4399fc5dc4bSYamadaMiz } 4409fc5dc4bSYamadaMiz return false; 4419fc5dc4bSYamadaMiz } 4429fc5dc4bSYamadaMiz 4439fc5dc4bSYamadaMiz private function handle_create_combined_file() 4449fc5dc4bSYamadaMiz { 4459fc5dc4bSYamadaMiz global $INPUT; 4469fc5dc4bSYamadaMiz 4479fc5dc4bSYamadaMiz // 投稿されたコンテンツを取得 4489fc5dc4bSYamadaMiz $combinedContent = $INPUT->post->str('content'); 449a0444036SYamadaMiz $filename = $INPUT->post->str('filename', 'combined_file.miz'); // デフォルトのファイル名を指定 4509fc5dc4bSYamadaMiz 451a0444036SYamadaMiz // ファイルを保存せず、コンテンツを直接返す 452a0444036SYamadaMiz if (!empty($combinedContent)) { 453a0444036SYamadaMiz // ファイルの内容をレスポンスで返す(PHP側でファイルを作成しない) 454a0444036SYamadaMiz $this->sendAjaxResponse(true, 'File created successfully', [ 455a0444036SYamadaMiz 'filename' => $filename, 456a0444036SYamadaMiz 'content' => $combinedContent 457a0444036SYamadaMiz ]); 458a0444036SYamadaMiz error_log("File content sent: " . $filename); 4599fc5dc4bSYamadaMiz } else { 460a0444036SYamadaMiz $this->sendAjaxResponse(false, 'Content is empty, no file created'); 4619fc5dc4bSYamadaMiz } 4629fc5dc4bSYamadaMiz } 4636b7e227cSYamadaMiz} 464