Passed
Push — main ( 221f6d...f8c128 )
by Siad
05:28
created

src/Phing/Filter/LineContains.php (1 issue)

Severity
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\Filter;
22
23
use Exception;
24
use Phing\Io\IOException;
25
use Phing\Io\Reader;
26
use Phing\Project;
27
use Phing\Type\FilterReader;
28
29
/**
30
 * Filter which includes only those lines that contain all the user-specified
31
 * strings.
32
 *
33
 * Example:
34
 *
35
 * <pre><linecontains>
36
 *   <contains value="foo">
37
 *   <contains value="bar">
38
 * </linecontains></pre>
39
 *
40
 * Or:
41
 *
42
 * <pre><filterreader classname="phing.filters.LineContains">
43
 *    <param type="contains" value="foo"/>
44
 *    <param type="contains" value="bar"/>
45
 * </filterreader></pre>
46
 *
47
 * This will include only those lines that contain <code>foo</code> and
48
 * <code>bar</code>.
49
 *
50
 * @author  Yannick Lecaillez <[email protected]>
51
 * @author  Hans Lellelid <[email protected]>
52
 *
53
 * @see     FilterReader
54
 */
55
class LineContains extends BaseParamFilterReader implements ChainableReader
56
{
57
    /**
58
     * The parameter name for the string to match on.
59
     */
60
    private const CONTAINS_KEY = 'contains';
61
62
    /**
63
     * The parameter name for the string to match on.
64
     */
65
    private const NEGATE_KEY = 'negate';
66
67
    /**
68
     * Array of Contains objects.
69
     *
70
     * @var array
71
     */
72
    private $contains = [];
73
74
    /**
75
     * @var bool
76
     */
77
    private $negate = false;
78
79
    /**
80
     * Remaining line to be read from this filter, or <code>null</code> if
81
     * the next call to <code>read()</code> should read the original stream
82
     * to find the next matching line.
83
     */
84
    private $line;
85
86
    private $matchAny = false;
87
88
    /**
89
     * Remaining line to be read from this filter, or <code>null</code> if
90
     * the next call to <code>read()</code> should read the original stream
91
     * to find the next matching line.
92
     *
93
     * @param int|int $len
94
     *
95
     * @throws IOException if the underlying stream throws an IOException during reading
96
     *
97
     * @return int|string the next character in the resulting stream, or -1
98
     *                    if the end of the resulting stream has been reached
99
     */
100 3
    public function read($len = null)
101
    {
102 3
        if (!$this->getInitialized()) {
103 2
            $this->initialize();
104 2
            $this->setInitialized(true);
105
        }
106
107 3
        $ch = -1;
108
109 3
        if (null !== $this->line) {
110 3
            $ch = $this->line[0] ?? -1;
111 3
            if (1 === strlen($this->line)) {
112 3
                $this->line = null;
113
            } else {
114 3
                $part = substr($this->line, 1);
115
116 3
                $this->line = false !== $part ? $part : null;
117
            }
118
        } else {
119 3
            for ($this->line = $this->readLine(); null !== $this->line; $this->line = $this->readLine()) {
120 3
                $matches = true;
121 3
                foreach ($this->contains as $iValue) {
122 3
                    $containsStr = $iValue->getValue();
123 3
                    if ('' === $containsStr) {
124
                        $this->log(
125
                            'The value of <contents> is evaluated to an empty string.',
126
                            Project::MSG_DEBUG
127
                        );
128
                        $matches = false;
129
                    } else {
130 3
                        $matches = false !== strpos($this->line, $containsStr);
131
                    }
132 3
                    if (!$matches) {
133 3
                        if ($this->matchAny) {
134 1
                            continue;
135
                        }
136
137 2
                        break;
138
                    }
139
140 3
                    if ($this->matchAny) {
141 1
                        break;
142
                    }
143
                }
144 3
                if ($matches ^ $this->isNegated()) {
145 3
                    break;
146
                }
147
            }
148 3
            if (null !== $this->line) {
149 3
                return $this->read();
150
            }
151
        }
152
153 3
        return $ch;
154
    }
155
156
    /**
157
     * Set the negation mode.  Default false (no negation).
158
     *
159
     * @param bool $b the bool negation mode to set
160
     */
161 2
    public function setNegate(bool $b)
162
    {
163 2
        $this->negate = $b;
164 2
    }
165
166
    /**
167
     * Find out whether we have been negated.
168
     *
169
     * @return bool negation flag
170
     */
171 3
    public function isNegated(): bool
172
    {
173 3
        return $this->negate;
174
    }
175
176
    /**
177
     * Adds a <code>contains</code> nested element.
178
     *
179
     * @return Contains The <code>contains</code> element added.
180
     *                  Must not be <code>null</code>.
181
     */
182 1
    public function createContains(): Contains
183
    {
184 1
        $num = array_push($this->contains, new Contains());
185
186 1
        return $this->contains[$num - 1];
187
    }
188
189
    /**
190
     * Returns the vector of words which must be contained within a line read
191
     * from the original stream in order for it to match this filter.
192
     *
193
     * @return array The array of words which must be contained within a line read
194
     *               from the original stream in order for it to match this filter. The
195
     *               returned object is "live" - in other words, changes made to the
196
     *               returned object are mirrored in the filter.
197
     */
198 1
    public function getContains(): array
199
    {
200 1
        return $this->contains;
201
    }
202
203
    /**
204
     * Creates a new LineContains using the passed in
205
     * Reader for instantiation.
206
     *
207
     * @param Reader $reader A Reader object providing the underlying stream.
208
     *                       Must not be <code>null</code>.
209
     *
210
     * @throws Exception
211
     *
212
     * @return LineContains A new filter based on this configuration, but filtering
213
     *                      the specified reader
214
     */
215 1
    public function chain(Reader $reader): Reader
216
    {
217 1
        $newFilter = new self($reader);
218 1
        $newFilter->setContains($this->getContains());
219 1
        $newFilter->setNegate($this->isNegated());
220 1
        $newFilter->setMatchAny($this->isMatchAny());
221 1
        $newFilter->setInitialized(true);
222 1
        $newFilter->setProject($this->getProject());
223
224 1
        return $newFilter;
225
    }
226
227 1
    public function setMatchAny(bool $matchAny): void
228
    {
229 1
        $this->matchAny = $matchAny;
230 1
    }
231
232 1
    public function isMatchAny(): bool
233
    {
234 1
        return $this->matchAny;
235
    }
236
237
    /**
238
     * Sets the array of words which must be contained within a line read
239
     * from the original stream in order for it to match this filter.
240
     *
241
     * @param array $contains An array of words which must be contained
242
     *                        within a line in order for it to match in this filter.
243
     *                        Must not be <code>null</code>.
244
     *
245
     * @throws Exception
246
     */
247 1
    private function setContains(array $contains)
248
    {
249 1
        $this->contains = $contains;
250 1
    }
251
252
    /**
253
     * Parses the parameters to add user-defined contains strings.
254
     */
255 2
    private function initialize()
256
    {
257 2
        $params = $this->getParameters();
258 2
        if (null !== $params) {
0 ignored issues
show
The condition null !== $params is always true.
Loading history...
259 2
            foreach ($params as $param) {
260 2
                if (self::CONTAINS_KEY === $param->getType()) {
261 2
                    $cont = new Contains();
262 2
                    $cont->setValue($param->getValue());
263 2
                    $this->contains[] = $cont;
264 1
                } elseif (self::NEGATE_KEY === $param->getType()) {
265 1
                    $this->setNegate(Project::toBoolean($param->getValue()));
266
                }
267
            }
268
        }
269 2
    }
270
}
271