Subversion-Projekte lars-tiefland.php_share

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
1 lars 1
<?php
2
/*
3
 *	$Id$
4
 *
5
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
6
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
7
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
8
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
9
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
10
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
11
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
12
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
13
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
14
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
15
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
16
 *
17
 * This software consists of voluntary contributions made by many individuals
18
 * and is licensed under the LGPL. For more information please see
19
 * <http://phing.info>.
20
 */
21
 
22
require_once 'phing/Task.php';
23
 
24
/**
25
 * A PHP code sniffer task. Checking the style of one or more PHP source files.
26
 *
27
 * @author	Dirk Thomas <dirk.thomas@4wdmedia.de>
28
 * @package	phing.tasks.ext
29
 */
30
class PhpCodeSnifferTask extends Task {
31
 
32
	protected $file;	// the source file (from xml attribute)
33
	protected $filesets = array(); // all fileset objects assigned to this task
34
 
35
	// parameters for php code sniffer
36
	protected $standard = 'Generic';
37
	protected $sniffs = array();
38
	protected $showWarnings = true;
39
	protected $verbosity = 0;
40
	protected $tabWidth = 0;
41
	protected $allowedFileExtensions = array('php');
42
	protected $ignorePatterns = false;
43
	protected $noSubdirectories = false;
44
	protected $configData = array();
45
 
46
	// parameters to customize output
47
	protected $showSniffs = false;
48
	protected $outputFormat = 'default';
49
 
50
	/**
51
	 * File to be performed syntax check on
52
	 * @param PhingFile $file
53
	 */
54
	public function setFile(PhingFile $file) {
55
		$this->file = $file;
56
	}
57
 
58
	/**
59
	 * Nested creator, creates a FileSet for this task
60
	 *
61
	 * @return FileSet The created fileset object
62
	 */
63
	function createFileSet() {
64
		$num = array_push($this->filesets, new FileSet());
65
		return $this->filesets[$num-1];
66
	}
67
 
68
	/**
69
	 * Sets the standard to test for
70
	 * @param string $standard
71
	 */
72
	public function setStandard($standard)
73
	{
74
		if (DIRECTORY_SEPARATOR != '/') $standard = str_replace('/', DIRECTORY_SEPARATOR, $standard);
75
		$this->standard = $standard;
76
	}
77
 
78
	/**
79
	 * Sets the sniffs which the standard should be restricted to
80
	 * @param string $sniffs
81
	 */
82
	public function setSniffs($sniffs)
83
	{
84
		$token = ' ,;';
85
		$sniff = strtok($sniffs, $token);
86
		while ($sniff !== false) {
87
			$this->sniffs[] = $sniff;
88
			$sniff = strtok($token);
89
		}
90
	}
91
 
92
	/**
93
	 * Sets the flag if warnings should be shown
94
	 * @param boolean $show
95
	 */
96
	public function setShowWarnings($show)
97
	{
98
		$this->showWarnings = StringHelper::booleanValue($show);
99
	}
100
 
101
	/**
102
	 * Sets the verbosity level
103
	 * @param int $level
104
	 */
105
	public function setVerbosity($level)
106
	{
107
		$this->verbosity = (int)$level;
108
	}
109
 
110
	/**
111
	 * Sets the tab width to replace tabs with spaces
112
	 * @param int $width
113
	 */
114
	public function setTabWidth($width)
115
	{
116
		$this->tabWidth = (int)$width;
117
	}
118
 
119
	/**
120
	 * Sets the allowed file extensions when using directories instead of specific files
121
	 * @param array $extensions
122
	 */
123
	public function setAllowedFileExtensions($extensions)
124
	{
125
		$this->allowedFileExtensions = array();
126
		$token = ' ,;';
127
		$ext = strtok($extensions, $token);
128
		while ($ext !== false) {
129
			$this->allowedFileExtensions[] = $ext;
130
			$ext = strtok($token);
131
		}
132
	}
133
 
134
	/**
135
	 * Sets the ignore patterns to skip files when using directories instead of specific files
136
	 * @param array $extensions
137
	 */
138
	public function setIgnorePatterns($patterns)
139
	{
140
		$this->ignorePatterns = array();
141
		$token = ' ,;';
142
		$pattern = strtok($patterns, $token);
143
		while ($pattern !== false) {
144
			$this->ignorePatterns[] = $pattern;
145
			$pattern = strtok($token);
146
		}
147
	}
148
 
149
	/**
150
	 * Sets the flag if subdirectories should be skipped
151
	 * @param boolean $subdirectories
152
	 */
153
	public function setNoSubdirectories($subdirectories)
154
	{
155
		$this->noSubdirectories = StringHelper::booleanValue($subdirectories);
156
	}
157
 
158
	/**
159
	 * Creates a config parameter for this task
160
	 *
161
	 * @return Parameter The created parameter
162
	 */
163
	public function createConfig() {
164
		$num = array_push($this->configData, new Parameter());
165
		return $this->configData[$num-1];
166
	}
167
 
168
	/**
169
	 * Sets the flag if the used sniffs should be listed
170
	 * @param boolean $show
171
	 */
172
	public function setShowSniffs($show)
173
	{
174
		$this->showSniffs = StringHelper::booleanValue($show);
175
	}
176
 
177
	/**
178
	 * Sets the output format
179
	 * @param string $format
180
	 */
181
	public function setFormat($format)
182
	{
183
		$this->outputFormat = $format;
184
	}
185
 
186
	/**
187
	 * Executes PHP code sniffer against PhingFile or a FileSet
188
	 */
189
	public function main() {
190
		if(!isset($this->file) and count($this->filesets) == 0) {
191
			throw new BuildException("Missing either a nested fileset or attribute 'file' set");
192
		}
193
 
194
		require_once 'PHP/CodeSniffer.php';
195
		$codeSniffer = new PHP_CodeSniffer($this->verbosity, $this->tabWidth);
196
		$codeSniffer->setAllowedFileExtensions($this->allowedFileExtensions);
197
		if (is_array($this->ignorePatterns)) $codeSniffer->setIgnorePatterns($this->ignorePatterns);
198
		foreach ($this->configData as $configData) {
199
			$codeSniffer->setConfigData($configData->getName(), $configData->getValue(), true);
200
		}
201
 
202
		if ($this->file instanceof PhingFile) {
203
			$codeSniffer->process($this->file->getPath(), $this->standard, $this->sniffs, $this->noSubdirectories);
204
 
205
		} else {
206
			$fileList = array();
207
			$project = $this->getProject();
208
			foreach ($this->filesets as $fs) {
209
				$ds = $fs->getDirectoryScanner($project);
210
				$files = $ds->getIncludedFiles();
211
				$dir = $fs->getDir($this->project)->getPath();
212
				foreach ($files as $file) {
213
					$fileList[] = $dir.DIRECTORY_SEPARATOR.$file;
214
				}
215
			}
216
			$codeSniffer->process($fileList, $this->standard, $this->sniffs, $this->noSubdirectories);
217
		}
218
		$this->output($codeSniffer);
219
	}
220
 
221
	/**
222
	 * Outputs the results
223
	 * @param PHP_CodeSniffer $codeSniffer
224
	 */
225
	protected function output($codeSniffer) {
226
		if ($this->showSniffs) {
227
			$sniffs = $codeSniffer->getSniffs();
228
			$sniffStr = '';
229
			foreach ($sniffs as $sniff) {
230
				$sniffStr .= '- ' . $sniff.PHP_EOL;
231
			}
232
			$this->log('The list of used sniffs (#' . count($sniffs) . '): ' . PHP_EOL . $sniffStr, Project::MSG_INFO);
233
		}
234
 
235
		switch ($this->outputFormat) {
236
			case 'default':
237
				$this->outputCustomFormat($codeSniffer);
238
				break;
239
			case 'xml':
240
				$codeSniffer->printXMLErrorReport($this->showWarnings);
241
				break;
242
			case 'checkstyle':
243
				$codeSniffer->printCheckstyleErrorReport($this->showWarnings);
244
				break;
245
			case 'csv':
246
				$codeSniffer->printCSVErrorReport($this->showWarnings);
247
				break;
248
			case 'report':
249
				$codeSniffer->printErrorReport($this->showWarnings);
250
				break;
251
			case 'summary':
252
				$codeSniffer->printErrorReportSummary($this->showWarnings);
253
				break;
254
			case 'doc':
255
				$codeSniffer->generateDocs($this->standard, $this->sniffs);
256
				break;
257
			default:
258
				$this->log('Unknown output format "' . $this->outputFormat . '"', Project::MSG_INFO);
259
				break;
260
		}
261
	}
262
 
263
	/**
264
	 * Outputs the results with a custom format
265
	 * @param PHP_CodeSniffer $codeSniffer
266
	 */
267
	protected function outputCustomFormat($codeSniffer) {
268
		$report = $codeSniffer->prepareErrorReport($this->showWarnings);
269
 
270
		$files = $report['files'];
271
		foreach ($files as $file => $attributes) {
272
			$errors = $attributes['errors'];
273
			$warnings = $attributes['warnings'];
274
			$messages = $attributes['messages'];
275
			if ($errors > 0) {
276
				$this->log($file . ': ' . $errors . ' error' . ($errors > 1 ? 's' : '') . ' detected', Project::MSG_ERR);
277
				$this->outputCustomFormatMessages($messages, 'ERROR');
278
			} else {
279
				$this->log($file . ': No syntax errors detected', Project::MSG_VERBOSE);
280
			}
281
			if ($warnings > 0) {
282
				$this->log($file . ': ' . $warnings . ' warning' . ($warnings > 1 ? 's' : '') . ' detected', Project::MSG_WARN);
283
				$this->outputCustomFormatMessages($messages, 'WARNING');
284
			}
285
		}
286
 
287
		$totalErrors = $report['totals']['errors'];
288
		$totalWarnings = $report['totals']['warnings'];
289
		$this->log(count($files) . ' files where checked', Project::MSG_INFO);
290
		if ($totalErrors > 0) {
291
			$this->log($totalErrors . ' error' . ($totalErrors > 1 ? 's' : '') . ' detected', Project::MSG_ERR);
292
		} else {
293
			$this->log('No syntax errors detected', Project::MSG_INFO);
294
		}
295
		if ($totalWarnings > 0) {
296
			$this->log($totalWarnings . ' warning' . ($totalWarnings > 1 ? 's' : '') . ' detected', Project::MSG_INFO);
297
		}
298
	}
299
 
300
	/**
301
	 * Outputs the messages of a specific type for one file
302
	 * @param array $messages
303
	 * @param string $type
304
	 */
305
	protected function outputCustomFormatMessages($messages, $type) {
306
		foreach ($messages as $line => $messagesPerLine) {
307
			foreach ($messagesPerLine as $column => $messagesPerColumn) {
308
				foreach ($messagesPerColumn as $message) {
309
					$msgType = $message['type'];
310
					if ($type == $msgType) {
311
						$logLevel = Project::MSG_INFO;
312
						if ($msgType == 'ERROR') {
313
							$logLevel = Project::MSG_ERR;
314
						} else if ($msgType == 'WARNING') {
315
							$logLevel = Project::MSG_WARN;
316
						}
317
						$text = $message['message'];
318
						$string = $msgType . ' in line ' . $line . ' column ' . $column . ': ' . $text;
319
						$this->log($string, $logLevel);
320
					}
321
				}
322
			}
323
		}
324
	}
325
 
326
}