• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

azjezz / psl / 22519606807

28 Feb 2026 11:11AM UTC coverage: 97.532% (-1.2%) from 98.733%
22519606807

push

github

web-flow
feat(network): rewrite networking stack with TLS, UDP, SOCKS5, CIDR, and IO utilities (#585)

860 of 937 new or added lines in 31 files covered. (91.78%)

15 existing lines in 6 files now uncovered.

7470 of 7659 relevant lines covered (97.53%)

42.83 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

96.88
/src/Psl/Shell/execute.php
1
<?php
2

3
declare(strict_types=1);
4

5
namespace Psl\Shell;
6

7
use Psl\DateTime\Duration;
8
use Psl\Process;
9
use Psl\Str;
10

11
use function pack;
12

13
/**
14
 * Execute an external program.
15
 *
16
 * @param non-empty-string $command The command to execute.
17
 * @param list<string> $arguments The command arguments listed as separate entries.
18
 * @param null|non-empty-string $working_directory The initial working directory for the command.
19
 *                                                 This must be an absolute directory path, or null if you want to
20
 *                                                 use the default value ( the current directory )
21
 * @param array<string, string> $environment A dict with the environment variables for the command that
22
 *                                           will be run.
23
 *
24
 * @psalm-taint-sink shell $command
25
 *
26
 * @throws Exception\FailedExecutionException In case the command resulted in an exit code other than 0.
27
 * @throws Exception\PossibleAttackException In case the command being run is suspicious ( e.g: contains NULL byte ).
28
 * @throws Exception\RuntimeException In case $working_directory doesn't exist, or unable to create a new process.
29
 * @throws Exception\TimeoutException If $timeout is reached before being able to read the process stream.
30
 */
31
function execute(
32
    string $command,
33
    array $arguments = [],
34
    null|string $working_directory = null,
35
    array $environment = [],
36
    ErrorOutputBehavior $error_output_behavior = ErrorOutputBehavior::Discard,
37
    null|Duration $timeout = null,
38
): string {
39
    if (Str\contains($command, "\0")) {
24✔
UNCOV
40
        throw new Exception\PossibleAttackException('NULL byte detected.');
×
41
    }
42

43
    foreach ($arguments as $argument) {
24✔
44
        if (Str\contains($argument, "\0")) {
24✔
45
            throw new Exception\PossibleAttackException('NULL byte detected.');
1✔
46
        }
47
    }
48

49
    $cmd = Process\Command::create($command)->withArguments($arguments)->withEnvironmentVariables($environment);
23✔
50

51
    if (null !== $working_directory) {
23✔
52
        $cmd = $cmd->withWorkingDirectory($working_directory);
2✔
53
    }
54

55
    try {
56
        $output = $cmd->output($timeout);
23✔
57
    } catch (Process\Exception\TimeoutException $e) {
2✔
58
        throw new Exception\TimeoutException('reached timeout while the process output is still not readable.', 0, $e);
1✔
59
    } catch (Process\Exception\RuntimeException $e) {
1✔
60
        throw new Exception\RuntimeException($e->getMessage(), 0, $e);
1✔
61
    }
62

63
    if (!$output->status->isSuccessful()) {
21✔
64
        $commandline = Str\join([$command, ...$arguments], ' ');
1✔
65

66
        throw new Exception\FailedExecutionException(
1✔
67
            $commandline,
1✔
68
            $output->stdout,
1✔
69
            $output->stderr,
1✔
70
            $output->status->getCode(),
1✔
71
        );
1✔
72
    }
73

74
    if (ErrorOutputBehavior::Packed === $error_output_behavior) {
20✔
75
        $result = '';
10✔
76
        if ('' !== $output->stdout) {
10✔
77
            $result .= pack('C1N1', 1, Str\Byte\length($output->stdout)) . $output->stdout;
3✔
78
        }
79

80
        if ('' !== $output->stderr) {
10✔
81
            $result .= pack('C1N1', 2, Str\Byte\length($output->stderr)) . $output->stderr;
8✔
82
        }
83

84
        return $result;
10✔
85
    }
86

87
    return match ($error_output_behavior) {
88
        ErrorOutputBehavior::Prepend => $output->stderr . $output->stdout,
10✔
89
        ErrorOutputBehavior::Append => $output->stdout . $output->stderr,
9✔
90
        ErrorOutputBehavior::Replace => $output->stderr,
8✔
91
        ErrorOutputBehavior::Discard => $output->stdout,
10✔
92
    };
93
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc