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

26
/**
27
 * Patches a file by applying a 'diff' file to it
28
 *
29
 * Requires "patch" to be on the execution path.
30
 *
31
 * @package phing.tasks.ext
32
 */
33
class PatchTask extends Task
34
{
35
    private static $PATCH = 'patch';
36

37
    /**
38
     * File to be patched
39
     *
40
     * @var PhingFile
41
     */
42
    private $originalFile;
43

44
    /**
45
     * @var PhingFile $directory
46
     */
47
    private $directory;
48

49
    /**
50
     * Halt on error return value from patch invocation.
51
     *
52
     * @var bool
53
     */
54
    private $failOnError = false;
55

56
    /**
57
     * @var Commandline $cmd
58
     */
59
    private $cmd;
60

61
    /**
62
     * @var bool $havePatchFile
63
     */
64
    private $havePatchFile = false;
65

66 1
    public function __construct()
67
    {
68 1
        $this->cmd = new Commandline();
69 1
        parent::__construct();
70
    }
71

72
    /**
73
     * The file containing the diff output
74
     *
75
     * Required.
76
     *
77
     * @param  PhingFile $file File containing the diff output
78
     * @return void
79
     * @throws BuildException if $file not exists
80
     */
81 1
    public function setPatchFile(PhingFile $file)
82
    {
83 1
        if (!$file->exists()) {
84 0
            throw new BuildException('patchfile ' . $file . " doesn't exist", $this->getLocation());
85
        }
86 1
        $this->cmd->createArgument()->setValue('-i');
87 1
        $this->cmd->createArgument()->setFile($file);
88 1
        $this->havePatchFile = true;
89
    }
90

91
    /**
92
     * flag to create backups; optional, default=false
93
     *
94
     * @param bool $backups if true create backups
95
     */
96 0
    public function setBackups($backups)
97
    {
98 0
        if ($backups) {
99 0
            $this->cmd->createArgument()->setValue('-b');
100
        }
101
    }
102

103
    /**
104
     * flag to ignore whitespace differences; default=false
105
     *
106
     * @param bool $ignore if true ignore whitespace differences
107
     */
108 1
    public function setIgnorewhitespace($ignore)
109
    {
110 1
        if ($ignore) {
111 1
            $this->cmd->createArgument()->setValue('-l');
112
        }
113
    }
114

115
    /**
116
     * The file to patch
117
     *
118
     * Optional if it can be inferred from the diff file.
119
     *
120
     * @param  PhingFile $file File to patch
121
     * @return void
122
     */
123 1
    public function setOriginalFile(PhingFile $file)
124
    {
125 1
        $this->originalFile = $file;
126
    }
127

128
    /**
129
     * The name of a file to send the output to, instead of patching
130
     * the file(s) in place
131
     *
132
     * Optional.
133
     *
134
     * @param  PhingFile $file File to send the output to
135
     * @return void
136
     */
137 1
    public function setDestFile(PhingFile $file)
138
    {
139 1
        $this->cmd->createArgument()->setValue('-o');
140 1
        $this->cmd->createArgument()->setFile($file);
141
    }
142

143
    /**
144
     * Strip the smallest prefix containing <i>num</i> leading slashes
145
     * from filenames.
146
     *
147
     * patch's <i>--strip</i> option.
148
     *
149
     * @param  int $num number of lines to strip
150
     * @return void
151
     * @throws BuildException if num is < 0, or other errors
152
     */
153 0
    public function setStrip($num)
154
    {
155 0
        if ($num < 0) {
156 0
            throw new BuildException('strip has to be >= 0');
157
        }
158

159 0
        $this->cmd->createArgument()->setValue("--strip $num");
160
    }
161

162
    /**
163
     * Work silently unless an error occurs
164
     *
165
     * Optional, default - false
166
     *
167
     * @param  bool $flag If true suppress set the -s option on the patch command
168
     * @return void
169
     */
170 0
    public function setQuiet($flag)
171
    {
172 0
        if ($flag) {
173 0
            $this->cmd->createArgument()->setValue('-s');
174
        }
175
    }
176

177
    /**
178
     * Assume patch was created with old and new files swapped
179
     *
180
     * Optional, default - false
181
     *
182
     * @param  bool $flag If true set the -R option on the patch command
183
     * @return void
184
     */
185 1
    public function setReverse($flag)
186
    {
187 1
        if ($flag) {
188 1
            $this->cmd->createArgument('-R');
189
        }
190
    }
191

192
    /**
193
     * The directory to run the patch command in
194
     *
195
     * Defaults to the project's base directory.
196
     *
197
     * @param  PhingFile $directory Directory to run the patch command in
198
     * @return void
199
     */
200 0
    public function setDir(PhingFile $directory)
201
    {
202 0
        $this->directory = $directory;
203
    }
204

205
    /**
206
     * Ignore patches that seem to be reversed or already applied
207
     *
208
     * @param  bool $flag If true set the -N (--forward) option
209
     * @return void
210
     */
211 0
    public function setForward($flag)
212
    {
213 0
        if ($flag) {
214 0
            $this->cmd->createArgument()->setValue('-N');
215
        }
216
    }
217

218
    /**
219
     * Set the maximum fuzz factor
220
     *
221
     * Defaults to 0
222
     *
223
     * @param  string $value Value of a fuzz factor
224
     * @return void
225
     */
226 0
    public function setFuzz($value)
227
    {
228 0
        $this->cmd->createArgument()->setValue("-F $value");
229
    }
230

231
    /**
232
     * If true, stop the build process if the patch command
233
     * exits with an error status.
234
     *
235
     * The default is "false"
236
     *
237
     * @param  bool $value "true" if it should halt, otherwise "false"
238
     * @return void
239
     */
240 1
    public function setFailOnError($value)
241
    {
242 1
        $this->failOnError = $value;
243
    }
244

245
    /**
246
     * @param string $value
247
     */
248 0
    public function setHaltOnFailure(string $value)
249
    {
250 0
        $this->failOnError = $value;
251
    }
252

253
    /**
254
     * Main task method
255
     *
256
     * @return void
257
     * @throws BuildException when it all goes a bit pear shaped
258
     */
259 1
    public function main()
260
    {
261 1
        if (!$this->havePatchFile) {
262 0
            throw new BuildException('patchfile argument is required', $this->getLocation());
263
        }
264

265 1
        $toExecute = $this->cmd;
266 1
        $toExecute->setExecutable(self::$PATCH);
267

268 1
        if ($this->originalFile !== null) {
269 1
            $toExecute->createArgument()->setFile($this->originalFile);
270
        }
271

272 1
        $exe = new ExecTask();
273 1
        $exe->setCommand(implode(' ', $toExecute->getCommandline()));
274 1
        $exe->setLevel('info');
275 1
        $exe->setExecutable($toExecute->getExecutable());
276

277
        try {
278 1
            if ($this->directory === null) {
279 1
                $exe->setDir($this->getProject()->getBasedir());
280
            } else {
281 0
                if (!$this->directory->isDirectory()) {
282 0
                    throw new BuildException($this->directory . ' is not a directory.', $this->getLocation());
283
                }
284 0
                $exe->setDir($this->directory);
285
            }
286

287 1
            $this->log($toExecute->describeCommand(), Project::MSG_VERBOSE);
288

289 1
            $returnCode = $exe->main();
290 1
            if ($exe->isFailure($returnCode)) {
291 0
                $msg = "'" . self::$PATCH . "' failed with exit code " . $returnCode;
292 0
                if ($this->failOnError) {
293 0
                    throw new BuildException($msg);
294
                }
295 0
                $this->log($msg, Project::MSG_ERR);
296
            }
297 0
        } catch (IOException $e) {
298 0
            if ($this->failOnError) {
299 0
                throw new BuildException($e, $this->getLocation());
300
            }
301 0
            $this->log($e->getMessage(), Project::MSG_ERR);
302
        }
303
    }
304
}

Read our documentation on viewing source code .

Loading