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

XmlLintTask::main()   B

Complexity

Conditions 8
Paths 4

Size

Total Lines 24
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 72

Importance

Changes 0
Metric Value
eloc 16
dl 0
loc 24
ccs 0
cts 16
cp 0
rs 8.4444
c 0
b 0
f 0
cc 8
nc 4
nop 0
crap 72
1
<?php
2
/**
3
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
4
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
5
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
6
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
7
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
8
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
9
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
10
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
11
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
12
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
13
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
14
 *
15
 * This software consists of voluntary contributions made by many individuals
16
 * and is licensed under the LGPL. For more information please see
17
 * <http://phing.info>.
18
 */
19
20
namespace Phing\Task\Optional;
21
22
use DOMDocument;
23
use Phing\Exception\BuildException;
24
use Phing\Io\File;
25
use Phing\Project;
26
use Phing\Task;
27
use Phing\Type\Element\FileSetAware;
28
29
/**
30
 * A XML lint task. Checking syntax of one or more XML files against an XML Schema using the DOM extension.
31
 *
32
 * @author  Knut Urdalen <[email protected]>
33
 * @package phing.tasks.ext
34
 */
35
class XmlLintTask extends Task
36
{
37
    use FileSetAware;
38
39
    protected $file; // the source file (from xml attribute)
40
    protected $schema; // the schema file (from xml attribute)
41
    protected $useRNG = false;
42
43
    protected $haltonfailure = true;
44
45
    /**
46
     * File to be performed syntax check on
47
     *
48
     * @param File $file
49
     */
50
    public function setFile(File $file)
51
    {
52
        $this->file = $file;
53
    }
54
55
    /**
56
     * XML Schema Description file to validate against
57
     *
58
     * @param File $schema
59
     */
60
    public function setSchema(File $schema)
61
    {
62
        $this->schema = $schema;
63
    }
64
65
    /**
66
     * Use RNG instead of DTD schema validation
67
     *
68
     * @param bool $bool
69
     */
70
    public function setUseRNG($bool)
71
    {
72
        $this->useRNG = (bool) $bool;
73
    }
74
75
    /**
76
     * Sets the haltonfailure attribute
77
     *
78
     * @param bool $haltonfailure
79
     *
80
     * @return void
81
     */
82
    public function setHaltonfailure(bool $haltonfailure)
83
    {
84
        $this->haltonfailure = $haltonfailure;
85
    }
86
87
    /**
88
     * Execute lint check against PhingFile or a FileSet.
89
     *
90
     * {@inheritdoc}
91
     *
92
     * @return void
93
     * @throws BuildException
94
     *
95
     */
96
    public function main()
97
    {
98
        if (isset($this->schema) && !file_exists($this->schema->getPath())) {
99
            throw new BuildException("Schema file not found: " . $this->schema->getPath());
100
        }
101
        if (!isset($this->file) and count($this->filesets) == 0) {
102
            throw new BuildException("Missing either a nested fileset or attribute 'file' set");
103
        }
104
105
        set_error_handler([$this, 'errorHandler']);
106
        if ($this->file instanceof File) {
107
            $this->lint($this->file->getPath());
108
        } else { // process filesets
109
            $project = $this->getProject();
110
            foreach ($this->filesets as $fs) {
111
                $ds = $fs->getDirectoryScanner($project);
112
                $files = $ds->getIncludedFiles();
113
                $dir = $fs->getDir($this->project)->getPath();
114
                foreach ($files as $file) {
115
                    $this->lint($dir . DIRECTORY_SEPARATOR . $file);
116
                }
117
            }
118
        }
119
        restore_error_handler();
120
    }
121
122
    /**
123
     * @param $message
124
     *
125
     * @return void
126
     *
127
     * @throws BuildException
128
     */
129
    protected function logError($message)
130
    {
131
        if ($this->haltonfailure) {
132
            throw new BuildException($message);
133
        }
134
135
        $this->log($message, Project::MSG_ERR);
136
    }
137
138
    /**
139
     * Performs validation
140
     *
141
     * @param string $file
142
     *
143
     * @return void
144
     */
145
    protected function lint($file)
146
    {
147
        if (file_exists($file)) {
148
            if (is_readable($file)) {
149
                $dom = new DOMDocument();
150
                if ($dom->load($file) === false) {
151
                    $error = libxml_get_last_error();
0 ignored issues
show
Unused Code introduced by
The assignment to $error is dead and can be removed.
Loading history...
152
                    $this->logError($file . ' is not well-formed (See messages above)');
153
                } else {
154
                    if (isset($this->schema)) {
155
                        if ($this->useRNG) {
156
                            if ($dom->relaxNGValidate($this->schema->getPath())) {
157
                                $this->log($file . ' validated with RNG grammar', Project::MSG_INFO);
158
                            } else {
159
                                $this->logError($file . ' fails to validate (See messages above)');
160
                            }
161
                        } else {
162
                            if ($dom->schemaValidate($this->schema->getPath())) {
163
                                $this->log($file . ' validated with schema', Project::MSG_INFO);
164
                            } else {
165
                                $this->logError($file . ' fails to validate (See messages above)');
166
                            }
167
                        }
168
                    } else {
169
                        $this->log(
170
                            $file . ' is well-formed (not validated due to missing schema specification)',
171
                            Project::MSG_INFO
172
                        );
173
                    }
174
                }
175
            } else {
176
                $this->logError('Permission denied to read file: ' . $file);
177
            }
178
        } else {
179
            $this->logError('File not found: ' . $file);
180
        }
181
    }
182
183
    /**
184
     * Local error handler to catch validation errors and log them through Phing
185
     *
186
     * @param int $level
187
     * @param string $message
188
     * @param string $file
189
     * @param int $line
190
     * @param mixed $context
191
     *
192
     * @return void
193
     */
194
    public function errorHandler($level, $message, $file, $line, $context)
0 ignored issues
show
Unused Code introduced by
The parameter $line is not used and could be removed. ( Ignorable by Annotation )

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

194
    public function errorHandler($level, $message, $file, /** @scrutinizer ignore-unused */ $line, $context)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $file is not used and could be removed. ( Ignorable by Annotation )

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

194
    public function errorHandler($level, $message, /** @scrutinizer ignore-unused */ $file, $line, $context)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $context is not used and could be removed. ( Ignorable by Annotation )

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

194
    public function errorHandler($level, $message, $file, $line, /** @scrutinizer ignore-unused */ $context)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
195
    {
196
        $matches = [];
197
        preg_match('/^.*\(\): (.*)$/', $message, $matches);
198
        $this->log($matches[1], Project::MSG_ERR);
199
    }
200
}
201