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

keradus / PHP-CS-Fixer / 17319949156

29 Aug 2025 09:20AM UTC coverage: 94.696% (-0.05%) from 94.744%
17319949156

push

github

keradus
CS

28333 of 29920 relevant lines covered (94.7%)

45.63 hits per line

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

75.0
/src/Linter/ProcessLinter.php
1
<?php
2

3
declare(strict_types=1);
4

5
/*
6
 * This file is part of PHP CS Fixer.
7
 *
8
 * (c) Fabien Potencier <fabien@symfony.com>
9
 *     Dariusz Rumiński <dariusz.ruminski@gmail.com>
10
 *
11
 * This source file is subject to the MIT license that is bundled
12
 * with this source code in the file LICENSE.
13
 */
14

15
namespace PhpCsFixer\Linter;
16

17
use PhpCsFixer\FileReader;
18
use PhpCsFixer\FileRemoval;
19
use Symfony\Component\Filesystem\Exception\IOException;
20
use Symfony\Component\Process\PhpExecutableFinder;
21
use Symfony\Component\Process\Process;
22

23
/**
24
 * Handle PHP code linting using separated process of `php -l _file_`.
25
 *
26
 * @author Dariusz Rumiński <dariusz.ruminski@gmail.com>
27
 *
28
 * @internal
29
 *
30
 * @no-named-arguments Parameter names are not covered by the backward compatibility promise.
31
 */
32
final class ProcessLinter implements LinterInterface
33
{
34
    private FileRemoval $fileRemoval;
35

36
    private ProcessLinterProcessBuilder $processBuilder;
37

38
    /**
39
     * Temporary file for code linting.
40
     */
41
    private ?string $temporaryFile = null;
42

43
    /**
44
     * @param null|string $executable PHP executable, null for autodetection
45
     */
46
    public function __construct(?string $executable = null)
47
    {
48
        if (null === $executable) {
9✔
49
            $executableFinder = new PhpExecutableFinder();
9✔
50
            $executable = $executableFinder->find(false);
9✔
51

52
            if (false === $executable) {
9✔
53
                throw new UnavailableLinterException('Cannot find PHP executable.');
×
54
            }
55

56
            if ('phpdbg' === \PHP_SAPI) {
9✔
57
                if (!str_contains($executable, 'phpdbg')) {
×
58
                    throw new UnavailableLinterException('Automatically found PHP executable is non-standard phpdbg. Could not find proper PHP executable.');
×
59
                }
60

61
                // automatically found executable is `phpdbg`, let us try to fallback to regular `php`
62
                $executable = str_replace('phpdbg', 'php', $executable);
×
63

64
                if (!is_executable($executable)) {
×
65
                    throw new UnavailableLinterException('Automatically found PHP executable is phpdbg. Could not find proper PHP executable.');
×
66
                }
67
            }
68
        }
69

70
        $this->processBuilder = new ProcessLinterProcessBuilder($executable);
9✔
71
        $this->fileRemoval = new FileRemoval();
9✔
72
    }
73

74
    public function __destruct()
75
    {
76
        if (null !== $this->temporaryFile) {
9✔
77
            $this->fileRemoval->delete($this->temporaryFile);
3✔
78
        }
79
    }
80

81
    /**
82
     * This class is not intended to be serialized,
83
     * and cannot be deserialized (see __wakeup method).
84
     */
85
    public function __sleep(): array
86
    {
87
        throw new \BadMethodCallException('Cannot serialize '.self::class);
1✔
88
    }
89

90
    /**
91
     * Disable the deserialization of the class to prevent attacker executing
92
     * code by leveraging the __destruct method.
93
     *
94
     * @see https://owasp.org/www-community/vulnerabilities/PHP_Object_Injection
95
     */
96
    public function __wakeup(): void
97
    {
98
        throw new \BadMethodCallException('Cannot unserialize '.self::class);
1✔
99
    }
100

101
    public function isAsync(): bool
102
    {
103
        return true;
1✔
104
    }
105

106
    public function lintFile(string $path): LintingResultInterface
107
    {
108
        return new ProcessLintingResult($this->createProcessForFile($path), $path);
3✔
109
    }
110

111
    public function lintSource(string $source): LintingResultInterface
112
    {
113
        return new ProcessLintingResult($this->createProcessForSource($source), $this->temporaryFile);
3✔
114
    }
115

116
    /**
117
     * @param string $path path to file
118
     */
119
    private function createProcessForFile(string $path): Process
120
    {
121
        // in case php://stdin
122
        if (!is_file($path)) {
6✔
123
            return $this->createProcessForSource(FileReader::createSingleton()->read($path));
×
124
        }
125

126
        $process = $this->processBuilder->build($path);
6✔
127
        $process->setTimeout(10);
6✔
128
        $process->start();
6✔
129

130
        return $process;
6✔
131
    }
132

133
    /**
134
     * Create process that lint PHP code.
135
     *
136
     * @param string $source code
137
     */
138
    private function createProcessForSource(string $source): Process
139
    {
140
        if (null === $this->temporaryFile) {
3✔
141
            $this->temporaryFile = tempnam(sys_get_temp_dir(), 'cs_fixer_tmp_');
3✔
142
            $this->fileRemoval->observe($this->temporaryFile);
3✔
143
        }
144

145
        if (false === @file_put_contents($this->temporaryFile, $source)) {
3✔
146
            throw new IOException(\sprintf('Failed to write file "%s".', $this->temporaryFile), 0, null, $this->temporaryFile);
×
147
        }
148

149
        return $this->createProcessForFile($this->temporaryFile);
3✔
150
    }
151
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc