Passed
Pull Request — master (#315)
by Théo
02:33
created

Php::compactContent()   B

Complexity

Conditions 8
Paths 8

Size

Total Lines 40
Code Lines 22

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 22
dl 0
loc 40
rs 8.4444
c 0
b 0
f 0
cc 8
nc 8
nop 1
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of the box project.
7
 *
8
 * (c) Kevin Herrera <[email protected]>
9
 *     Théo Fidry <[email protected]>
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 KevinGH\Box\Compactor;
16
17
use KevinGH\Box\Annotation\DocblockAnnotationParser;
18
use KevinGH\Box\Annotation\InvalidToken;
19
use RuntimeException;
20
use const T_COMMENT;
21
use const T_DOC_COMMENT;
22
use const T_WHITESPACE;
23
use function count;
24
use function in_array;
25
use function is_string;
26
use function preg_replace;
27
use function str_repeat;
28
use function strpos;
29
use function substr_count;
30
use function token_get_all;
31
32
/**
33
 * A PHP source code compactor copied from Composer.
34
 *
35
 * @see https://github.com/composer/composer/blob/a8df30c09be550bffc37ba540fb7c7f0383c3944/src/Composer/Compiler.php#L214
36
 *
37
 * @author Kevin Herrera <[email protected]>
38
 * @author Fabien Potencier <[email protected]>
39
 * @author Jordi Boggiano <[email protected]>
40
 * @author Théo Fidry <[email protected]>
41
 * @private
42
 */
43
final class Php extends FileExtensionCompactor
44
{
45
    private $annotationParser;
46
47
    /**
48
     * {@inheritdoc}
49
     */
50
    public function __construct(DocblockAnnotationParser $annotationParser, array $extensions = ['php'])
51
    {
52
        parent::__construct($extensions);
53
54
        $this->annotationParser = $annotationParser;
55
    }
56
57
    /**
58
     * {@inheritdoc}
59
     */
60
    protected function compactContent(string $contents): string
61
    {
62
        $output = '';
63
64
        foreach (token_get_all($contents) as $token) {
65
            if (is_string($token)) {
66
                $output .= $token;
67
            } elseif (in_array($token[0], [T_COMMENT, T_DOC_COMMENT], true)) {
68
                if (false !== strpos($token[1], '@')) {
69
                    try {
70
                        $output .= $this->compactAnnotations($token[1]);
71
                    } catch (InvalidToken $exception) {
72
                        // This exception is due to the dumper to be out of sync with the current grammar and/or the
73
                        // grammar being incomplete. In both cases throwing here is better in order to identify and
74
                        // this those cases instead of silently failing.
75
76
                        throw $exception;
77
                    } catch (RuntimeException $exception) {
78
                        $output .= $token[1];
79
                    }
80
                } else {
81
                    $output .= str_repeat("\n", substr_count($token[1], "\n"));
82
                }
83
            } elseif (T_WHITESPACE === $token[0]) {
84
                // reduce wide spaces
85
                $whitespace = preg_replace('{[ \t]+}', ' ', $token[1]);
86
87
                // normalize newlines to \n
88
                $whitespace = preg_replace('{(?:\r\n|\r|\n)}', "\n", $whitespace);
89
90
                // trim leading spaces
91
                $whitespace = preg_replace('{\n +}', "\n", $whitespace);
92
93
                $output .= $whitespace;
94
            } else {
95
                $output .= $token[1];
96
            }
97
        }
98
99
        return $output;
100
    }
101
102
    private function compactAnnotations(string $docblock): string
103
    {
104
        $breaksNbr = substr_count($docblock, "\n");
105
106
        $annotations = $this->annotationParser->parse($docblock);
107
108
        if ([] === $annotations) {
109
            return str_repeat("\n", $breaksNbr);
110
        }
111
112
        $compactedDocblock = '/**';
113
114
        foreach ($annotations as $annotation) {
115
            $compactedDocblock .= "\n".$annotation;
116
        }
117
118
        $breaksNbr -= count($annotations);
119
120
        if ($breaksNbr > 0) {
121
            $compactedDocblock .= str_repeat("\n", $breaksNbr - 1);
122
            $compactedDocblock .= "\n*/";
123
        } else {
124
            // A space is required here to avoid having /***/
125
            $compactedDocblock .= ' */';
126
        }
127
128
        return $compactedDocblock;
129
    }
130
}
131