Ban proc_open() and exec() entirely - replace with shell_exec() and file redirection

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
root
2025-10-21 05:25:33 +00:00
parent 94c68861cc
commit 1c561dd301
5 changed files with 192 additions and 357 deletions

View File

@@ -82,69 +82,37 @@ class JqhtmlWebpackCompiler
// Execute official CLI compiler with IIFE format for self-registering templates
// CRITICAL: Must include --sourcemap for proper error mapping in bundles
// JQHTML v2.2.65+ uses Mozilla source-map library for reliable concatenation
// IMPORTANT: Using proc_open() instead of \exec_safe() to handle large template outputs
// \exec_safe() can truncate output for complex templates due to line-by-line buffering
// IMPORTANT: Using file redirection instead of proc_open() to avoid pipe buffer truncation
// proc_open() has race conditions with feof() that cause silent data loss on large outputs (35KB+)
// Generate temp file for output
$temp_file = storage_path('rsx-tmp/jqhtml_compile_' . uniqid() . '.js');
// Redirect stdout to file, stderr to stdout for error capture, then echo exit code
$command = sprintf(
'%s compile %s --format iife --sourcemap',
'%s compile %s --format iife --sourcemap > %s 2>&1; echo $?',
escapeshellarg($this->compiler_path),
escapeshellarg($file_path)
escapeshellarg($file_path),
escapeshellarg($temp_file)
);
$descriptors = [
0 => ['pipe', 'r'], // stdin
1 => ['pipe', 'w'], // stdout
2 => ['pipe', 'w'] // stderr
];
// Execute command synchronously - shell_exec captures the exit code from echo $?
$result = shell_exec($command);
$return_code = (int)trim($result);
$process = proc_open($command, $descriptors, $pipes);
if (!is_resource($process)) {
throw new \RuntimeException("Failed to execute jqhtml compiler");
// Read the compiled output from file
$compiled_js = '';
if (file_exists($temp_file)) {
$compiled_js = file_get_contents($temp_file);
unlink($temp_file); // Clean up temp file
}
// Close stdin
fclose($pipes[0]);
// Set blocking mode to ensure complete reads
stream_set_blocking($pipes[1], true);
stream_set_blocking($pipes[2], true);
// Read stdout and stderr completely in chunks
// CRITICAL: Use feof() as loop condition to prevent race condition truncation
// Checking feof() AFTER empty reads can cause 8192-byte truncation bug
$output_str = '';
$error_str = '';
// Read stdout until EOF
while (!feof($pipes[1])) {
$chunk = fread($pipes[1], 8192);
if ($chunk !== false) {
$output_str .= $chunk;
}
}
// Read stderr until EOF
while (!feof($pipes[2])) {
$chunk = fread($pipes[2], 8192);
if ($chunk !== false) {
$error_str .= $chunk;
}
}
fclose($pipes[1]);
fclose($pipes[2]);
// Get return code
$return_code = proc_close($process);
// Combine stdout and stderr for error messages
if ($return_code !== 0 && !empty($error_str)) {
$output_str = $error_str . "\n" . $output_str;
}
// If there was an error, the output file will contain the error message
$output_str = $compiled_js;
// Check for compilation errors
if ($return_code !== 0) {
// Official CLI outputs errors to stderr (captured in stdout with 2>&1)
// Error output captured in output_str via 2>&1 redirection
// Try multiple error formats
// Format 1: "at filename:line:column" (newer format)
@@ -179,8 +147,7 @@ class JqhtmlWebpackCompiler
);
}
// Success - the output is the compiled JavaScript
$compiled_js = $output_str;
// Success - compiled_js already contains the output from the temp file
// Don't add any comments - they break sourcemap line offsets
// Just use the compiler output as-is