about summary refs log tree commit diff
diff options
context:
space:
mode:
authorFrederick Muriuki Muriithi2026-08-24 10:48:03 -0500
committerFrederick Muriuki Muriithi2026-08-24 13:18:34 -0500
commitd1e617f31c2ed2900845b7ea806ab5ee647d4de0 (patch)
treeeddc86d0049668914b3aca2e66a65fd7168e5400
parentea3796d38c2dfeeafd79465518626feff727da74 (diff)
downloadBNW-d1e617f31c2ed2900845b7ea806ab5ee647d4de0.tar.gz
Add a wrapper around `proc_open(...)` to use for terminal commands.
-rw-r--r--sourcecodes/terminal_commands.php84
1 files changed, 84 insertions, 0 deletions
diff --git a/sourcecodes/terminal_commands.php b/sourcecodes/terminal_commands.php
new file mode 100644
index 00000000..fb4f4466
--- /dev/null
+++ b/sourcecodes/terminal_commands.php
@@ -0,0 +1,84 @@
+<?php
+
+function __command2string__(string $str_cmd, string $arg): string
+{
+    if(is_null($str_cmd) or trim($str_cmd) == '') {
+        return trim($arg);
+    }
+    return sprintf("%s %s", $str_cmd, $arg);
+}
+
+/**
+ * Run terminal commands: thin wrapper around `proc_open`.
+ *
+ * This wraps the `proc_open(...)` function, by doing some basic checks and
+ * raising appropriate `RuntimeException errors in case of failures.`
+ *
+ * @param array $command An array of strings, where the first item is the
+ *        command to run and the rest are arguments to that command.
+ * @returns array Returns the exit code for the process, the output on the
+ *          standard output and the output on the standard error.
+ * @raises RuntimeException
+ */
+function runTerminalCommand(array $command): array
+{
+    if(!is_array($command))
+    {
+        throw new \RuntimeException(
+            "Command *must* always be an array of strings.");
+    }
+
+    if(sizeof($command) == 0) {
+        throw new \RuntimeException(
+            "You must provide at least the command to run.");
+    }
+
+    $descriptors = [
+        1 => ['pipe', 'w'], //stdout
+        2 => ['pipe', 'w'], //stderr
+    ];
+
+    // Generate `$strcmd` below to use for user notifications.
+    $strcmd = array_reduce(
+        array_merge(
+            [escapeshellcmd($command[0])],
+            array_map(
+                'escapeshellarg',
+                array_slice($command, 1, sizeof($command)))),
+        '__command2string__',
+        "");
+    $process = proc_open(
+        $command, // `proc_open` auto-escapes array command
+        $descriptors,
+        $pipes);
+
+    if(!is_resource($process)) {
+        throw new \RunTimeException(sprintf(
+            "PHP was not able to spawn the process [%s]. " . 
+            "Check system resources or paths.",
+            $strcmd
+        ));
+    }
+
+    // Read streams:
+    $stdout = stream_get_contents($pipes[1]);
+    $stderr = stream_get_contents($pipes[2]);
+
+    // Clean streams and close process resource.
+    fclose($pipes[1]);
+    fclose($pipes[2]);
+    $exitcode = proc_close($process);
+
+    if($exitcode !== 0) {
+        throw new \RuntimeException(sprintf(
+            "Command [%s] failed with exit code %d.\n" .
+            "Stdout: %s\n\nStderr: %s",
+            $strcmd,
+            $exitcode,
+            trim($stdout),
+            trim($stderr)
+        ));
+    }
+    return [$exitcode, $stdout, $stderr];
+}
+?>