Passed
Push — main ( 56ab68...1377a9 )
by Siad
05:20
created

TouchTask::processFileSets()   A

Complexity

Conditions 6
Paths 10

Size

Total Lines 18
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 6.1666

Importance

Changes 0
Metric Value
eloc 11
dl 0
loc 18
ccs 10
cts 12
cp 0.8333
rs 9.2222
c 0
b 0
f 0
cc 6
nc 10
nop 0
crap 6.1666
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\Task\System;
22
23
use Phing\Exception\BuildException;
24
use Phing\Io\File;
25
use Phing\Io\FileUtils;
26
use Phing\Io\IOException;
27
use Phing\Project;
28
use Phing\Task;
29
use Phing\Type\Element\FileListAware;
30
use Phing\Type\Element\FileSetAware;
31
use Phing\Type\Mapper;
32
33
/**
34
 * Touch a file and/or fileset(s); corresponds to the Unix touch command.
35
 *
36
 * If the file to touch doesn't exist, an empty one is created.
37
 */
38
class TouchTask extends Task
39
{
40
    use FileListAware;
41
    use FileSetAware;
42
43
    /**
44
     * @var File
45
     */
46
    private $file;
47
    private $seconds = -1;
48
    private $dateTime;
49
    private $mkdirs = false;
50
    private $verbose = true;
51
52
    /** @var Mapper */
53
    private $mapperElement;
54
55
    /**
56
     * Sets a single source file to touch.  If the file does not exist
57
     * an empty file will be created.
58
     * @param File $file
59
     */
60 76
    public function setFile(File $file): void
61
    {
62 76
        $this->file = $file;
63 76
    }
64
65
    /**
66
     * The new modification time of the file in milliseconds since midnight
67
     * Jan 1 1970. Negative values are not accepted nor are values less than
68
     * 1000. Note that PHP is actually based on seconds so the value passed
69
     * in will be divided by 1000.
70
     *
71
     * Optional, default=now
72
     *
73
     * @param int $millis
74
     */
75 6
    public function setMillis(int $millis): void
76
    {
77 6
        if ($millis >= 0) {
78 4
            if ($millis >= 1000) {
79 3
                $this->seconds = (int) $millis / 1000;
80
            } else {
81 4
                throw new BuildException('Millis less than 1000 would be treated as 0');
82
            }
83
        } else {
84 2
            throw new BuildException('Millis attribute cannot be negative');
85
        }
86 3
    }
87
88
    /**
89
     * the new modification time of the file
90
     * in seconds since midnight Jan 1 1970.
91
     * Optional, default=now.
92
     *
93
     * @param int $seconds
94
     */
95 9
    public function setSeconds($seconds): void
96
    {
97 9
        if ($seconds >= 0) {
98 8
            $this->seconds = (int) $seconds;
99
        } else {
100 1
            throw new BuildException('Seconds attribute cannot be negative');
101
        }
102 8
    }
103
104
    /**
105
     * the new modification time of the file
106
     * in the format MM/DD/YYYY HH:MM AM or PM;
107
     * Optional, default=now.
108
     *
109
     * @param string $dateTime
110
     */
111 9
    public function setDatetime($dateTime): void
112
    {
113 9
        $timestmap = strtotime($dateTime);
114 9
        if (false !== $timestmap) {
115 8
            $this->dateTime = (string) $dateTime;
116 8
            $this->setSeconds($timestmap);
117
        } else {
118 1
            throw new BuildException(
119 1
                "Date of {$dateTime} cannot be parsed correctly. "
120 1
                . "It should be in a format parsable by PHP's strtotime() function."
121 1
                . PHP_EOL
122
            );
123
        }
124 7
    }
125
126
    /**
127
     * Set whether nonexistent parent directories should be created
128
     * when touching new files.
129
     *
130
     * @param bool $mkdirs whether to create parent directories
131
     */
132 11
    public function setMkdirs($mkdirs): void
133
    {
134 11
        $this->mkdirs = $mkdirs;
135 11
    }
136
137
    /**
138
     * Set whether the touch task will report every file it creates;
139
     * defaults to <code>true</code>.
140
     *
141
     * @param bool $verbose flag
142
     */
143
    public function setVerbose($verbose): void
144
    {
145
        $this->verbose = $verbose;
146
    }
147
148
    /**
149
     * Execute the touch operation.
150
     *
151
     * @throws BuildException
152
     * @throws IOException
153
     */
154 2
    public function createMapper(): Mapper
155
    {
156 2
        if (null !== $this->mapperElement) {
157
            throw new BuildException('Cannot define more than one mapper', $this->getLocation());
158
        }
159 2
        $this->mapperElement = new Mapper($this->project);
160
161 2
        return $this->mapperElement;
162
    }
163
164
    /**
165
     * Execute the touch operation.
166
     *
167
     * @throws BuildException
168
     * @throws IOException
169
     */
170 80
    public function main()
171
    {
172 80
        $this->checkConfiguration();
173 78
        $this->touch();
174 76
    }
175
176
    /**
177
     * @throws IOException
178
     */
179 80
    protected function checkConfiguration(): void
180
    {
181 80
        $savedSeconds = $this->seconds;
182
183 80
        if (null === $this->file && 0 === count($this->filesets) && 0 === count($this->filelists)) {
184 1
            throw new BuildException('Specify at least one source - a file, a fileset or a filelist.');
185
        }
186
187 79
        if (null !== $this->file && $this->file->exists() && $this->file->isDirectory()) {
188 1
            throw new BuildException('Use a fileset to touch directories.');
189
        }
190
191 78
        $this->log(
192 78
            'Setting seconds to ' . $savedSeconds . ' from datetime attribute',
193 78
            ($this->seconds < 0 ? Project::MSG_DEBUG : Project::MSG_VERBOSE)
194
        );
195
196 78
        $this->seconds = $savedSeconds;
197 78
    }
198
199
    /**
200
     * Does the actual work.
201
     * @throws IOException
202
     * @throws \Exception
203
     */
204 78
    private function touch(): void
205
    {
206 78
        if (null !== $this->file) {
207 70
            if (!$this->file->exists()) {
208 59
                $this->log(
209 59
                    'Creating ' . $this->file,
210 59
                    $this->verbose ? Project::MSG_INFO : Project::MSG_VERBOSE
211
                );
212
213
                try { // try to create file
214 59
                    $this->file->createNewFile($this->mkdirs);
215 1
                } catch (IOException  $ioe) {
216 1
                    throw new BuildException(
217 1
                        'Error creating new file ' . $this->file,
218
                        $ioe,
219 1
                        $this->getLocation()
220
                    );
221
                }
222
            }
223
        }
224
225 77
        $resetSeconds = false;
226 77
        if ($this->seconds < 0) {
227 68
            $resetSeconds = true;
228
            // Note: this function actually returns seconds, not milliseconds (e.g. 1606505920.2657)
229 68
            $this->seconds = microtime(true);
230
        }
231
232 77
        if (null !== $this->file) {
233 69
            $this->touchFile($this->file);
234
        }
235
236 76
        $this->processFileSets();
237 76
        $this->processFileLists();
238
239 76
        if ($resetSeconds) {
240 67
            $this->seconds = -1;
241
        }
242 76
    }
243
244
    /**
245
     * @param $file
246
     * @return array
247
     */
248 10
    private function getMappedFileNames($file): array
249
    {
250 10
        if (null !== $this->mapperElement) {
251 2
            $mapper = $this->mapperElement->getImplementation();
252 2
            $results = $mapper->main($file);
253 2
            if (null === $results) {
0 ignored issues
show
introduced by
The condition null === $results is always false.
Loading history...
254
                return [];
255
            }
256 2
            $fileNames = $results;
257
        } else {
258 8
            $fileNames = [$file];
259
        }
260
261 10
        return $fileNames;
262
    }
263
264
    /**
265
     * @param File $file
266
     * @throws \Exception
267
     */
268 77
    private function touchFile(File $file): void
269
    {
270 77
        if (!$file->canWrite()) {
271 1
            throw new BuildException('Can not change modification date of read-only file ' . (string) $file);
272
        }
273 76
        $file->setLastModified($this->seconds);
274 76
    }
275
276
    /**
277
     * @throws IOException
278
     * @throws \Exception
279
     */
280 76
    private function processFileSets(): void
281
    {
282 76
        foreach ($this->filesets as $fs) {
283 2
            $ds = $fs->getDirectoryScanner($this->getProject());
284 2
            $fromDir = $fs->getDir($this->getProject());
285
286 2
            $srcFiles = $ds->getIncludedFiles();
287 2
            $srcDirs = $ds->getIncludedDirectories();
288
289 2
            for ($j = 0, $_j = count($srcFiles); $j < $_j; ++$j) {
290 2
                foreach ($this->getMappedFileNames((string) $srcFiles[$j]) as $fileName) {
291 2
                    $this->touchFile(new File($fromDir, $fileName));
292
                }
293
            }
294
295 2
            for ($j = 0, $_j = count($srcDirs); $j < $_j; ++$j) {
296
                foreach ($this->getMappedFileNames((string) $srcDirs[$j]) as $fileName) {
297
                    $this->touchFile(new File($fromDir, $fileName));
298
                }
299
            }
300
        }
301 76
    }
302
303
    /**
304
     * @throws IOException
305
     * @throws \Exception
306
     */
307 76
    private function processFileLists(): void
308
    {
309 76
        foreach ($this->filelists as $fl) {
310 8
            $fromDir = $fl->getDir($this->getProject());
311 8
            $srcFiles = $fl->getFiles($this->getProject());
312 8
            foreach ($srcFiles as $jValue) {
313 8
                foreach ($this->getMappedFileNames((string) $jValue) as $fileName) {
314 8
                    $this->touchFile(new File($fromDir, $fileName));
315
                }
316
            }
317
        }
318 76
    }
319
}
320