Passed
Push — main ( b7649a...398f47 )
by Michiel
06:32
created

PatchTask::main()   B

Complexity

Conditions 9
Paths 65

Size

Total Lines 43
Code Lines 28

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 16
CRAP Score 14.477

Importance

Changes 0
Metric Value
eloc 28
dl 0
loc 43
ccs 16
cts 27
cp 0.5926
rs 8.0555
c 0
b 0
f 0
cc 9
nc 65
nop 0
crap 14.477
1
<?php
2
/**
3
 *  Patches a file by applying a 'diff' file to it
4
 *
5
 *  Requires "patch" to be on the execution path.
6
 *
7
 *  Based on Apache Ant PatchTask:
8
 *
9
 *  Licensed to the Apache Software Foundation (ASF) under one or more
10
 *  contributor license agreements.  See the NOTICE file distributed with
11
 *  this work for additional information regarding copyright ownership.
12
 *  The ASF licenses this file to You under the Apache License, Version 2.0
13
 *  (the "License"); you may not use this file except in compliance with
14
 *  the License.  You may obtain a copy of the License at
15
 *
16
 *      http://www.apache.org/licenses/LICENSE-2.0
17
 *
18
 *  Unless required by applicable law or agreed to in writing, software
19
 *  distributed under the License is distributed on an "AS IS" BASIS,
20
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
21
 *  See the License for the specific language governing permissions and
22
 *  limitations under the License.
23
 */
24
25
namespace Phing\Task\Optional;
26
27
use Phing\Exception\BuildException;
28
use Phing\Io\IOException;
29
use Phing\Io\File;
30
use Phing\Project;
31
use Phing\Task;
32
use Phing\Task\System\ExecTask;
33
use Phing\Type\Commandline;
34
35
/**
36
 * Patches a file by applying a 'diff' file to it
37
 *
38
 * Requires "patch" to be on the execution path.
39
 *
40
 * @package phing.tasks.ext
41
 */
42
class PatchTask extends Task
43
{
44
    private static $PATCH = 'patch';
45
46
    /**
47
     * File to be patched
48
     *
49
     * @var File
50
     */
51
    private $originalFile;
52
53
    /**
54
     * @var File $directory
55
     */
56
    private $directory;
57
58
    /**
59
     * Halt on error return value from patch invocation.
60
     *
61
     * @var bool
62
     */
63
    private $failOnError = false;
64
65
    /**
66
     * @var Commandline $cmd
67
     */
68
    private $cmd;
69
70
    /**
71
     * @var bool $havePatchFile
72
     */
73
    private $havePatchFile = false;
74
75 1
    public function __construct()
76
    {
77 1
        $this->cmd = new Commandline();
78 1
        parent::__construct();
79 1
    }
80
81
    /**
82
     * The file containing the diff output
83
     *
84
     * Required.
85
     *
86
     * @param File $file File containing the diff output
87
     * @return void
88
     * @throws BuildException if $file not exists
89
     */
90 1
    public function setPatchFile(File $file)
91
    {
92 1
        if (!$file->exists()) {
93
            throw new BuildException('patchfile ' . $file . " doesn't exist", $this->getLocation());
94
        }
95 1
        $this->cmd->createArgument()->setValue('-i');
96 1
        $this->cmd->createArgument()->setFile($file);
97 1
        $this->havePatchFile = true;
98 1
    }
99
100
    /**
101
     * flag to create backups; optional, default=false
102
     *
103
     * @param bool $backups if true create backups
104
     */
105
    public function setBackups($backups)
106
    {
107
        if ($backups) {
108
            $this->cmd->createArgument()->setValue('-b');
109
        }
110
    }
111
112
    /**
113
     * flag to ignore whitespace differences; default=false
114
     *
115
     * @param bool $ignore if true ignore whitespace differences
116
     */
117 1
    public function setIgnorewhitespace($ignore)
118
    {
119 1
        if ($ignore) {
120 1
            $this->cmd->createArgument()->setValue('-l');
121
        }
122 1
    }
123
124
    /**
125
     * The file to patch
126
     *
127
     * Optional if it can be inferred from the diff file.
128
     *
129
     * @param File $file File to patch
130
     * @return void
131
     */
132 1
    public function setOriginalFile(File $file)
133
    {
134 1
        $this->originalFile = $file;
135 1
    }
136
137
    /**
138
     * The name of a file to send the output to, instead of patching
139
     * the file(s) in place
140
     *
141
     * Optional.
142
     *
143
     * @param File $file File to send the output to
144
     * @return void
145
     */
146 1
    public function setDestFile(File $file)
147
    {
148 1
        $this->cmd->createArgument()->setValue('-o');
149 1
        $this->cmd->createArgument()->setFile($file);
150 1
    }
151
152
    /**
153
     * Strip the smallest prefix containing <i>num</i> leading slashes
154
     * from filenames.
155
     *
156
     * patch's <i>--strip</i> option.
157
     *
158
     * @param int $num number of lines to strip
159
     * @return void
160
     * @throws BuildException if num is < 0, or other errors
161
     */
162
    public function setStrip($num)
163
    {
164
        if ($num < 0) {
165
            throw new BuildException('strip has to be >= 0');
166
        }
167
168
        $this->cmd->createArgument()->setValue("--strip $num");
169
    }
170
171
    /**
172
     * Work silently unless an error occurs
173
     *
174
     * Optional, default - false
175
     *
176
     * @param bool $flag If true suppress set the -s option on the patch command
177
     * @return void
178
     */
179
    public function setQuiet($flag)
180
    {
181
        if ($flag) {
182
            $this->cmd->createArgument()->setValue('-s');
183
        }
184
    }
185
186
    /**
187
     * Assume patch was created with old and new files swapped
188
     *
189
     * Optional, default - false
190
     *
191
     * @param bool $flag If true set the -R option on the patch command
192
     * @return void
193
     */
194 1
    public function setReverse($flag)
195
    {
196 1
        if ($flag) {
197 1
            $this->cmd->createArgument('-R');
0 ignored issues
show
Bug introduced by
'-R' of type string is incompatible with the type boolean expected by parameter $insertAtStart of Phing\Type\Commandline::createArgument(). ( Ignorable by Annotation )

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

197
            $this->cmd->createArgument(/** @scrutinizer ignore-type */ '-R');
Loading history...
198
        }
199 1
    }
200
201
    /**
202
     * The directory to run the patch command in
203
     *
204
     * Defaults to the project's base directory.
205
     *
206
     * @param File $directory Directory to run the patch command in
207
     * @return void
208
     */
209
    public function setDir(File $directory)
210
    {
211
        $this->directory = $directory;
212
    }
213
214
    /**
215
     * Ignore patches that seem to be reversed or already applied
216
     *
217
     * @param bool $flag If true set the -N (--forward) option
218
     * @return void
219
     */
220
    public function setForward($flag)
221
    {
222
        if ($flag) {
223
            $this->cmd->createArgument()->setValue('-N');
224
        }
225
    }
226
227
    /**
228
     * Set the maximum fuzz factor
229
     *
230
     * Defaults to 0
231
     *
232
     * @param string $value Value of a fuzz factor
233
     * @return void
234
     */
235
    public function setFuzz($value)
236
    {
237
        $this->cmd->createArgument()->setValue("-F $value");
238
    }
239
240
    /**
241
     * If true, stop the build process if the patch command
242
     * exits with an error status.
243
     *
244
     * The default is "false"
245
     *
246
     * @param bool $value "true" if it should halt, otherwise "false"
247
     * @return void
248
     */
249 1
    public function setFailOnError($value)
250
    {
251 1
        $this->failOnError = $value;
252 1
    }
253
254
    /**
255
     * @param string $value
256
     */
257
    public function setHaltOnFailure(string $value)
258
    {
259
        $this->failOnError = $value;
0 ignored issues
show
Documentation Bug introduced by
The property $failOnError was declared of type boolean, but $value is of type string. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
260
    }
261
262
    /**
263
     * Main task method
264
     *
265
     * @return void
266
     * @throws BuildException when it all goes a bit pear shaped
267
     */
268 1
    public function main()
269
    {
270 1
        if (!$this->havePatchFile) {
271
            throw new BuildException('patchfile argument is required', $this->getLocation());
272
        }
273
274 1
        $toExecute = $this->cmd;
275 1
        $toExecute->setExecutable(self::$PATCH);
276
277 1
        if ($this->originalFile !== null) {
278 1
            $toExecute->createArgument()->setFile($this->originalFile);
279
        }
280
281 1
        $exe = new ExecTask();
282 1
        $exe->setCommand(implode(' ', $toExecute->getCommandline()));
283 1
        $exe->setLevel('info');
284 1
        $exe->setExecutable($toExecute->getExecutable());
285
286
        try {
287 1
            if ($this->directory === null) {
288 1
                $exe->setDir($this->getProject()->getBasedir());
289
            } else {
290
                if (!$this->directory->isDirectory()) {
291
                    throw new BuildException($this->directory . ' is not a directory.', $this->getLocation());
292
                }
293
                $exe->setDir($this->directory);
294
            }
295
296 1
            $this->log($toExecute->describeCommand(), Project::MSG_VERBOSE);
297
298 1
            $returnCode = $exe->main();
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $returnCode is correct as $exe->main() targeting Phing\Task\System\ExecTask::main() seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

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

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

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

Loading history...
299 1
            if ($exe->isFailure($returnCode)) {
300
                $msg = "'" . self::$PATCH . "' failed with exit code " . $returnCode;
301
                if ($this->failOnError) {
302
                    throw new BuildException($msg);
303
                }
304 1
                $this->log($msg, Project::MSG_ERR);
305
            }
306
        } catch (IOException $e) {
307
            if ($this->failOnError) {
308
                throw new BuildException($e, $this->getLocation());
309
            }
310
            $this->log($e->getMessage(), Project::MSG_ERR);
311
        }
312 1
    }
313
}
314