Passed
Push — main ( f5700b...aaf4f3 )
by Michiel
32:12 queued 25:00
created

ExecTask::setResolveExecutable()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
eloc 1
dl 0
loc 3
ccs 0
cts 2
cp 0
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 1
crap 2
1
<?php
2
3
/**
4
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
5
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
6
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
7
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
8
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
9
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
10
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
11
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
12
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
13
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
14
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
15
 *
16
 * This software consists of voluntary contributions made by many individuals
17
 * and is licensed under the LGPL. For more information please see
18
 * <http://phing.info>.
19
 */
20
21
namespace Phing\Task\System;
22
23
use InvalidArgumentException;
24
use Phing\Exception\BuildException;
25
use Phing\Io\File;
26
use Phing\Io\FileUtils;
27
use Phing\Io\IOException;
28
use Phing\Phing;
29
use Phing\Project;
30
use Phing\Task;
31
use Phing\Task\System\Condition\OsCondition;
32
use Phing\Task\System\Element\LogLevelAware;
33
use Phing\Type\Commandline;
34
use Phing\Type\CommandlineArgument;
35
use Phing\Type\Environment;
36
use Phing\Type\EnvVariable;
37
use Phing\Type\Path;
38
use Phing\Util\StringHelper;
39
40
/**
41
 * Executes a command on the shell.
42
 *
43
 * @author  Andreas Aderhold <[email protected]>
44
 * @author  Hans Lellelid <[email protected]>
45
 * @author  Christian Weiske <[email protected]>
46
 */
47
class ExecTask extends Task
48
{
49
    use LogLevelAware;
50
51
    public const INVALID = PHP_INT_MAX;
52
53
    /**
54
     * Command to be executed.
55
     *
56
     * @var string
57
     */
58
    protected $realCommand;
59
60
    /**
61
     * Commandline managing object.
62
     *
63
     * @var Commandline
64
     */
65
    protected $commandline;
66
67
    /**
68
     * Working directory.
69
     *
70
     * @var File
71
     */
72
    protected $dir;
73
74
    protected $currdir;
75
76
    /**
77
     * Operating system.
78
     *
79
     * @var string
80
     */
81
    protected $os;
82
83
    /**
84
     * Whether to escape shell command using escapeshellcmd().
85
     *
86
     * @var bool
87
     */
88
    protected $escape = false;
89
90
    /**
91
     * Where to direct output.
92
     *
93
     * @var File
94
     */
95
    protected $output;
96
97
    /**
98
     * Whether to use PHP's passthru() function instead of exec().
99
     *
100
     * @var bool
101
     */
102
    protected $passthru = false;
103
104
    /**
105
     * Whether to log returned output as MSG_INFO instead of MSG_VERBOSE.
106
     *
107
     * @var bool
108
     */
109
    protected $logOutput = false;
110
111
    /**
112
     * Where to direct error output.
113
     *
114
     * @var File
115
     */
116
    protected $error;
117
118
    /**
119
     * If spawn is set then [unix] programs will redirect stdout and add '&'.
120
     *
121
     * @var bool
122
     */
123
    protected $spawn = false;
124
125
    /**
126
     * Property name to set with return value from exec call.
127
     *
128
     * @var string
129
     */
130
    protected $returnProperty;
131
132
    /**
133
     * Property name to set with output value from exec call.
134
     *
135
     * @var string
136
     */
137
    protected $outputProperty;
138
139
    /**
140
     * Whether to check the return code.
141
     *
142
     * @var bool
143
     */
144
    protected $checkreturn = false;
145
146
    private $exitValue = self::INVALID;
147
148
    private $osFamily;
149
    private $executable;
150
    private $resolveExecutable = false;
151
    private $searchPath = false;
152
    private $env;
153
154
    /**
155
     * @throws BuildException
156
     */
157 90
    public function __construct()
158
    {
159 90
        parent::__construct();
160 90
        $this->commandline = new Commandline();
161 90
        $this->env = new Environment();
162
    }
163
164
    /**
165
     * Main method: wraps execute() command.
166
     *
167
     * @throws BuildException
168
     */
169 35
    public function main()
170
    {
171 35
        if (!$this->isValidOs()) {
172 1
            return null;
173
        }
174
175
        // Suggest Task instead of executable
176 33
        if ($this->executable && ($hint = $this->findHint($this->executable))) {
177 13
            $this->log($hint, Project::MSG_VERBOSE);
178
        }
179
180
        try {
181 33
            $this->commandline->setExecutable($this->resolveExecutable($this->executable, $this->searchPath));
182
        } catch (IOException | \InvalidArgumentException $e) {
183
            throw new BuildException($e);
184
        }
185
186 33
        $this->prepare();
187 31
        $this->buildCommand();
188 31
        [$return, $output] = $this->executeCommand();
189 31
        $this->cleanup($return, $output);
190
191 30
        return $return;
192
    }
193
194
    /**
195
     * @param int $exitValue
196
     *
197
     * @return bool
198
     */
199 1
    public function isFailure($exitValue = null)
200
    {
201 1
        if (null === $exitValue) {
202
            $exitValue = $this->getExitValue();
203
        }
204
205 1
        return 0 !== $exitValue;
206
    }
207
208
    /**
209
     * Query the exit value of the process.
210
     *
211
     * @return int the exit value or self::INVALID if no exit value has
212
     *             been received
213
     */
214
    public function getExitValue(): int
215
    {
216
        return $this->exitValue;
217
    }
218
219
    /**
220
     * The executable to use.
221
     *
222
     * @param bool|string $value String or string-compatible (e.g. w/ __toString()).
223
     */
224 88
    public function setExecutable($value): void
225
    {
226 88
        if (is_bool($value)) {
227 3
            $value = true === $value ? 'true' : 'false';
228
        }
229 88
        $this->executable = $value;
230 88
        $this->commandline->setExecutable($value);
231
    }
232
233
    /**
234
     * Whether to use escapeshellcmd() to escape command.
235
     *
236
     * @param bool $escape If the command shall be escaped or not
237
     */
238 7
    public function setEscape(bool $escape): void
239
    {
240 7
        $this->escape = $escape;
241
    }
242
243
    /**
244
     * Specify the working directory for executing this command.
245
     *
246
     * @param File $dir Working directory
247
     */
248 9
    public function setDir(File $dir): void
249
    {
250 9
        $this->dir = $dir;
251
    }
252
253
    /**
254
     * Specify OS (or multiple OS) that must match in order to execute this command.
255
     *
256
     * @param string $os Operating system string (e.g. "Linux")
257
     */
258 6
    public function setOs($os): void
259
    {
260 6
        $this->os = (string) $os;
261
    }
262
263
    /**
264
     * List of operating systems on which the command may be executed.
265
     */
266
    public function getOs(): string
267
    {
268
        return $this->os;
269
    }
270
271
    /**
272
     * Restrict this execution to a single OS Family.
273
     *
274
     * @param string $osFamily the family to restrict to
275
     */
276 2
    public function setOsFamily($osFamily): void
277
    {
278 2
        $this->osFamily = strtolower($osFamily);
279
    }
280
281
    /**
282
     * Restrict this execution to a single OS Family.
283
     */
284
    public function getOsFamily()
285
    {
286
        return $this->osFamily;
287
    }
288
289
    /**
290
     * File to which output should be written.
291
     *
292
     * @param File $f Output log file
293
     */
294 6
    public function setOutput(File $f): void
295
    {
296 6
        $this->output = $f;
297
    }
298
299
    /**
300
     * File to which error output should be written.
301
     *
302
     * @param File $f Error log file
303
     */
304 4
    public function setError(File $f): void
305
    {
306 4
        $this->error = $f;
307
    }
308
309
    /**
310
     * Whether to use PHP's passthru() function instead of exec().
311
     *
312
     * @param bool $passthru If passthru shall be used
313
     */
314 4
    public function setPassthru($passthru): void
315
    {
316 4
        $this->passthru = $passthru;
317
    }
318
319
    /**
320
     * Whether to log returned output as MSG_INFO instead of MSG_VERBOSE.
321
     *
322
     * @param bool $logOutput If output shall be logged visibly
323
     */
324 1
    public function setLogoutput($logOutput): void
325
    {
326 1
        $this->logOutput = $logOutput;
327
    }
328
329
    /**
330
     * Whether to suppress all output and run in the background.
331
     *
332
     * @param bool $spawn If the command is to be run in the background
333
     */
334 4
    public function setSpawn($spawn): void
335
    {
336 4
        $this->spawn = $spawn;
337
    }
338
339
    /**
340
     * Whether to check the return code.
341
     *
342
     * @param bool $checkreturn If the return code shall be checked
343
     */
344 16
    public function setCheckreturn($checkreturn): void
345
    {
346 16
        $this->checkreturn = $checkreturn;
347
    }
348
349
    /**
350
     * The name of property to set to return value from exec() call.
351
     *
352
     * @param string $prop Property name
353
     */
354 5
    public function setReturnProperty($prop): void
355
    {
356 5
        $this->returnProperty = $prop;
357
    }
358
359
    /**
360
     * The name of property to set to output value from exec() call.
361
     *
362
     * @param string $prop Property name
363
     */
364 8
    public function setOutputProperty($prop): void
365
    {
366 8
        $this->outputProperty = $prop;
367
    }
368
369
    /**
370
     * Add an environment variable to the launched process.
371
     *
372
     * @param EnvVariable $var new environment variable
373
     */
374 2
    public function addEnv(EnvVariable $var)
375
    {
376 2
        $this->env->addVariable($var);
377
    }
378
379
    /**
380
     * Creates a nested <arg> tag.
381
     *
382
     * @return CommandlineArgument Argument object
383
     */
384 57
    public function createArg()
385
    {
386 57
        return $this->commandline->createArgument();
387
    }
388
389
    /**
390
     * Set whether to attempt to resolve the executable to a file.
391
     *
392
     * @param bool $resolveExecutable if true, attempt to resolve the
393
     *                                path of the executable
394
     */
395
    public function setResolveExecutable($resolveExecutable): void
396
    {
397
        $this->resolveExecutable = $resolveExecutable;
398
    }
399
400
    /**
401
     * Set whether to search nested, then
402
     * system PATH environment variables for the executable.
403
     *
404
     * @param bool $searchPath if true, search PATHs
405
     */
406
    public function setSearchPath($searchPath): void
407
    {
408
        $this->searchPath = $searchPath;
409
    }
410
411
    /**
412
     * Indicates whether to attempt to resolve the executable to a
413
     * file.
414
     *
415
     * @return bool the resolveExecutable flag
416
     */
417
    public function getResolveExecutable(): bool
418
    {
419
        return $this->resolveExecutable;
420
    }
421
422
    /**
423
     * Prepares the command building and execution, i.e.
424
     * changes to the specified directory.
425
     *
426
     * @throws BuildException
427
     */
428 33
    protected function prepare()
429
    {
430 33
        if (null === $this->dir) {
431 30
            $this->dir = $this->getProject()->getBasedir();
432
        }
433
434 33
        if (null === $this->commandline->getExecutable()) {
0 ignored issues
show
introduced by
The condition null === $this->commandline->getExecutable() is always false.
Loading history...
435 1
            throw new BuildException(
436 1
                'ExecTask: Please provide "executable"'
437 1
            );
438
        }
439
440
        // expand any symbolic links first
441
        try {
442 32
            if (!$this->dir->getCanonicalFile()->exists()) {
443 1
                throw new BuildException(
444 1
                    "The directory '" . (string) $this->dir . "' does not exist"
445 1
                );
446
            }
447 31
            if (!$this->dir->getCanonicalFile()->isDirectory()) {
448 31
                throw new BuildException(
449 31
                    "'" . (string) $this->dir . "' is not a directory"
450 31
                );
451
            }
452 1
        } catch (IOException $e) {
453
            throw new BuildException(
454
                "'" . (string) $this->dir . "' is not a readable directory"
455
            );
456
        }
457 31
        $this->currdir = getcwd();
458 31
        @chdir($this->dir->getPath());
459
460 31
        $this->commandline->setEscape($this->escape);
461
    }
462
463
    /**
464
     * Builds the full command to execute and stores it in $command.
465
     *
466
     * @throws BuildException
467
     *
468
     * @uses   $command
469
     */
470 31
    protected function buildCommand()
471
    {
472 31
        if (null !== $this->error) {
473 1
            $this->realCommand .= ' 2> ' . escapeshellarg($this->error->getPath());
474 1
            $this->log(
475 1
                'Writing error output to: ' . $this->error->getPath(),
476 1
                $this->logLevel
477 1
            );
478
        }
479
480 31
        if (null !== $this->output) {
481 1
            $this->realCommand .= ' 1> ' . escapeshellarg($this->output->getPath());
482 1
            $this->log(
483 1
                'Writing standard output to: ' . $this->output->getPath(),
484 1
                $this->logLevel
485 1
            );
486 30
        } elseif ($this->spawn) {
487 1
            $this->realCommand .= ' 1>/dev/null';
488 1
            $this->log('Sending output to /dev/null', $this->logLevel);
489
        }
490
491
        // If neither output nor error are being written to file
492
        // then we'll redirect error to stdout so that we can dump
493
        // it to screen below.
494
495 31
        if (null === $this->output && null === $this->error && false === $this->passthru) {
496 28
            $this->realCommand .= ' 2>&1';
497
        }
498
499
        // we ignore the spawn bool for windows
500 31
        if ($this->spawn) {
501 1
            $this->realCommand .= ' &';
502
        }
503
504 31
        $envString = '';
505 31
        $environment = $this->env->getVariables();
506 31
        if (null !== $environment) {
0 ignored issues
show
introduced by
The condition null !== $environment is always true.
Loading history...
507 2
            foreach ($environment as $variable) {
508 2
                if ($this->isPath($variable)) {
509
                    continue;
510
                }
511 2
                $this->log('Setting environment variable: ' . $variable, Project::MSG_VERBOSE);
512 2
                if (OsCondition::isOS(OsCondition::FAMILY_WINDOWS)) {
513
                    $envString .= 'set ' . $variable . '& ';
514
                } else {
515 2
                    $envString .= 'export ' . $variable . '; ';
516
                }
517
            }
518
        }
519
520 31
        $this->realCommand = $envString . $this->commandline . $this->realCommand;
521
    }
522
523
    /**
524
     * Executes the command and returns return code and output.
525
     *
526
     * @throws BuildException
527
     *
528
     * @return array array(return code, array with output)
529
     */
530 49
    protected function executeCommand()
531
    {
532 49
        $cmdl = $this->realCommand;
533
534 49
        $this->log('Executing command: ' . $cmdl, $this->logLevel);
535
536 49
        $output = [];
537 49
        $return = null;
538
539 49
        if ($this->passthru) {
540 2
            passthru($cmdl, $return);
541
        } else {
542 47
            exec($cmdl, $output, $return);
543
        }
544
545 49
        return [$return, $output];
546
    }
547
548
    /**
549
     * Runs all tasks after command execution:
550
     * - change working directory back
551
     * - log output
552
     * - verify return value.
553
     *
554
     * @param int   $return Return code
555
     * @param array $output Array with command output
556
     *
557
     * @throws BuildException
558
     */
559 31
    protected function cleanup($return, $output): void
560
    {
561 31
        if (null !== $this->dir) {
562 31
            @chdir($this->currdir);
563
        }
564
565 31
        $outloglevel = $this->logOutput ? Project::MSG_INFO : Project::MSG_VERBOSE;
566 31
        foreach ($output as $line) {
567 24
            $this->log($line, $outloglevel);
568
        }
569
570 31
        $this->maybeSetReturnPropertyValue($return);
571
572 31
        if ($this->outputProperty) {
573 4
            $this->project->setProperty(
574 4
                $this->outputProperty,
575 4
                implode("\n", $output)
576 4
            );
577
        }
578
579 31
        $this->setExitValue($return);
580
581 31
        if (0 !== $return) {
582 14
            if ($this->checkreturn) {
583 1
                throw new BuildException($this->getTaskType() . ' returned: ' . $return, $this->getLocation());
584
            }
585 13
            $this->log('Result: ' . $return, Project::MSG_ERR);
586
        }
587
    }
588
589
    /**
590
     * Set the exit value.
591
     *
592
     * @param int $value exit value of the process
593
     */
594 31
    protected function setExitValue($value): void
595
    {
596 31
        $this->exitValue = $value;
597
    }
598
599 49
    protected function maybeSetReturnPropertyValue(int $return)
600
    {
601 49
        if ($this->returnProperty) {
602 3
            $this->getProject()->setNewProperty($this->returnProperty, $return);
603
        }
604
    }
605
606
    /**
607
     * Is this the OS the user wanted?
608
     *
609
     * @return bool.
0 ignored issues
show
Documentation Bug introduced by
The doc comment bool. at position 0 could not be parsed: Unknown type name 'bool.' at position 0 in bool..
Loading history...
610
     *               <ul>
611
     *               <li>
612
     *               <li><code>true</code> if the os and osfamily attributes are null.</li>
613
     *               <li><code>true</code> if osfamily is set, and the os family and must match
614
     *               that of the current OS, according to the logic of
615
     *               {@link Os#isOs(String, String, String, String)}, and the result of the
616
     *               <code>os</code> attribute must also evaluate true.
617
     *               </li>
618
     *               <li>
619
     *               <code>true</code> if os is set, and the system.property os.name
620
     *               is found in the os attribute,</li>
621
     *               <li><code>false</code> otherwise.</li>
622
     *               </ul>
623
     */
624 54
    protected function isValidOs(): bool
625
    {
626
        //hand osfamily off to OsCondition class, if set
627 54
        if (null !== $this->osFamily && !OsCondition::isFamily($this->osFamily)) {
628
            return false;
629
        }
630
        //the Exec OS check is different from Os.isOs(), which
631
        //probes for a specific OS. Instead it searches the os field
632
        //for the current os.name
633 53
        $myos = Phing::getProperty('os.name');
634 53
        $this->log('Current OS is ' . $myos, Project::MSG_VERBOSE);
635 53
        if ((null !== $this->os) && (false === strpos($this->os, $myos))) {
636
            // this command will be executed only on the specified OS
637 2
            $this->log(
638 2
                'This OS, ' . $myos
639 2
                . ' was not found in the specified list of valid OSes: ' . $this->os,
640 2
                Project::MSG_VERBOSE
641 2
            );
642
643 2
            return false;
644
        }
645
646 51
        return true;
647
    }
648
649
    /**
650
     * The method attempts to figure out where the executable is so that we can feed
651
     * the full path. We first try basedir, then the exec dir, and then
652
     * fallback to the straight executable name (i.e. on the path).
653
     *
654
     * @param string $exec           the name of the executable
655
     * @param bool   $mustSearchPath if true, the executable will be looked up in
656
     *                               the PATH environment and the absolute path
657
     *                               is returned
658
     *
659
     * @throws BuildException
660
     * @throws IOException
661
     *
662
     * @return string the executable as a full path if it can be determined
663
     */
664 33
    protected function resolveExecutable($exec, $mustSearchPath): ?string
665
    {
666 33
        if (!$this->resolveExecutable) {
667 33
            return $exec;
668
        }
669
        // try to find the executable
670
        $executableFile = $this->getProject()->resolveFile($exec);
671
        if ($executableFile->exists()) {
672
            return $executableFile->getAbsolutePath();
673
        }
674
        // now try to resolve against the dir if given
675
        if (null !== $this->dir) {
676
            $executableFile = (new FileUtils())->resolveFile($this->dir, $exec);
677
            if ($executableFile->exists()) {
678
                return $executableFile->getAbsolutePath();
679
            }
680
        }
681
        // couldn't find it - must be on path
682
        if ($mustSearchPath) {
683
            $p = null;
684
            $environment = $this->env->getVariables();
685
            if (null !== $environment) {
0 ignored issues
show
introduced by
The condition null !== $environment is always true.
Loading history...
686
                foreach ($environment as $env) {
687
                    if ($this->isPath($env)) {
688
                        $p = new Path($this->getProject(), $this->getPath($env));
689
690
                        break;
691
                    }
692
                }
693
            }
694
            if (null === $p) {
695
                $p = new Path($this->getProject(), getenv('path'));
696
            }
697
            if (null !== $p) {
698
                $dirs = $p->listPaths();
699
                foreach ($dirs as $dir) {
700
                    $executableFile = (new FileUtils())->resolveFile(new File($dir), $exec);
701
                    if ($executableFile->exists()) {
702
                        return $executableFile->getAbsolutePath();
703
                    }
704
                }
705
            }
706
        }
707
708
        return $exec;
709
    }
710
711 2
    private function isPath($line)
712
    {
713 2
        return StringHelper::startsWith('PATH=', $line) || StringHelper::startsWith('Path=', $line);
714
    }
715
716
    private function getPath($value)
717
    {
718
        if (is_string($value)) {
719
            return StringHelper::substring($value, strlen('PATH='));
720
        }
721
722
        if (is_array($value)) {
723
            $p = $value['PATH'];
724
725
            return $p ?? $value['Path'];
726
        }
727
728
        throw new InvalidArgumentException('$value should be of type array or string.');
729
    }
730
731
    /**
732
     * Give a Task as an alternative to executable
733
     */
734 28
    public function findHint(string $executable): ?string
735
    {
736
        switch ($executable) {
737 28
            case '/usr/bin/mkdir':
738 28
            case 'mkdir':
739
                $hint = 'Consider using MkdirTask https://www.phing.info/guide/chunkhtml/MkdirTask.html';
740
                break;
741 28
            case '/usr/bin/touch':
742 28
            case 'touch':
743
                $hint = 'Consider using TouchTask https://www.phing.info/guide/chunkhtml/TouchTask.html';
744
                break;
745 28
            case '/usr/bin/truncate':
746 28
            case 'truncate':
747
                $hint = 'Consider using TruncateTask https://www.phing.info/guide/chunkhtml/TruncateTask.html';
748
                break;
749 28
            case '/usr/bin/xsltproc':
750 28
            case 'xsltproc':
751
                $hint = 'Consider using XsltTask https://www.phing.info/guide/chunkhtml/XsltTask.html';
752
                break;
753 28
            case '/usr/bin/chmod':
754 28
            case 'chmod':
755
                $hint = 'Consider using ChmodTask https://www.phing.info/guide/chunkhtml/ChmodTask.html';
756
                break;
757 28
            case '/usr/bin/chown':
758 28
            case 'chown':
759
                $hint = 'Consider using ChownTask https://www.phing.info/guide/chunkhtml/ChownTask.html';
760
                break;
761 28
            case '/usr/bin/mv':
762 28
            case 'mv':
763
                $hint = 'Consider using MoveTask https://www.phing.info/guide/chunkhtml/MoveTask.html';
764
                break;
765 28
            case 'sed':
766
                $hint = 'Consider using ReplaceTokens filter https://www.phing.info/guide/chunkhtml/ReplaceTokens.html';
767
                break;
768 28
            case '/usr/bin/rmdir':
769 28
            case 'rmdir':
770 28
            case '/usr/bin/rm':
771 28
            case 'rm':
772 28
            case '/usr/bin/unlink':
773 28
            case 'unlink':
774
                $hint = 'Consider using DeleteTask https://www.phing.info/guide/chunkhtml/DeleteTask.html';
775
                break;
776 28
            case '/usr/bin/sleep':
777 28
            case 'sleep':
778 1
                $hint = 'Consider using SleepTask https://www.phing.info/guide/chunkhtml/SleepTask.html';
779 1
                break;
780 27
            case '/usr/local/bin':
781 27
            case 'ln':
782
                $hint = 'Consider using SymlinkTask https://www.phing.info/guide/chunkhtml/SymlinkTask.html';
783
                break;
784 27
            case '/usr/bin/wget':
785 27
            case 'wget':
786
                $hint = 'Consider using HttpGetTask https://www.phing.info/guide/chunkhtml/HttpGetTask.html';
787
                break;
788 27
            case '/usr/bin/curl':
789 27
            case 'curl':
790 1
                $hint = 'Consider using HttpRequestTask https://www.phing.info/guide/chunkhtml/HttpRequestTask.html';
791 1
                break;
792 26
            case '/usr/bin/xdg-open':
793 26
            case 'xdg-open':
794 26
            case 'wslview':
795 26
            case 'open':
796 26
            case 'start':
797
                $hint = 'Consider using OpenTask https://www.phing.info/guide/chunkhtml/OpenTask.html';
798
                break;
799 26
            case '/usr/bin/zip':
800 26
            case 'zip':
801
                $hint = 'Consider using ZipTask https://www.phing.info/guide/chunkhtml/ZipTask.html';
802
                break;
803 26
            case '/usr/bin/unzip':
804 26
            case 'unzip':
805
                $hint = 'Consider using UnzipTask https://www.phing.info/guide/chunkhtml/UnzipTask.html';
806
                break;
807 26
            case '/usr/bin/tar':
808 26
            case 'tar':
809
                $hint = 'Consider using TarTask https://www.phing.info/guide/chunkhtml/TarTask.html';
810
                break;
811 26
            case '/usr/bin/echo':
812 26
            case 'echo':
813 11
                $hint = 'Consider using EchoTask https://www.phing.info/guide/chunkhtml/EchoTask.html';
814 11
                break;
815
            default:
816 15
                $hint = null;
817 15
                break;
818
        }
819
820 28
        return $hint;
821
    }
822
}
823