Compiler::compile()   B
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 30
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 17
nc 2
nop 0
dl 0
loc 30
rs 8.8571
c 0
b 0
f 0
1
<?php
2
3
/*
4
 * This file is part of the php-formatter package
5
 *
6
 * Copyright (c) 2014-2016 Marc Morera
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 *
11
 * Feel free to edit as you please, and have fun.
12
 *
13
 * @author Marc Morera <[email protected]>
14
 */
15
16
namespace Mmoreram\PHPFormatter\Compiler;
17
18
use DateTime;
19
use Phar;
20
use RuntimeException;
21
use Symfony\Component\Finder\Finder;
22
use Symfony\Component\Finder\SplFileInfo;
23
use Symfony\Component\Process\Process;
24
25
/**
26
 * Class Compiler.
27
 */
28
class Compiler
29
{
30
    /**
31
     * @var string
32
     *
33
     * version
34
     */
35
    protected $version;
36
37
    /**
38
     * @var DateTime
39
     *
40
     * versionDate
41
     */
42
    protected $versionDate;
43
44
    /**
45
     * Compiles composer into a single phar file.
46
     *
47
     * @throws RuntimeException
48
     */
49
    public function compile()
50
    {
51
        $pharFilePath = dirname(__FILE__) . '/../../../build/php-formatter.phar';
52
53
        if (file_exists($pharFilePath)) {
54
            unlink($pharFilePath);
55
        }
56
57
        $this->loadVersion();
58
59
        /**
60
         * Creating phar object.
61
         */
62
        $phar = new Phar($pharFilePath, 0, 'php-formatter.phar');
63
        $phar->setSignatureAlgorithm(\Phar::SHA1);
64
65
        $phar->startBuffering();
66
67
        $this
68
            ->addPHPFiles($phar)
69
            ->addVendorFiles($phar)
70
            ->addComposerVendorFiles($phar)
71
            ->addBin($phar)
72
            ->addStub($phar)
73
            ->addLicense($phar);
74
75
        $phar->stopBuffering();
76
77
        unset($phar);
78
    }
79
80
    /**
81
     * Add a file into the phar package.
82
     *
83
     * @param Phar        $phar  Phar object
84
     * @param SplFileInfo $file  File to add
85
     * @param bool        $strip strip
86
     *
87
     * @return Compiler self Object
88
     */
89
    protected function addFile(
90
        Phar $phar,
91
        SplFileInfo $file,
92
        $strip = true
93
    ) {
94
        $path = strtr(str_replace(dirname(dirname(dirname(__DIR__))) . DIRECTORY_SEPARATOR, '', $file->getRealPath()), '\\', '/');
95
        $content = $file->getContents();
96
        if ($strip) {
97
            $content = $this->stripWhitespace($content);
98
        } elseif ('LICENSE' === $file->getBasename()) {
99
            $content = "\n" . $content . "\n";
100
        }
101
102
        if ($path === 'src/Composer/Composer.php') {
103
            $content = str_replace('@package_version@', $this->version, $content);
104
            $content = str_replace('@release_date@', $this->versionDate, $content);
105
        }
106
107
        $phar->addFromString($path, $content);
108
109
        return $this;
110
    }
111
112
    /**
113
     * Add bin into Phar.
114
     *
115
     * @param Phar $phar Phar
116
     *
117
     * @return Compiler self Object
118
     */
119
    protected function addBin(Phar $phar)
120
    {
121
        $content = file_get_contents(__DIR__ . '/../../../bin/php-formatter');
122
        $content = preg_replace('{^#!/usr/bin/env php\s*}', '', $content);
123
        $phar->addFromString('bin/php-formatter', $content);
124
125
        return $this;
126
    }
127
128
    /**
129
     * Removes whitespace from a PHP source string while preserving line numbers.
130
     *
131
     * @param string $source A PHP string
132
     *
133
     * @return string The PHP string with the whitespace removed
134
     */
135
    protected function stripWhitespace($source)
136
    {
137
        if (!function_exists('token_get_all')) {
138
            return $source;
139
        }
140
141
        $output = '';
142
        foreach (token_get_all($source) as $token) {
143
            if (is_string($token)) {
144
                $output .= $token;
145
            } elseif (in_array($token[0], [T_COMMENT, T_DOC_COMMENT])) {
146
                $output .= str_repeat("\n", substr_count($token[1], "\n"));
147
            } elseif (T_WHITESPACE === $token[0]) {
148
                // reduce wide spaces
149
                $whitespace = preg_replace('{[ \t]+}', ' ', $token[1]);
150
                // normalize newlines to \n
151
                $whitespace = preg_replace('{(?:\r\n|\r|\n)}', "\n", $whitespace);
152
                // trim leading spaces
153
                $whitespace = preg_replace('{\n +}', "\n", $whitespace);
154
                $output .= $whitespace;
155
            } else {
156
                $output .= $token[1];
157
            }
158
        }
159
160
        return $output;
161
    }
162
163
    protected function addStub(Phar $phar)
164
    {
165
        $stub = <<<'EOF'
166
#!/usr/bin/env php
167
<?php
168
169
/*
170
 * This file is part of the php-formatter package
171
 *
172
 * Copyright (c) 2014 Marc Morera
173
 *
174
 * For the full copyright and license information, please view the LICENSE
175
 * file that was distributed with this source code.
176
 *
177
 * Feel free to edit as you please, and have fun.
178
 *
179
 * @author Marc Morera <[email protected]>
180
 */
181
182
Phar::mapPhar('php-formatter.phar');
183
184
require 'phar://php-formatter.phar/bin/php-formatter';
185
186
__HALT_COMPILER();
187
EOF;
188
        $phar->setStub($stub);
189
190
        return $this;
191
    }
192
193
    /**
194
     * Add php files.
195
     *
196
     * @param Phar $phar Phar instance
197
     *
198
     * @return Compiler self Object
199
     */
200
    private function addPHPFiles(Phar $phar)
201
    {
202
        /**
203
         * All *.php files.
204
         */
205
        $finder = new Finder();
206
        $finder
207
            ->files()
208
            ->ignoreVCS(true)
209
            ->name('*.php')
210
            ->notName('Compiler.php')
211
            ->notName('ClassLoader.php')
212
            ->in(realpath(__DIR__ . '/../../../src'));
213
214
        foreach ($finder->files() as $file) {
215
            $this->addFile($phar, $file);
216
        }
217
218
        return $this;
219
    }
220
221
    /**
222
     * Add vendor files.
223
     *
224
     * @param Phar $phar Phar instance
225
     *
226
     * @return Compiler self Object
227
     */
228
    private function addVendorFiles(Phar $phar)
229
    {
230
        $vendorPath = __DIR__ . '/../../../vendor/';
231
232
        /**
233
         * All *.php files.
234
         */
235
        $finder = new Finder();
236
        $finder
237
            ->files()
238
            ->ignoreVCS(true)
239
            ->name('*.php')
240
            ->exclude('Tests')
241
            ->in(realpath($vendorPath . 'symfony/'));
242
243
        foreach ($finder as $file) {
244
            $this->addFile($phar, $file);
245
        }
246
247
        return $this;
248
    }
249
250
    /**
251
     * Add composer vendor files.
252
     *
253
     * @param Phar $phar Phar
254
     *
255
     * @return Compiler self Object
256
     */
257
    private function addComposerVendorFiles(Phar $phar)
258
    {
259
        $vendorPath = __DIR__ . '/../../../vendor/';
260
261
        /*
262
         * Adding composer vendor files
263
         */
264
        $this
265
            ->addFile($phar, new SplFileInfo($vendorPath . 'autoload.php', $vendorPath, $vendorPath . 'autoload.php'))
266
            ->addFile($phar, new SplFileInfo($vendorPath . 'composer/autoload_namespaces.php', $vendorPath . 'composer', $vendorPath . 'composer/autoload_namespaces.php'))
267
            ->addFile($phar, new SplFileInfo($vendorPath . 'composer/autoload_psr4.php', $vendorPath . 'composer', $vendorPath . 'composer/autoload_psr4.php'))
268
            ->addFile($phar, new SplFileInfo($vendorPath . 'composer/autoload_classmap.php', $vendorPath . 'composer', $vendorPath . 'composer/autoload_classmap.php'))
269
            ->addFile($phar, new SplFileInfo($vendorPath . 'composer/autoload_real.php', $vendorPath . 'composer', $vendorPath . 'composer/autoload_real.php'))
270
            ->addFile($phar, new SplFileInfo($vendorPath . 'composer/ClassLoader.php', $vendorPath . 'composer', $vendorPath . 'composer/ClassLoader.php'));
271
272
        if (file_exists($vendorPath . 'composer/include_paths.php')) {
273
            $this->addFile($phar, new SplFileInfo($vendorPath . 'composer/include_paths.php', $vendorPath . 'composer', $vendorPath . 'composer/include_paths.php'));
274
        }
275
276
        return $this;
277
    }
278
279
    /**
280
     * Add license.
281
     *
282
     * @param Phar $phar Phar
283
     *
284
     * @return Compiler self Object
285
     */
286
    private function addLicense(Phar $phar)
287
    {
288
        $this->addFile($phar, new SplFileInfo(__DIR__ . '/../../../LICENSE', __DIR__ . '/../../..', __DIR__ . '/../../../LICENSE'), false);
289
290
        return $this;
291
    }
292
293
    /**
294
     * Load versions.
295
     */
296
    private function loadVersion()
297
    {
298
        $process = new Process('git log --pretty="%H" -n1 HEAD', __DIR__);
299
        if ($process->run() != 0) {
0 ignored issues
show
Bug introduced by
It seems like you are loosely comparing $process->run() of type integer|null to 0; this is ambiguous as not only 0 == 0 is true, but null == 0 is true, too. Consider using a strict comparison ===.
Loading history...
300
            throw new \RuntimeException('Can\'t run git log. You must ensure to run compile from php-formatter git repository clone and that git binary is available.');
301
        }
302
        $this->version = trim($process->getOutput());
303
304
        $process = new Process('git log -n1 --pretty=%ci HEAD', __DIR__);
305
        if ($process->run() != 0) {
0 ignored issues
show
Bug introduced by
It seems like you are loosely comparing $process->run() of type integer|null to 0; this is ambiguous as not only 0 == 0 is true, but null == 0 is true, too. Consider using a strict comparison ===.
Loading history...
306
            throw new \RuntimeException('Can\'t run git log. You must ensure to run compile from php-formatter git repository clone and that git binary is available.');
307
        }
308
        $date = new \DateTime(trim($process->getOutput()));
309
        $date->setTimezone(new \DateTimeZone('UTC'));
310
        $this->versionDate = $date->format('Y-m-d H:i:s');
0 ignored issues
show
Documentation Bug introduced by
It seems like $date->format('Y-m-d H:i:s') of type string is incompatible with the declared type object<DateTime> of property $versionDate.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
311
312
        $process = new Process('git describe --tags HEAD');
313
        if ($process->run() == 0) {
0 ignored issues
show
Bug introduced by
It seems like you are loosely comparing $process->run() of type integer|null to 0; this is ambiguous as not only 0 == 0 is true, but null == 0 is true, too. Consider using a strict comparison ===.
Loading history...
314
            $this->version = trim($process->getOutput());
315
        }
316
317
        return $this;
318
    }
319
}
320