Comments::removeTrailingBlankLines()   A
last analyzed

Complexity

Conditions 3
Paths 2

Size

Total Lines 7
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 3
nc 2
nop 1
dl 0
loc 7
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace Consolidation\Comments;
4
5
/**
6
 * Remember comments in one text file (usually a yaml file), and
7
 * re-inject them into an edited copy of the same file.
8
 *
9
 * This is a workaround for the fact that the Symfony Yaml parser
10
 * does not record comments.
11
 *
12
 * Comments at the beginning and end of the file are guarenteed to
13
 * be retained at the beginning and the end of the resulting file.
14
 *
15
 * Comments that appear before sections of yaml that is deleted will
16
 * be deliberately discarded as well.
17
 *
18
 * If the resulting yaml file contents are reordered, comments may
19
 * become mis-ordered (attached to the wrong element).
20
 *
21
 * Comments that appear before sections of yaml that are edited may
22
 * be inadvertantly lost. It is recommended to always place comments
23
 * immediately before identifier lines (i.e. "foo:").
24
 */
25
class Comments
26
{
27
    protected $hasStored;
28
    protected $headComments;
29
    protected $accumulated;
30
    protected $stored;
31
    protected $endComments;
32
33
    public function __construct()
34
    {
35
        $this->hasStored = false;
36
        $this->headComments = false;
37
        $this->accumulated = [];
38
        $this->stored = [];
39
        $this->endComments = [];
40
    }
41
42
    /**
43
     * Collect all of the comments from a text file
44
     * (usually a yaml file).
45
     *
46
     * @param array $contentLines
47
     */
48
    public function collect(array $contentLines)
49
    {
50
        $contentLines = $this->removeTrailingBlankLines($contentLines);
51
52
        // Put look through the rest of the lines and store the comments as needed
53
        foreach ($contentLines as $line) {
54
            if ($this->isBlank($line)) {
55
                $this->accumulateEmptyLine();
56
            } elseif ($this->isComment($line)) {
57
                $this->accumulate($line);
58
            } else {
59
                $this->storeAccumulated($line);
60
            }
61
        }
62
        $this->endCollect();
63
    }
64
65
    /**
66
     * Description
67
     * @param array $contentLines
68
     * @return array of lines with comments re-intersperced
69
     */
70
    public function inject(array $contentLines)
71
    {
72
        $contentLines = $this->removeTrailingBlankLines($contentLines);
73
74
        // If there were any comments at the beginning of the
75
        // file, then put them back at the beginning.
76
        $result = $this->headComments === false ? [] : $this->headComments;
77
        foreach ($contentLines as $line) {
78
            $fetched = $this->find($line);
79
            $result = array_merge($result, $fetched);
0 ignored issues
show
Bug introduced by
It seems like $result can also be of type true; however, parameter $array1 of array_merge() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

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

79
            $result = array_merge(/** @scrutinizer ignore-type */ $result, $fetched);
Loading history...
80
81
            $result[] = $line;
82
        }
83
        // Any comments found at the end of the file will stay at
84
        // the end of the file.
85
        $result = array_merge($result, $this->endComments);
86
        return $result;
87
    }
88
89
    /**
90
     * @param string $line
91
     * @return true if provided line is a comment
92
     */
93
    protected function isComment($line)
94
    {
95
        return preg_match('%^ *#%', $line);
0 ignored issues
show
Bug Best Practice introduced by
The expression return preg_match('%^ *#%', $line) returns the type integer which is incompatible with the documented return type true.
Loading history...
96
    }
97
98
    /**
99
     * Stop collecting. Any accumulated comments will be
100
     * remembered so that they may be re-injected at the
101
     * end of the new file.
102
     */
103
    protected function endCollect()
104
    {
105
        $this->endComments = $this->accumulated;
106
        $this->accumulated = [];
107
    }
108
109
    protected function accumulateEmptyLine()
110
    {
111
        if ($this->hasStored) {
112
            return $this->accumulate('');
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->accumulate('') targeting Consolidation\Comments\Comments::accumulate() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
113
        }
114
115
        if ($this->headComments === false) {
116
            $this->headComments = [];
117
        }
118
119
        $this->headComments = array_merge($this->headComments, $this->accumulated);
0 ignored issues
show
Bug introduced by
It seems like $this->headComments can also be of type true; however, parameter $array1 of array_merge() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

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

119
        $this->headComments = array_merge(/** @scrutinizer ignore-type */ $this->headComments, $this->accumulated);
Loading history...
120
        $this->accumulated = [''];
121
    }
122
123
    /**
124
     * Accumulate comments and blank lines in our cache.
125
     * @param string $line
126
     */
127
    protected function accumulate($line)
128
    {
129
        $this->accumulated[] = $line;
130
    }
131
132
    /**
133
     * When a non-comment line is found, remember all of
134
     * the comment lines that came before it in our cache.
135
     *
136
     * @param string $line
137
     */
138
    protected function storeAccumulated($line)
139
    {
140
        // Remember that we called storeAccumulated at least once
141
        $this->hasStored = true;
142
143
        // The very first time storeAccumulated is called, the
144
        // accumulated comments will be placed in $this->headComments
145
        // instead of stored, so they may be restored to the
146
        // beginning of the new file.
147
        if ($this->headComments === false) {
148
            $this->headComments = $this->accumulated;
149
            $this->accumulated = [];
150
            return;
151
        }
152
        if (!empty($this->accumulated)) {
153
            // $this->stored saves a stack of accumulated results.
154
            $this->stored[$line][] = $this->accumulated;
155
            $this->accumulated = [];
156
        }
157
    }
158
159
    /**
160
     * Check to see if the provided line has any associated comments.
161
     *
162
     * @param string $line
163
     */
164
    protected function find($line)
165
    {
166
        if (!isset($this->stored[$line]) || empty($this->stored[$line])) {
167
            return [];
168
        }
169
        // The stored result is a stack of accumulated comments. Pop
170
        // one off; if more remain, they will be attached to the next
171
        // line with the same value.
172
        return array_shift($this->stored[$line]);
173
    }
174
175
    /**
176
     * Remove all of the blank lines from the end of an array of lines.
177
     */
178
    protected function removeTrailingBlankLines($lines)
179
    {
180
        // Remove all of the trailing blank lines.
181
        while (!empty($lines) && $this->isBlank(end($lines))) {
182
            array_pop($lines);
183
        }
184
        return $lines;
185
    }
186
187
    /**
188
     * Return 'true' if the provided line is empty (save for whitespace)
189
     */
190
    protected function isBlank($line)
191
    {
192
        return preg_match('#^\s*$#', $line);
193
    }
194
}
195