Issues (557)

Security Analysis    not enabled

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  Header Injection
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

src/Phing/Phing.php (6 issues)

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;
22
23
use Exception;
24
use Phing\Exception\BuildException;
25
use Phing\Exception\ConfigurationException;
26
use Phing\Exception\ExitStatusException;
27
use Phing\Input\ConsoleInputHandler;
28
use Phing\Io\File;
29
use Phing\Io\FileOutputStream;
30
use Phing\Io\FileParserFactory;
31
use Phing\Io\FileReader;
32
use Phing\Io\FileSystem;
33
use Phing\Io\FileUtils;
34
use Phing\Io\IOException;
35
use Phing\Io\OutputStream;
36
use Phing\Io\PrintStream;
37
use Phing\Listener\BuildLogger;
38
use Phing\Listener\DefaultLogger;
39
use Phing\Listener\SilentLogger;
40
use Phing\Listener\StreamRequiredBuildLogger;
41
use Phing\Parser\ProjectConfigurator;
42
use Phing\Util\DefaultClock;
43
use Phing\Util\Diagnostics;
44
use Phing\Util\Properties;
45
use Phing\Util\SizeHelper;
46
use Phing\Util\StringHelper;
47
use SebastianBergmann\Version;
48
use Symfony\Component\Console\Output\ConsoleOutput;
49
use Throwable;
50
51
use function array_filter;
52
use function array_map;
53
use function array_reduce;
54
use function gmdate;
55
use function implode;
56
use function method_exists;
57
use function sprintf;
58
use function strlen;
59
use function strval;
60
use function trim;
61
62
use const DATE_RFC7231;
63
use const PHP_EOL;
64
65
/**
66
 * Entry point into Phing.  This class handles the full lifecycle of a build -- from
67
 * parsing & handling commandline arguments to assembling the project to shutting down
68
 * and cleaning up in the end.
69
 *
70
 * If you are invoking Phing from an external application, this is still
71
 * the class to use.  Your application can invoke the start() method, passing
72
 * any commandline arguments or additional properties.
73
 *
74
 * @author Andreas Aderhold <[email protected]>
75
 * @author Hans Lellelid <[email protected]>
76
 */
77
class Phing
78
{
79
    /**
80
     * Alias for phar file.
81
     */
82
    public const PHAR_ALIAS = 'phing.phar';
83
84
    /**
85
     * The default build file name.
86
     */
87
    public const DEFAULT_BUILD_FILENAME = 'build.xml';
88
    public const DEFAULT_BUILD_CONTENT = <<<'XML'
89
        <?xml version="1.0" encoding="UTF-8" ?>
90
91
        <project name="" description="" default="">
92
93
            <target name="" description="">
94
95
            </target>
96
97
        </project>
98
        XML;
99
    public const PHING_HOME = 'phing.home';
100
    public const PHP_VERSION = 'php.version';
101
    public const PHP_INTERPRETER = 'php.interpreter';
102
103
    /**
104
     * Our current message output status. Follows Project::MSG_XXX.
105
     */
106
    private static $msgOutputLevel = Project::MSG_INFO;
107
    /**
108
     * Set of properties that are passed in from commandline or invoking code.
109
     *
110
     * @var Properties
111
     */
112
    private static $definedProps;
113
    /**
114
     * Used by utility function getResourcePath().
115
     */
116
    private static $importPaths;
117
    /**
118
     * Cache of the Phing version information when it has been loaded.
119
     */
120
    private static $phingVersion;
121
    /**
122
     * Cache of the short Phing version information when it has been loaded.
123
     */
124
    private static $phingShortVersion;
125
    /**
126
     * System-wide static properties (moved from System).
127
     */
128
    private static $properties = [];
129
    /**
130
     * Static system timer.
131
     */
132
    private static $timer;
133
    /**
134
     * The current Project.
135
     */
136
    private static $currentProject;
137
    /**
138
     * Whether to capture PHP errors to buffer.
139
     */
140
    private static $phpErrorCapture = false;
141
    /**
142
     * Array of captured PHP errors.
143
     */
144
    private static $capturedPhpErrors = [];
145
    /**
146
     * @var OUtputStream stream for standard output
147
     */
148
    private static $out;
149
    /**
150
     * @var OutputStream stream for error output
151
     */
152
    private static $err;
153
    /**
154
     * @var bool whether we are using a logfile
155
     */
156
    private static $isLogFileUsed = false;
157
    /**
158
     * Array to hold original ini settings that Phing changes (and needs
159
     * to restore in restoreIni() method).
160
     *
161
     * @var array Struct of array(setting-name => setting-value)
162
     *
163
     * @see restoreIni()
164
     */
165
    private static $origIniSettings = [];
166
    /**
167
     * PhingFile that we are using for configuration.
168
     */
169
    private $buildFile;
170
    /**
171
     * The build targets.
172
     */
173
    private $targets = [];
174
    /**
175
     * Names of classes to add as listeners to project.
176
     */
177
    private $listeners = [];
178
    /**
179
     * keep going mode.
180
     *
181
     * @var bool
182
     */
183
    private $keepGoingMode = false;
184
    private $loggerClassname;
185
    /**
186
     * The class to handle input (can be only one).
187
     */
188
    private $inputHandlerClassname;
189
    /**
190
     * Whether or not log output should be reduced to the minimum.
191
     *
192
     * @var bool
193
     */
194
    private $silent = false;
195
    /**
196
     * Indicates whether phing should run in strict mode.
197
     */
198
    private $strictMode = false;
199
    /**
200
     * Indicates if this phing should be run.
201
     */
202
    private $readyToRun = false;
203
    /**
204
     * Indicates we should only parse and display the project help information.
205
     */
206
    private $projectHelp = false;
207
    /**
208
     * Whether to values in a property file should override existing values.
209
     */
210
    private $propertyFileOverride = false;
211
    /**
212
     * Whether or not output to the log is to be unadorned.
213
     */
214
    private $emacsMode = false;
215
216
    /**
217
     * @var string
218
     */
219
    private $searchForThis;
220
    private $propertyFiles = [];
221
222
    /**
223
     * Gets the stream to use for standard (non-error) output.
224
     *
225
     * @return OutputStream
226
     */
227
    public static function getOutputStream()
228
    {
229
        return self::$out;
230
    }
231
232
    /**
233
     * Gets the stream to use for error output.
234
     *
235
     * @return OutputStream
236
     */
237
    public static function getErrorStream()
238
    {
239
        return self::$err;
240
    }
241
242
    /**
243
     * Command line entry point. This method kicks off the building
244
     * of a project object and executes a build using either a given
245
     * target or the default target.
246
     *
247
     * @param array $args command line args
248
     *
249
     * @throws Exception
250
     */
251
    public static function fire($args): void
252
    {
253
        self::start($args);
254
    }
255
256
    /**
257
     * Entry point allowing for more options from other front ends.
258
     *
259
     * This method encapsulates the complete build lifecycle.
260
     *
261
     * @param array      $args                     the commandline args passed to phing shell script
262
     * @param array|null $additionalUserProperties Any additional properties to be passed to Phing (alternative front-end might implement this).
263
     *                                             These additional properties will be available using the getDefinedProperty() method and will
264
     *                                             be added to the project's "user" properties
265
     *
266
     * @throws Exception - if there is an error during build
267
     *
268
     * @see    runBuild()
269
     * @see    execute()
270
     */
271
    public static function start($args, ?array $additionalUserProperties = null)
272
    {
273
        try {
274
            $m = new self();
275
            $m->execute($args);
276
        } catch (Exception $exc) {
277
            self::handleLogfile();
278
            self::printMessage($exc);
279
            self::statusExit(1);
280
281
            return;
282
        }
283
284
        if (null !== $additionalUserProperties) {
285
            foreach ($additionalUserProperties as $key => $value) {
286
                $m::setDefinedProperty($key, $value);
287
            }
288
        }
289
290
        // expect the worst
291
        $exitCode = 1;
292
293
        try {
294
            try {
295
                $m->runBuild();
296
                $exitCode = 0;
297
            } catch (ExitStatusException $ese) {
298
                $exitCode = $ese->getCode();
299
                if (0 !== $exitCode) {
300
                    self::handleLogfile();
301
302
                    throw $ese;
303
                }
304
            }
305
        } catch (BuildException $exc) {
306
            // avoid printing output twice: self::printMessage($exc);
307
        } catch (Throwable $exc) {
308
            self::printMessage($exc);
309
        } finally {
310
            self::handleLogfile();
311
        }
312
        self::statusExit($exitCode);
313
    }
314
315
    /**
316
     * Setup/initialize Phing environment from commandline args.
317
     *
318
     * @param array $args commandline args passed to phing shell
319
     *
320
     * @throws ConfigurationException
321
     */
322
    public function execute($args): void
323
    {
324
        self::$definedProps = new Properties();
325
        $this->searchForThis = null;
326
327
        // 1) First handle any options which should always
328
        // Note: The order in which these are executed is important (if multiple of these options are specified)
329
330
        if (in_array('-help', $args) || in_array('-h', $args)) {
331
            static::printUsage();
332
333
            return;
334
        }
335
336
        if (in_array('-version', $args) || in_array('-v', $args)) {
337
            static::printVersion();
338
339
            return;
340
        }
341
342
        if (in_array('-init', $args) || in_array('-i', $args)) {
343
            $key = array_search('-init', $args) ?: array_search('-i', $args);
344
            $path = $args[$key + 1] ?? null;
345
346
            self::init($path);
347
348
            return;
349
        }
350
351
        if (in_array('-diagnostics', $args)) {
352
            Diagnostics::doReport(new PrintStream(self::$out));
353
354
            return;
355
        }
356
357
        // 2) Next pull out stand-alone args.
358
        // Note: The order in which these are executed is important (if multiple of these options are specified)
359
360
        if (
361
            false !== ($key = array_search('-quiet', $args, true))
362
            || false !== ($key = array_search(
363
                '-q',
364
                $args,
365
                true
366
            ))
367
        ) {
368
            self::$msgOutputLevel = Project::MSG_WARN;
369
            unset($args[$key]);
370
        }
371
372
        if (
373
            false !== ($key = array_search('-emacs', $args, true))
374
            || false !== ($key = array_search('-e', $args, true))
375
        ) {
376
            $this->emacsMode = true;
377
            unset($args[$key]);
378
        }
379
380
        if (false !== ($key = array_search('-verbose', $args, true))) {
381
            self::$msgOutputLevel = Project::MSG_VERBOSE;
382
            unset($args[$key]);
383
        }
384
385
        if (false !== ($key = array_search('-debug', $args, true))) {
386
            self::$msgOutputLevel = Project::MSG_DEBUG;
387
            unset($args[$key]);
388
        }
389
390
        if (
391
            false !== ($key = array_search('-silent', $args, true))
392
            || false !== ($key = array_search('-S', $args, true))
393
        ) {
394
            $this->silent = true;
395
            unset($args[$key]);
396
        }
397
398
        if (false !== ($key = array_search('-propertyfileoverride', $args, true))) {
399
            $this->propertyFileOverride = true;
400
            unset($args[$key]);
401
        }
402
403
        // 3) Finally, cycle through to parse remaining args
404
        //
405
        $keys = array_keys($args); // Use keys and iterate to max(keys) since there may be some gaps
406
        $max = $keys ? max($keys) : -1;
407
        for ($i = 0; $i <= $max; ++$i) {
408
            if (!array_key_exists($i, $args)) {
409
                // skip this argument, since it must have been removed above.
410
                continue;
411
            }
412
413
            $arg = $args[$i];
414
415
            if ('-logfile' == $arg) {
416
                try {
417
                    // see: http://phing.info/trac/ticket/65
418
                    if (!isset($args[$i + 1])) {
419
                        $msg = "You must specify a log file when using the -logfile argument\n";
420
421
                        throw new ConfigurationException($msg);
422
                    }
423
424
                    $logFile = new File($args[++$i]);
425
                    $out = new FileOutputStream($logFile); // overwrite
426
                    self::setOutputStream($out);
427
                    self::setErrorStream($out);
428
                    self::$isLogFileUsed = true;
429
                } catch (IOException $ioe) {
430
                    $msg = 'Cannot write on the specified log file. Make sure the path exists and you have write permissions.';
431
432
                    throw new ConfigurationException($msg, $ioe);
433
                }
434
            } elseif ('-buildfile' == $arg || '-file' == $arg || '-f' == $arg) {
435
                if (!isset($args[$i + 1])) {
436
                    $msg = 'You must specify a buildfile when using the -buildfile argument.';
437
438
                    throw new ConfigurationException($msg);
439
                }
440
441
                $this->buildFile = new File($args[++$i]);
442
            } elseif ('-listener' == $arg) {
443
                if (!isset($args[$i + 1])) {
444
                    $msg = 'You must specify a listener class when using the -listener argument';
445
446
                    throw new ConfigurationException($msg);
447
                }
448
449
                $this->listeners[] = $args[++$i];
450
            } elseif (StringHelper::startsWith('-D', $arg)) {
451
                // Evaluating the property information //
452
                // Checking whether arg. is not just a switch, and next arg. does not starts with switch identifier
453
                if (('-D' == $arg) && (!StringHelper::startsWith('-', $args[$i + 1]))) {
454
                    $name = $args[++$i];
455
                } else {
456
                    $name = substr($arg, 2);
457
                }
458
459
                $value = null;
460
                $posEq = strpos($name, '=');
461
                if (false !== $posEq) {
462
                    $value = substr($name, $posEq + 1);
463
                    $name = substr($name, 0, $posEq);
464
                } elseif ($i < count($args) - 1 && !StringHelper::startsWith('-D', $args[$i + 1])) {
465
                    $value = $args[++$i];
466
                }
467
                self::$definedProps->setProperty($name, $value);
468
            } elseif ('-logger' == $arg) {
469
                if (!isset($args[$i + 1])) {
470
                    $msg = 'You must specify a classname when using the -logger argument';
471
472
                    throw new ConfigurationException($msg);
473
                }
474
475
                $this->loggerClassname = $args[++$i];
476
            } elseif ('-no-strict' == $arg) {
477
                $this->strictMode = false;
478
            } elseif ('-strict' == $arg) {
479
                $this->strictMode = true;
480
            } elseif ('-inputhandler' == $arg) {
481
                if (null !== $this->inputHandlerClassname) {
482
                    throw new ConfigurationException('Only one input handler class may be specified.');
483
                }
484
                if (!isset($args[$i + 1])) {
485
                    $msg = 'You must specify a classname when using the -inputhandler argument';
486
487
                    throw new ConfigurationException($msg);
488
                }
489
490
                $this->inputHandlerClassname = $args[++$i];
491
            } elseif ('-propertyfile' === $arg) {
492
                $i = $this->handleArgPropertyFile($args, $i);
493
            } elseif ('-keep-going' === $arg || '-k' === $arg) {
494
                $this->keepGoingMode = true;
495
            } elseif ('-longtargets' == $arg) {
496
                self::$definedProps->setProperty('phing.showlongtargets', 1);
497
            } elseif ('-projecthelp' == $arg || '-targets' == $arg || '-list' == $arg || '-l' == $arg || '-p' == $arg) {
498
                // set the flag to display the targets and quit
499
                $this->projectHelp = true;
500
            } elseif ('-find' == $arg) {
501
                // eat up next arg if present, default to build.xml
502
                if ($i < count($args) - 1) {
503
                    $this->searchForThis = $args[++$i];
504
                } else {
505
                    $this->searchForThis = self::DEFAULT_BUILD_FILENAME;
506
                }
507
            } elseif ('-' == substr($arg, 0, 1)) {
508
                // we don't have any more args
509
                self::printUsage();
510
                self::$err->write(PHP_EOL);
511
512
                throw new ConfigurationException('Unknown argument: ' . $arg);
513
            } else {
514
                // if it's no other arg, it may be the target
515
                $this->targets[] = $arg;
516
            }
517
        }
518
519
        // if buildFile was not specified on the command line,
520
        if (null === $this->buildFile) {
521
            // but -find then search for it
522
            if (null !== $this->searchForThis) {
523
                $this->buildFile = $this->findBuildFile(self::getProperty('user.dir'), $this->searchForThis);
0 ignored issues
show
$this->searchForThis of type void is incompatible with the type string expected by parameter $suffix of Phing\Phing::findBuildFile(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

523
                $this->buildFile = $this->findBuildFile(self::getProperty('user.dir'), /** @scrutinizer ignore-type */ $this->searchForThis);
Loading history...
524
            } else {
525
                $this->buildFile = new File(self::DEFAULT_BUILD_FILENAME);
526
            }
527
        }
528
529
        try {
530
            // make sure buildfile (or buildfile.dist) exists
531
            if (!$this->buildFile->exists()) {
532
                $distFile = new File($this->buildFile->getAbsolutePath() . '.dist');
533
                if (!$distFile->exists()) {
534
                    throw new ConfigurationException(
535
                        'Buildfile: ' . $this->buildFile->__toString() . ' does not exist!'
536
                    );
537
                }
538
                $this->buildFile = $distFile;
539
            }
540
541
            // make sure it's not a directory
542
            if ($this->buildFile->isDirectory()) {
543
                throw new ConfigurationException('Buildfile: ' . $this->buildFile->__toString() . ' is a dir!');
544
            }
545
        } catch (IOException $e) {
546
            // something else happened, buildfile probably not readable
547
            throw new ConfigurationException('Buildfile: ' . $this->buildFile->__toString() . ' is not readable!');
548
        }
549
550
        $this->loadPropertyFiles();
551
552
        $this->readyToRun = true;
553
    }
554
555
    /**
556
     * Prints the usage of how to use this class.
557
     */
558 1
    public static function printUsage()
559
    {
560 1
        $msg = <<<'TEXT'
561
            phing [options] [target [target2 [target3] ...]]
562
            Options:
563
              -h -help                 print this message
564
              -l -list                 list available targets in this project
565
              -i -init [file]          generates an initial buildfile
566
              -v -version              print the version information and exit
567
              -q -quiet                be extra quiet
568
              -S -silent               print nothing but task outputs and build failures
569
                 -verbose              be extra verbose
570
                 -debug                print debugging information
571
              -e -emacs                produce logging information without adornments
572
                 -diagnostics          print diagnostics information
573
                 -strict               runs build in strict mode, considering a warning as error
574
                 -no-strict            runs build normally (overrides buildfile attribute)
575
                 -longtargets          show target descriptions during build
576
                 -logfile <file>       use given file for log
577
                 -logger <classname>   the class which is to perform logging
578
                 -listener <classname> add an instance of class as a project listener
579
              -f -buildfile <file>     use given buildfile
580
                 -D<property>=<value>  use value for given property
581
              -k -keep-going           execute all targets that do not depend on failed target(s)
582
                 -propertyfile <file>  load all properties from file
583
                 -propertyfileoverride values in property file override existing values
584
                 -find <file>          search for buildfile towards the root of the filesystem and use it
585
                 -inputhandler <file>  the class to use to handle user input
586
587
            Report bugs to https://github.com/phingofficial/phing/issues
588
589 1
            TEXT;
590
591 1
        self::$err->write($msg);
592
    }
593
594
    /**
595
     * Prints the current Phing version.
596
     */
597
    public static function printVersion()
598
    {
599
        self::$out->write(self::getPhingVersion() . PHP_EOL);
600
    }
601
602
    /**
603
     * Gets the current Phing version based on VERSION.TXT file. Once the information
604
     * has been loaded once, it's cached and returned from the cache on future
605
     * calls.
606
     *
607
     * @throws ConfigurationException
608
     */
609 6
    public static function getPhingVersion(): string
610
    {
611 6
        if (self::$phingVersion === null) {
612
            $versionPath = self::getResourcePath('phing/etc/VERSION.TXT');
613
            if (null === $versionPath) {
614
                $versionPath = self::getResourcePath('etc/VERSION.TXT');
615
            }
616
            if (null === $versionPath) {
617
                throw new ConfigurationException('No VERSION.TXT file found; try setting phing.home environment variable.');
618
            }
619
620
            try { // try to read file
621
                $file = new File($versionPath);
622
                $reader = new FileReader($file);
623
                $phingVersion = trim($reader->read());
624
            } catch (IOException $iox) {
625
                throw new ConfigurationException("Can't read version information file");
626
            }
627
628
            $basePath = dirname(__DIR__, 2);
629
630
            $version = new Version($phingVersion, $basePath);
631
            self::$phingShortVersion = (method_exists($version, 'asString') ? $version->asString() : $version->getVersion());
632
633
            self::$phingVersion = 'Phing ' . self::$phingShortVersion;
634
        }
635 6
        return self::$phingVersion;
636
    }
637
638
    /**
639
     * Returns the short Phing version information, if available. Once the information
640
     * has been loaded once, it's cached and returned from the cache on future
641
     * calls.
642
     *
643
     * @return the short Phing version information as a string
0 ignored issues
show
The type Phing\the was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
644
     *
645
     * @throws ConfigurationException
646
     */
647
    public static function getPhingShortVersion(): string
648
    {
649
        if (self::$phingShortVersion === null) {
650
            self::getPhingVersion();
651
        }
652
        return self::$phingShortVersion;
653
    }
654
655
    /**
656
     * Looks on include path for specified file.
657
     *
658
     * @param string $path
659
     *
660
     * @return null|string file found (null if no file found)
661
     */
662 906
    public static function getResourcePath($path): ?string
663
    {
664 906
        if (null === self::$importPaths) {
665
            self::$importPaths = self::explodeIncludePath();
666
        }
667
668 906
        $path = str_replace(['\\', '/'], DIRECTORY_SEPARATOR, $path);
669
670 906
        foreach (self::$importPaths as $prefix) {
671 906
            $testPath = $prefix . DIRECTORY_SEPARATOR . $path;
672 906
            if (file_exists($testPath)) {
673
                return $testPath;
674
            }
675
        }
676
677
        // Check for the property phing.home
678 906
        $homeDir = self::getProperty(self::PHING_HOME);
679 906
        if ($homeDir) {
680 906
            $testPath = $homeDir . DIRECTORY_SEPARATOR . $path;
681 906
            if (file_exists($testPath)) {
682 906
                return $testPath;
683
            }
684
        }
685
686
        // Check for the phing home of phar archive
687
        if (0 === strpos(self::$importPaths[0], 'phar://')) {
688
            $testPath = self::$importPaths[0] . '/../' . $path;
689
            if (file_exists($testPath)) {
690
                return $testPath;
691
            }
692
        }
693
694
        // Do one additional check based on path of current file (Phing.php)
695
        $maybeHomeDir = realpath(__DIR__ . DIRECTORY_SEPARATOR);
696
        $testPath = $maybeHomeDir . DIRECTORY_SEPARATOR . $path;
697
        if (file_exists($testPath)) {
698
            return $testPath;
699
        }
700
701
        return null;
702
    }
703
704
    /**
705
     * Explode an include path into an array.
706
     *
707
     * If no path provided, uses current include_path. Works around issues that
708
     * occur when the path includes stream schemas.
709
     *
710
     * Pulled from Zend_Loader::explodeIncludePath() in ZF1.
711
     *
712
     * @copyright Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
713
     * @license   http://framework.zend.com/license/new-bsd New BSD License
714
     *
715
     * @param null|string $path
716
     *
717
     * @return array
718
     */
719 17
    public static function explodeIncludePath($path = null)
720
    {
721 17
        if (null === $path) {
722 17
            $path = get_include_path();
723
        }
724
725 17
        if (PATH_SEPARATOR === ':') {
726
            // On *nix systems, include_paths which include paths with a stream
727
            // schema cannot be safely explode'd, so we have to be a bit more
728
            // intelligent in the approach.
729 17
            $paths = preg_split('#:(?!//)#', $path);
730
        } else {
731
            $paths = explode(PATH_SEPARATOR, $path);
732
        }
733
734 17
        return $paths;
735
    }
736
737
    /**
738
     * Returns property value for a System property.
739
     * System properties are "global" properties like application.startdir,
740
     * and user.dir.  Many of these correspond to similar properties in Java
741
     * or Ant.
742
     *
743
     * @param string $propName
744
     *
745
     * @return string value of found property (or null, if none found)
746
     */
747 924
    public static function getProperty($propName)
748
    {
749
        // some properties are detemined on each access
750
        // some are cached, see below
751
752
        // default is the cached value:
753 924
        $val = self::$properties[$propName] ?? null;
754
755
        // special exceptions
756
        switch ($propName) {
757 924
            case 'user.dir':
758 153
                $val = getcwd();
759
760 153
                break;
761
        }
762
763 924
        return $val;
764
    }
765
766
    /**
767
     * Creates generic buildfile.
768
     *
769
     * @param string $path
770
     */
771
    public static function init($path)
772
    {
773
        if ($buildfilePath = self::initPath($path)) {
774
            self::initWrite($buildfilePath);
775
        }
776
    }
777
778
    /**
779
     * Sets the stream to use for standard (non-error) output.
780
     *
781
     * @param OutputStream $stream the stream to use for standard output
782
     */
783 1
    public static function setOutputStream(OutputStream $stream)
784
    {
785 1
        self::$out = $stream;
786
    }
787
788
    /**
789
     * Sets the stream to use for error output.
790
     *
791
     * @param OutputStream $stream the stream to use for error output
792
     */
793 1
    public static function setErrorStream(OutputStream $stream): void
794
    {
795 1
        self::$err = $stream;
796
    }
797
798
    /**
799
     * Making output level a static property so that this property
800
     * can be accessed by other parts of the system, enabling
801
     * us to display more information -- e.g. backtraces -- for "debug" level.
802
     *
803
     * @return int
804
     */
805 9
    public static function getMsgOutputLevel()
806
    {
807 9
        return self::$msgOutputLevel;
808
    }
809
810
    /**
811
     * Prints the message of the Exception if it's not null.
812
     */
813
    public static function printMessage(Throwable $t): void
814
    {
815
        if (null === self::$err) { // Make sure our error output is initialized
816
            self::initializeOutputStreams();
817
        }
818
        if (self::getMsgOutputLevel() >= Project::MSG_VERBOSE) {
819
            self::$err->write((string) $t . PHP_EOL);
820
        } else {
821
            self::$err->write($t->getMessage() . PHP_EOL);
822
        }
823
    }
824
825
    /**
826
     * Performs any shutdown routines, such as stopping timers.
827
     *
828
     * @throws IOException
829
     */
830 1
    public static function shutdown(): void
831
    {
832 1
        FileSystem::getFileSystem()::deleteFilesOnExit();
833 1
        self::$msgOutputLevel = Project::MSG_INFO;
834 1
        self::restoreIni();
835 1
        self::getTimer()->stop();
836
    }
837
838
    /**
839
     * Returns reference to DefaultClock object.
840
     */
841 2
    public static function getTimer(): DefaultClock
842
    {
843 2
        if (null === self::$timer) {
844
            self::$timer = new DefaultClock();
845
        }
846
847 2
        return self::$timer;
848
    }
849
850
    /**
851
     * This sets a property that was set via command line or otherwise passed into Phing.
852
     *
853
     * @param string $name
854
     * @param mixed  $value
855
     *
856
     * @return mixed value of found property (or null, if none found)
857
     */
858
    public static function setDefinedProperty($name, $value)
859
    {
860
        return self::$definedProps->setProperty($name, $value);
861
    }
862
863
    /**
864
     * Executes the build.
865
     *
866
     * @throws IOException
867
     * @throws Throwable
868
     */
869
    public function runBuild(): void
870
    {
871
        if (!$this->readyToRun) {
872
            return;
873
        }
874
875
        $project = new Project();
876
877
        self::setCurrentProject($project);
878
        set_error_handler(['Phing\Phing', 'handlePhpError']);
879
880
        $error = null;
881
882
        try {
883
            $this->addBuildListeners($project);
884
            $this->addInputHandler($project);
885
886
            // set this right away, so that it can be used in logging.
887
            $project->setUserProperty('phing.file', $this->buildFile->getAbsolutePath());
888
            $project->setUserProperty('phing.dir', dirname($this->buildFile->getAbsolutePath()));
889
            $project->setUserProperty('phing.version', static::getPhingVersion());
890
            $project->fireBuildStarted();
891
            $project->init();
892
            $project->setKeepGoingMode($this->keepGoingMode);
893
894
            $e = self::$definedProps->keys();
895
            while (count($e)) {
896
                $arg = (string) array_shift($e);
897
                $value = (string) self::$definedProps->getProperty($arg);
898
                $project->setUserProperty($arg, $value);
899
            }
900
            unset($e);
901
902
            // first use the Configurator to create the project object
903
            // from the given build file.
904
905
            ProjectConfigurator::configureProject($project, $this->buildFile);
906
907
            // Set the project mode
908
            $project->setStrictMode(StringHelper::booleanValue($this->strictMode));
909
910
            // make sure that minimum required phing version is satisfied
911
            $this->comparePhingVersion($project->getPhingVersion());
912
913
            if ($this->projectHelp) {
914
                $this->printDescription($project);
915
                $this->printTargets($project);
916
917
                return;
918
            }
919
920
            // make sure that we have a target to execute
921
            if (0 === count($this->targets)) {
922
                $this->targets[] = $project->getDefaultTarget();
923
            }
924
925
            $project->executeTargets($this->targets);
926
        } catch (Throwable $t) {
927
            $error = $t;
928
929
            throw $t;
930
        } finally {
931
            if (!$this->projectHelp) {
932
                try {
933
                    $project->fireBuildFinished($error);
934
                } catch (Exception $e) {
935
                    self::$err->write('Caught an exception while logging the end of the build.  Exception was:' . PHP_EOL);
936
                    self::$err->write($e->getTraceAsString());
937
                    if (null !== $error) {
938
                        self::$err->write('There has been an error prior to that:' . PHP_EOL);
939
                        self::$err->write($error->getTraceAsString());
940
                    }
941
942
                    throw new BuildException($error);
943
                }
944
            } elseif (null !== $error) {
945
                $project->log($error->getMessage(), Project::MSG_ERR);
946
            }
947
948
            restore_error_handler();
949
            self::unsetCurrentProject();
950
        }
951
    }
952
953
    /**
954
     * Import a class, supporting the following conventions:
955
     * - PEAR style (@see http://pear.php.net/manual/en/standards.naming.php)
956
     * - PSR-0 (@see https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md).
957
     *
958
     * @param string $classname Name of class
959
     * @param mixed  $classpath String or object supporting __toString()
960
     *
961
     * @throws BuildException - if cannot find the specified file
962
     *
963
     * @return string the unqualified classname (which can be instantiated)
964
     */
965 909
    public static function import($classname, $classpath = null)
966
    {
967
        // first check to see that the class specified hasn't already been included.
968 909
        if (class_exists($classname)) {
969 907
            return $classname;
970
        }
971
972 7
        $filename = strtr($classname, ['_' => DIRECTORY_SEPARATOR, '\\' => DIRECTORY_SEPARATOR]) . '.php';
973
974 7
        Phing::importFile($filename, $classpath);
975
976 5
        return $classname;
977
    }
978
979
    /**
980
     * Import a PHP file.
981
     *
982
     * This used to be named __import, however PHP has reserved all method names
983
     * with a double underscore prefix for future use.
984
     *
985
     * @param string $path      Path to the PHP file
986
     * @param mixed  $classpath String or object supporting __toString()
987
     *
988
     * @throws ConfigurationException
989
     */
990 10
    public static function importFile($path, $classpath = null)
991
    {
992 10
        if ($classpath) {
993
            // Apparently casting to (string) no longer invokes __toString() automatically.
994 2
            if (is_object($classpath)) {
995
                $classpath = $classpath->__toString();
996
            }
997
998
            // classpaths are currently additive, but we also don't want to just
999
            // indiscriminantly prepand/append stuff to the include_path.  This means
1000
            // we need to parse current incldue_path, and prepend any
1001
            // specified classpath locations that are not already in the include_path.
1002
            //
1003
            // NOTE:  the reason why we do it this way instead of just changing include_path
1004
            // and then changing it back, is that in many cases applications (e.g. Propel) will
1005
            // include/require class files from within method calls.  This means that not all
1006
            // necessary files will be included in this import() call, and hence we can't
1007
            // change the include_path back without breaking those apps.  While this method could
1008
            // be more expensive than switching & switching back (not sure, but maybe), it makes it
1009
            // possible to write far less expensive run-time applications (e.g. using Propel), which is
1010
            // really where speed matters more.
1011
1012 2
            $curr_parts = Phing::explodeIncludePath();
1013 2
            $add_parts = Phing::explodeIncludePath($classpath);
1014 2
            $new_parts = array_diff($add_parts, $curr_parts);
1015 2
            if ($new_parts) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $new_parts of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
1016 1
                set_include_path(implode(PATH_SEPARATOR, array_merge($new_parts, $curr_parts)));
1017
            }
1018
        }
1019
1020 10
        $ret = include_once $path;
1021
1022 8
        if (false === $ret) {
1023
            $msg = "Error importing {$path}";
1024
            if (self::getMsgOutputLevel() >= Project::MSG_DEBUG) {
1025
                $x = new Exception('for-path-trace-only');
1026
                $msg .= $x->getTraceAsString();
1027
            }
1028
1029
            throw new ConfigurationException($msg);
1030
        }
1031
    }
1032
1033
    /**
1034
     * Print the project description, if any.
1035
     *
1036
     * @throws IOException
1037
     */
1038
    public function printDescription(Project $project): void
1039
    {
1040
        if (null !== $project->getDescription()) {
1041
            $project->log($project->getDescription());
1042
        }
1043
    }
1044
1045
    /**
1046
     * Print out a list of all targets in the current buildfile.
1047
     */
1048 1
    public function printTargets(Project $project)
1049
    {
1050 1
        $visibleTargets = array_filter($project->getTargets(), function (Target $target) {
1051 1
            return !$target->isHidden() && !empty($target->getName());
1052 1
        });
1053 1
        $padding = array_reduce($visibleTargets, function (int $carry, Target $target) {
1054
            return max(strlen($target->getName()), $carry);
1055 1
        }, 0);
1056 1
        $categories = [
1057 1
            'Default target:' => array_filter($visibleTargets, function (Target $target) use ($project) {
1058
                return trim(strval($target)) === $project->getDefaultTarget();
1059 1
            }),
1060 1
            'Main targets:' => array_filter($visibleTargets, function (Target $target) {
1061
                return !empty($target->getDescription());
1062 1
            }),
1063 1
            'Subtargets:' => array_filter($visibleTargets, function (Target $target) {
1064
                return empty($target->getDescription());
1065 1
            }),
1066 1
        ];
1067 1
        foreach ($categories as $title => $targets) {
1068 1
            $targetList = $this->generateTargetList($title, $targets, $padding);
1069 1
            $project->log($targetList, Project::MSG_WARN);
1070
        }
1071
    }
1072
1073
    /**
1074
     * Unsets the current Project.
1075
     */
1076 1
    public static function unsetCurrentProject(): void
1077
    {
1078 1
        self::$currentProject = null;
1079
    }
1080
1081
    /**
1082
     * Error handler for PHP errors encountered during the build.
1083
     * This uses the logging for the currently configured project.
1084
     *
1085
     * @param $level
1086
     * @param string $message
1087
     * @param $file
1088
     * @param $line
1089
     */
1090
    public static function handlePhpError($level, $message, $file, $line)
1091
    {
1092
        // don't want to print suppressed errors
1093
        if (!(error_reporting() & $level)) {
1094
            return true;
1095
        }
1096
        if (self::$phpErrorCapture) {
1097
            self::$capturedPhpErrors[] = [
1098
                'message' => $message,
1099
                'level' => $level,
1100
                'line' => $line,
1101
                'file' => $file,
1102
            ];
1103
        } else {
1104
            $message = '[PHP Error] ' . $message;
1105
            $message .= ' [line ' . $line . ' of ' . $file . ']';
1106
1107
            switch ($level) {
1108
                case E_USER_DEPRECATED:
1109
                case E_DEPRECATED:
1110
                case E_NOTICE:
1111
                case E_USER_NOTICE:
1112
                    self::log($message, Project::MSG_VERBOSE);
1113
1114
                    break;
1115
1116
                case E_WARNING:
1117
                case E_USER_WARNING:
1118
                    self::log($message, Project::MSG_WARN);
1119
1120
                    break;
1121
1122
                case E_ERROR:
1123
                case E_USER_ERROR:
1124
                default:
1125
                    self::log($message, Project::MSG_ERR);
1126
            } // switch
1127
        } // if phpErrorCapture
1128
    }
1129
1130
    /**
1131
     * A static convenience method to send a log to the current (last-setup) Project.
1132
     * If there is no currently-configured Project, then this will do nothing.
1133
     *
1134
     * @param string $message
1135
     * @param int    $priority project::MSG_INFO, etc
1136
     */
1137 1
    public static function log($message, $priority = Project::MSG_INFO): void
1138
    {
1139 1
        $p = self::getCurrentProject();
1140 1
        if ($p) {
0 ignored issues
show
$p is of type Phing\Project, thus it always evaluated to true.
Loading history...
1141 1
            $p->log($message, $priority);
1142
        }
1143
    }
1144
1145
    /**
1146
     * Gets the current Project.
1147
     *
1148
     * @return Project current Project or NULL if none is set yet/still
1149
     */
1150 8
    public static function getCurrentProject()
1151
    {
1152 8
        return self::$currentProject;
1153
    }
1154
1155
    /**
1156
     * Sets the current Project.
1157
     *
1158
     * @param Project $p
1159
     */
1160 1
    public static function setCurrentProject($p): void
1161
    {
1162 1
        self::$currentProject = $p;
1163
    }
1164
1165
    /**
1166
     * Begins capturing PHP errors to a buffer.
1167
     * While errors are being captured, they are not logged.
1168
     */
1169
    public static function startPhpErrorCapture(): void
1170
    {
1171
        self::$phpErrorCapture = true;
1172
        self::$capturedPhpErrors = [];
1173
    }
1174
1175
    /**
1176
     * Stops capturing PHP errors to a buffer.
1177
     * The errors will once again be logged after calling this method.
1178
     */
1179
    public static function stopPhpErrorCapture(): void
1180
    {
1181
        self::$phpErrorCapture = false;
1182
    }
1183
1184
    /**
1185
     * Clears the captured errors without affecting the starting/stopping of the capture.
1186
     */
1187
    public static function clearCapturedPhpErrors(): void
1188
    {
1189
        self::$capturedPhpErrors = [];
1190
    }
1191
1192
    /**
1193
     * Gets any PHP errors that were captured to buffer.
1194
     *
1195
     * @return array array('message' => message, 'line' => line number, 'file' => file name, 'level' => error level)
1196
     */
1197
    public static function getCapturedPhpErrors()
1198
    {
1199
        return self::$capturedPhpErrors;
1200
    }
1201
1202
    /**
1203
     * This gets a property that was set via command line or otherwise passed into Phing.
1204
     * "Defined" in this case means "externally defined".  The reason this method exists is to
1205
     * provide a public means of accessing commandline properties for (e.g.) logger or listener
1206
     * scripts.  E.g. to specify which logfile to use, PearLogger needs to be able to access
1207
     * the pear.log.name property.
1208
     *
1209
     * @param string $name
1210
     *
1211
     * @return string value of found property (or null, if none found)
1212
     */
1213
    public static function getDefinedProperty($name)
1214
    {
1215
        return self::$definedProps->getProperty($name);
1216
    }
1217
1218
    /**
1219
     * Retuns reference to all properties.
1220
     */
1221 906
    public static function &getProperties()
1222
    {
1223 906
        return self::$properties;
1224
    }
1225
1226
    /**
1227
     * Start up Phing.
1228
     * Sets up the Phing environment but does not initiate the build process.
1229
     *
1230
     * @throws exception - If the Phing environment cannot be initialized
1231
     */
1232 1
    public static function startup(): void
1233
    {
1234
        // setup STDOUT and STDERR defaults
1235 1
        self::initializeOutputStreams();
1236
1237
        // some init stuff
1238 1
        self::getTimer()->start();
1239
1240 1
        self::setSystemConstants();
1241 1
        self::setIncludePaths();
1242 1
        self::setIni();
1243
    }
1244
1245
    /**
1246
     * @param $propName
1247
     * @param $propValue
1248
     *
1249
     * @return string
1250
     */
1251 8
    public static function setProperty($propName, $propValue)
1252
    {
1253 8
        $propName = (string) $propName;
1254 8
        $oldValue = self::getProperty($propName);
1255 8
        self::$properties[$propName] = $propValue;
1256
1257 8
        return $oldValue;
1258
    }
1259
1260
    /**
1261
     * Returns buildfile's path.
1262
     *
1263
     * @param $path
1264
     *
1265
     * @throws ConfigurationException
1266
     *
1267
     * @return string
1268
     */
1269
    protected static function initPath($path)
1270
    {
1271
        // Fallback
1272
        if (empty($path)) {
1273
            $defaultDir = self::getProperty('application.startdir');
1274
            $path = $defaultDir . DIRECTORY_SEPARATOR . self::DEFAULT_BUILD_FILENAME;
1275
        }
1276
1277
        // Adding filename if necessary
1278
        if (is_dir($path)) {
1279
            $path .= DIRECTORY_SEPARATOR . self::DEFAULT_BUILD_FILENAME;
1280
        }
1281
1282
        // Check if path is available
1283
        $dirname = dirname($path);
1284
        if (is_dir($dirname) && !is_file($path)) {
1285
            return $path;
1286
        }
1287
1288
        // Path is valid, but buildfile already exists
1289
        if (is_file($path)) {
1290
            throw new ConfigurationException('Buildfile already exists.');
1291
        }
1292
1293
        throw new ConfigurationException('Invalid path for sample buildfile.');
1294
    }
1295
1296
    /**
1297
     * Writes sample buildfile.
1298
     *
1299
     * If $buildfilePath does not exist, the buildfile is created.
1300
     *
1301
     * @param $buildfilePath buildfile's location
0 ignored issues
show
Documentation Bug introduced by
The doc comment buildfile's at position 0 could not be parsed: Unknown type name 'buildfile's' at position 0 in buildfile's.
Loading history...
1302
     *
1303
     * @throws ConfigurationException
1304
     */
1305
    protected static function initWrite($buildfilePath): void
1306
    {
1307
        // Overwriting protection
1308
        if (file_exists($buildfilePath)) {
1309
            throw new ConfigurationException('Cannot overwrite existing file.');
1310
        }
1311
1312
        file_put_contents($buildfilePath, self::DEFAULT_BUILD_CONTENT);
1313
    }
1314
1315
    /**
1316
     * This operation is expected to call `exit($int)`, which
1317
     * is what the base version does.
1318
     * However, it is possible to do something else.
1319
     *
1320
     * @param int $exitCode code to exit with
1321
     */
1322
    protected static function statusExit($exitCode): void
1323
    {
1324
        Phing::shutdown();
1325
1326
        exit($exitCode);
0 ignored issues
show
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
1327
    }
1328
1329
    /**
1330
     * Handle the -propertyfile argument.
1331
     *
1332
     * @throws ConfigurationException
1333
     * @throws IOException
1334
     */
1335
    private function handleArgPropertyFile(array $args, int $pos): int
1336
    {
1337
        if (!isset($args[$pos + 1])) {
1338
            throw new ConfigurationException('You must specify a filename when using the -propertyfile argument');
1339
        }
1340
1341
        $this->propertyFiles[] = $args[++$pos];
1342
1343
        return $pos;
1344
    }
1345
1346
    /**
1347
     * Search parent directories for the build file.
1348
     *
1349
     * Takes the given target as a suffix to append to each
1350
     * parent directory in search of a build file.  Once the
1351
     * root of the file-system has been reached an exception
1352
     * is thrown.
1353
     *
1354
     * @param string $start  start file path
1355
     * @param string $suffix suffix filename to look for in parents
1356
     *
1357
     * @throws ConfigurationException
1358
     *
1359
     * @return File A handle to the build file
1360
     */
1361
    private function findBuildFile($start, $suffix)
1362
    {
1363
        if (self::getMsgOutputLevel() >= Project::MSG_INFO) {
1364
            self::$out->write('Searching for ' . $suffix . ' ...' . PHP_EOL);
1365
        }
1366
1367
        $parent = new File((new File($start))->getAbsolutePath());
1368
        $file = new File($parent, $suffix);
1369
1370
        // check if the target file exists in the current directory
1371
        while (!$file->exists()) {
1372
            // change to parent directory
1373
            $parent = $parent->getParentFile();
1374
1375
            // if parent is null, then we are at the root of the fs,
1376
            // complain that we can't find the build file.
1377
            if (null === $parent) {
1378
                throw new ConfigurationException('Could not locate a build file!');
1379
            }
1380
            // refresh our file handle
1381
            $file = new File($parent, $suffix);
1382
        }
1383
1384
        return $file;
1385
    }
1386
1387
    /**
1388
     * @throws IOException
1389
     */
1390
    private function loadPropertyFiles(): void
1391
    {
1392
        foreach ($this->propertyFiles as $filename) {
1393
            $fileParserFactory = new FileParserFactory();
1394
            $fileParser = $fileParserFactory->createParser(pathinfo($filename, PATHINFO_EXTENSION));
1395
            $p = new Properties(null, $fileParser);
1396
1397
            try {
1398
                $p->load(new File($filename));
1399
            } catch (IOException $e) {
1400
                self::$out->write('Could not load property file ' . $filename . ': ' . $e->getMessage());
1401
            }
1402
            foreach ($p->getProperties() as $prop => $value) {
1403
                self::$definedProps->setProperty($prop, $value);
1404
            }
1405
        }
1406
    }
1407
1408
    /**
1409
     * Close logfiles, if we have been writing to them.
1410
     *
1411
     * @since Phing 2.3.0
1412
     */
1413
    private static function handleLogfile(): void
1414
    {
1415
        if (self::$isLogFileUsed) {
1416
            self::$err->close();
1417
            self::$out->close();
1418
        }
1419
    }
1420
1421
    /**
1422
     * Sets the stdout and stderr streams if they are not already set.
1423
     */
1424 1
    private static function initializeOutputStreams()
1425
    {
1426 1
        if (null === self::$out) {
1427
            if (!defined('STDOUT')) {
1428
                self::$out = new OutputStream(fopen('php://stdout', 'w'));
1429
            } else {
1430
                self::$out = new OutputStream(STDOUT);
1431
            }
1432
        }
1433 1
        if (null === self::$err) {
1434
            if (!defined('STDERR')) {
1435
                self::$err = new OutputStream(fopen('php://stderr', 'w'));
1436
            } else {
1437
                self::$err = new OutputStream(STDERR);
1438
            }
1439
        }
1440
    }
1441
1442
    /**
1443
     * Restores [most] PHP INI values to their pre-Phing state.
1444
     *
1445
     * Currently the following settings are not restored:
1446
     *  - max_execution_time (because getting current time limit is not possible)
1447
     *  - memory_limit (which may have been increased by Phing)
1448
     */
1449 1
    private static function restoreIni(): void
1450
    {
1451 1
        foreach (self::$origIniSettings as $settingName => $settingValue) {
1452
            switch ($settingName) {
1453 1
                case 'error_reporting':
1454 1
                    error_reporting($settingValue);
1455
1456 1
                    break;
1457
1458
                default:
1459 1
                    ini_set($settingName, $settingValue);
1460
            }
1461
        }
1462
    }
1463
1464
    /**
1465
     * Bind any registered build listeners to this project.
1466
     *
1467
     * This means adding the logger and any build listeners that were specified
1468
     * with -listener arg.
1469
     *
1470
     * @throws BuildException
1471
     * @throws ConfigurationException
1472
     */
1473
    private function addBuildListeners(Project $project)
1474
    {
1475
        // Add the default listener
1476
        $project->addBuildListener($this->createLogger());
1477
1478
        foreach ($this->listeners as $listenerClassname) {
1479
            try {
1480
                $clz = Phing::import($listenerClassname);
1481
            } catch (Exception $e) {
1482
                $msg = 'Unable to instantiate specified listener '
1483
                    . 'class ' . $listenerClassname . ' : '
1484
                    . $e->getMessage();
1485
1486
                throw new ConfigurationException($msg);
1487
            }
1488
1489
            $listener = new $clz();
1490
1491
            if ($listener instanceof StreamRequiredBuildLogger) {
1492
                throw new ConfigurationException('Unable to add ' . $listenerClassname . ' as a listener, since it requires explicit error/output streams. (You can specify it as a -logger.)');
1493
            }
1494
            $project->addBuildListener($listener);
1495
        }
1496
    }
1497
1498
    /**
1499
     * Creates the default build logger for sending build events to the log.
1500
     *
1501
     * @throws BuildException
1502
     *
1503
     * @return BuildLogger The created Logger
1504
     */
1505
    private function createLogger()
1506
    {
1507
        if ($this->silent) {
1508
            $logger = new SilentLogger();
1509
            self::$msgOutputLevel = Project::MSG_WARN;
1510
        } elseif (null !== $this->loggerClassname) {
1511
            self::import($this->loggerClassname);
1512
            // get class name part
1513
            $classname = self::import($this->loggerClassname);
1514
            $logger = new $classname();
1515
            if (!($logger instanceof BuildLogger)) {
1516
                throw new BuildException($classname . ' does not implement the BuildLogger interface.');
1517
            }
1518
        } else {
1519
            $logger = new DefaultLogger();
1520
        }
1521
        $logger->setMessageOutputLevel(self::$msgOutputLevel);
1522
        $logger->setOutputStream(self::$out);
1523
        $logger->setErrorStream(self::$err);
1524
        $logger->setEmacsMode($this->emacsMode);
1525
1526
        return $logger;
1527
    }
1528
1529
    /**
1530
     * Creates the InputHandler and adds it to the project.
1531
     *
1532
     * @param Project $project the project instance
1533
     *
1534
     * @throws ConfigurationException
1535
     */
1536
    private function addInputHandler(Project $project): void
1537
    {
1538
        if (null === $this->inputHandlerClassname) {
1539
            $handler = new ConsoleInputHandler(STDIN, new ConsoleOutput());
1540
        } else {
1541
            try {
1542
                $clz = Phing::import($this->inputHandlerClassname);
1543
                $handler = new $clz();
1544
                if (null !== $project && method_exists($handler, 'setProject')) {
1545
                    $handler->setProject($project);
1546
                }
1547
            } catch (Exception $e) {
1548
                $msg = 'Unable to instantiate specified input handler '
1549
                    . 'class ' . $this->inputHandlerClassname . ' : '
1550
                    . $e->getMessage();
1551
1552
                throw new ConfigurationException($msg);
1553
            }
1554
        }
1555
        $project->setInputHandler($handler);
1556
    }
1557
1558
    /**
1559
     * @param string $version
1560
     *
1561
     * @throws BuildException
1562
     * @throws ConfigurationException
1563
     */
1564
    private function comparePhingVersion($version): void
1565
    {
1566
        $current = self::getPhingShortVersion();
1567
1568
        // make sure that version checks are not applied to trunk
1569
        if ('dev' === $current) {
1570
            return;
1571
        }
1572
1573
        if (-1 == version_compare($current, $version)) {
1574
            throw new BuildException(
1575
                sprintf('Incompatible Phing version (%s). Version "%s" required.', $current, $version)
1576
            );
1577
        }
1578
    }
1579
1580
    /**
1581
     * Returns a formatted list of target names with an optional description.
1582
     *
1583
     * @param string   $title   Title for this list
1584
     * @param Target[] $targets Targets in this list
1585
     * @param int      $padding Padding for name column
1586
     */
1587 1
    private function generateTargetList(string $title, array $targets, int $padding): string
1588
    {
1589 1
        usort($targets, function (Target $a, Target $b) {
1590
            return $a->getName() <=> $b->getName();
1591 1
        });
1592
1593 1
        $header = <<<HEADER
1594 1
            {$title}
1595
            -------------------------------------------------------------------------------
1596
1597 1
            HEADER;
1598
1599 1
        $getDetails = function (Target $target) use ($padding): string {
1600
            $details = [];
1601
            if (!empty($target->getDescription())) {
1602
                $details[] = $target->getDescription();
1603
            }
1604
            if (!empty($target->getDependencies())) {
1605
                $details[] = ' - depends on: ' . implode(', ', $target->getDependencies());
1606
            }
1607
            if (!empty($target->getIf())) {
1608
                $details[] = ' - if property: ' . $target->getIf();
1609
            }
1610
            if (!empty($target->getUnless())) {
1611
                $details[] = ' - unless property: ' . $target->getUnless();
1612
            }
1613
            $detailsToString = function (?string $name, ?string $detail) use ($padding): string {
1614
                return sprintf(" %-{$padding}s  %s", $name, $detail);
1615
            };
1616
1617
            return implode(PHP_EOL, array_map($detailsToString, [$target->getName()], $details));
1618 1
        };
1619
1620 1
        return $header . implode(PHP_EOL, array_map($getDetails, $targets)) . PHP_EOL;
1621
    }
1622
1623
    /**
1624
     * Set System constants which can be retrieved by calling Phing::getProperty($propName).
1625
     */
1626 1
    private static function setSystemConstants(): void
1627
    {
1628
        /*
1629
         * PHP_OS returns on
1630
         *   WindowsNT4.0sp6  => WINNT
1631
         *   Windows2000      => WINNT
1632
         *   Windows ME       => WIN32
1633
         *   Windows 98SE     => WIN32
1634
         *   FreeBSD 4.5p7    => FreeBSD
1635
         *   Redhat Linux     => Linux
1636
         *   Mac OS X         => Darwin
1637
         */
1638 1
        self::setProperty('host.os', PHP_OS);
1639
1640
        // this is used by some tasks too
1641 1
        self::setProperty('os.name', PHP_OS);
1642
1643
        // it's still possible this won't be defined,
1644
        // e.g. if Phing is being included in another app w/o
1645
        // using the phing.php script.
1646 1
        if (!defined('PHP_CLASSPATH')) {
1647
            define('PHP_CLASSPATH', get_include_path());
1648
        }
1649
1650 1
        self::setProperty('php.classpath', PHP_CLASSPATH);
1651
1652
        // try to determine the host filesystem and set system property
1653
        // used by Fileself::getFileSystem to instantiate the correct
1654
        // abstraction layer
1655
1656 1
        if (PHP_OS_FAMILY === 'Windows') {
1657
            self::setProperty('host.fstype', 'WINDOWS');
1658
            self::setProperty('user.home', getenv('HOMEDRIVE') . getenv('HOMEPATH'));
1659
        } else {
1660 1
            self::setProperty('host.fstype', 'UNIX');
1661 1
            self::setProperty('user.home', getenv('HOME'));
1662
        }
1663 1
        self::setProperty(self::PHP_INTERPRETER, PHP_BINARY);
1664 1
        self::setProperty('file.separator', FileUtils::getSeparator());
1665 1
        self::setProperty('line.separator', PHP_EOL);
1666 1
        self::setProperty('path.separator', FileUtils::getPathSeparator());
1667 1
        self::setProperty(self::PHP_VERSION, PHP_VERSION);
1668 1
        self::setProperty('php.tmpdir', sys_get_temp_dir());
1669 1
        self::setProperty('application.startdir', getcwd());
1670 1
        self::setProperty('phing.startTime', gmdate(DATE_RFC7231));
1671
1672
        // try to detect machine dependent information
1673 1
        $sysInfo = [];
1674 1
        if (function_exists('posix_uname') && 0 !== stripos(PHP_OS, 'WIN')) {
1675 1
            $sysInfo = posix_uname();
1676
        } else {
1677
            $sysInfo['nodename'] = php_uname('n');
1678
            $sysInfo['machine'] = php_uname('m');
1679
            //this is a not so ideal substition, but maybe better than nothing
1680
            $sysInfo['domain'] = $_SERVER['SERVER_NAME'] ?? 'unknown';
1681
            $sysInfo['release'] = php_uname('r');
1682
            $sysInfo['version'] = php_uname('v');
1683
        }
1684
1685 1
        self::setProperty('host.name', $sysInfo['nodename'] ?? 'unknown');
1686 1
        self::setProperty('host.arch', $sysInfo['machine'] ?? 'unknown');
1687 1
        self::setProperty('host.domain', $sysInfo['domain'] ?? 'unknown');
1688 1
        self::setProperty('host.os.release', $sysInfo['release'] ?? 'unknown');
1689 1
        self::setProperty('host.os.version', $sysInfo['version'] ?? 'unknown');
1690 1
        unset($sysInfo);
1691
    }
1692
1693
    /**
1694
     * Sets the include path to PHP_CLASSPATH constant (if this has been defined).
1695
     *
1696
     * @throws ConfigurationException - if the include_path could not be set (for some bizarre reason)
1697
     */
1698 1
    private static function setIncludePaths(): void
1699
    {
1700 1
        if (defined('PHP_CLASSPATH')) {
1701 1
            $result = set_include_path(PHP_CLASSPATH);
1702 1
            if (false === $result) {
1703
                throw new ConfigurationException('Could not set PHP include_path.');
1704
            }
1705 1
            self::$origIniSettings['include_path'] = $result; // save original value for setting back later
1706
        }
1707
    }
1708
1709
    /**
1710
     * Sets PHP INI values that Phing needs.
1711
     */
1712 1
    private static function setIni(): void
1713
    {
1714 1
        self::$origIniSettings['error_reporting'] = error_reporting(E_ALL);
1715
1716
        // We won't bother storing original max_execution_time, since 1) the value in
1717
        // php.ini may be wrong (and there's no way to get the current value) and
1718
        // 2) it would mean something very strange to set it to a value less than time script
1719
        // has already been running, which would be the likely change.
1720
1721 1
        set_time_limit(0);
1722
1723 1
        self::$origIniSettings['short_open_tag'] = ini_set('short_open_tag', 'off');
1724 1
        self::$origIniSettings['default_charset'] = ini_set('default_charset', 'iso-8859-1');
1725
1726 1
        $mem_limit = (int) SizeHelper::fromHumanToBytes(ini_get('memory_limit'));
1727 1
        if ($mem_limit < (32 * 1024 * 1024) && $mem_limit > -1) {
1728
            // We do *not* need to save the original value here, since we don't plan to restore
1729
            // this after shutdown (we don't trust the effectiveness of PHP's garbage collection).
1730
            ini_set('memory_limit', '32M'); // nore: this may need to be higher for many projects
1731
        }
1732
    }
1733
}
1734