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
/**
21
 * Runs the PHP_Depend software analyzer and metric tool.
22
 * Performs static code analysis on a given source base.
23
 *
24
 * @package phing.tasks.ext.pdepend
25
 * @author  Benjamin Schultz <bschultz@proqrent.de>
26
 * @since   2.4.1
27
 */
28
class PhpDependTask extends Task
29
{
30
    use FileSetAware;
31

32
    /**
33
     * A php source code filename or directory
34
     *
35
     * @var PhingFile
36
     */
37
    protected $file = null;
38

39
    /**
40
     * List of allowed file extensions. Default file extensions are <b>php</b>
41
     * and <p>php5</b>.
42
     *
43
     * @var array<string>
44
     */
45
    protected $allowedFileExtensions = ['php', 'php5'];
46

47
    /**
48
     * List of exclude directories. Default exclude dirs are <b>.git</b>,
49
     * <b>.svn</b> and <b>CVS</b>.
50
     *
51
     * @var array<string>
52
     */
53
    protected $excludeDirectories = ['.git', '.svn', 'CVS'];
54

55
    /**
56
     * List of exclude packages
57
     *
58
     * @var array<string>
59
     */
60
    protected $excludePackages = [];
61

62
    /**
63
     * Should the parse ignore doc comment annotations?
64
     *
65
     * @var boolean
66
     */
67
    protected $withoutAnnotations = false;
68

69
    /**
70
     * Should PHP_Depend treat <b>+global</b> as a regular project package?
71
     *
72
     * @var boolean
73
     */
74
    protected $supportBadDocumentation = false;
75

76
    /**
77
     * Flag for enable/disable debugging
78
     *
79
     * @var boolean
80
     */
81
    protected $debug = false;
82

83
    /**
84
     * PHP_Depend configuration file
85
     *
86
     * @var PhingFile
87
     */
88
    protected $configFile = null;
89

90
    /**
91
     * Logger elements
92
     *
93
     * @var PhpDependLoggerElement[]
94
     */
95
    protected $loggers = [];
96

97
    /**
98
     * Analyzer elements
99
     *
100
     * @var PhpDependAnalyzerElement[]
101
     */
102
    protected $analyzers = [];
103

104
    /**
105
     * Holds the PHP_Depend runner instance
106
     *
107
     * @var PHP_Depend_TextUI_Runner
108
     */
109
    protected $runner = null;
110

111
    /**
112
     * Flag that determines whether to halt on error
113
     *
114
     * @var boolean
115
     */
116
    protected $haltonerror = false;
117

118
    /**
119
     * @var bool
120
     */
121
    private $oldVersion = false;
122

123
    /**
124
     * @var string
125
     */
126
    protected $pharLocation = "";
127

128
    /**
129
     * Load the necessary environment for running PHP_Depend
130
     *
131
     * @throws BuildException
132
     */
133 1
    protected function requireDependencies()
134
    {
135 1
        if (!empty($this->pharLocation)) {
136 0
            include_once 'phar://' . $this->pharLocation . '/vendor/autoload.php';
137
        }
138

139
        // check 2.x version (composer/phar)
140 1
        if (class_exists('PDepend\\TextUI\\Runner')) {
141 1
            return;
142
        }
143

144 0
        $this->oldVersion = true;
145

146
        // check 1.x version (composer)
147 0
        if (class_exists('PHP_Depend_TextUI_Runner')) {
148
            // include_path hack for PHP_Depend 1.1.3
149 0
            $rc = new ReflectionClass('PHP_Depend');
150 0
            set_include_path(get_include_path() . ":" . realpath(dirname($rc->getFileName()) . "/../"));
151

152 0
            return;
153
        }
154

155 0
        @include_once 'PHP/Depend/Autoload.php';
156

157 0
        if (!class_exists('PHP_Depend_Autoload')) {
158 0
            throw new BuildException(
159 0
                'PhpDependTask depends on PHP_Depend being installed and on include_path',
160 0
                $this->getLocation()
161
            );
162
        }
163

164
        // register PHP_Depend autoloader
165 0
        $autoload = new PHP_Depend_Autoload();
166 0
        $autoload->register();
167
    }
168

169
    /**
170
     * Set the input source file or directory
171
     *
172
     * @param PhingFile $file The input source file or directory
173
     */
174 0
    public function setFile(PhingFile $file)
175
    {
176 0
        $this->file = $file;
177
    }
178

179
    /**
180
     * Sets a list of filename extensions for valid php source code files
181
     *
182
     * @param string $fileExtensions List of valid file extensions
183
     */
184 0
    public function setAllowedFileExtensions($fileExtensions)
185
    {
186 0
        $this->allowedFileExtensions = [];
187

188 0
        $token = ' ,;';
189 0
        $ext = strtok($fileExtensions, $token);
190

191 0
        while ($ext !== false) {
192 0
            $this->allowedFileExtensions[] = $ext;
193 0
            $ext = strtok($token);
194
        }
195
    }
196

197
    /**
198
     * Sets a list of exclude directories
199
     *
200
     * @param string $excludeDirectories List of exclude directories
201
     */
202 0
    public function setExcludeDirectories($excludeDirectories)
203
    {
204 0
        $this->excludeDirectories = [];
205

206 0
        $token = ' ,;';
207 0
        $pattern = strtok($excludeDirectories, $token);
208

209 0
        while ($pattern !== false) {
210 0
            $this->excludeDirectories[] = $pattern;
211 0
            $pattern = strtok($token);
212
        }
213
    }
214

215
    /**
216
     * Sets a list of exclude packages
217
     *
218
     * @param string $excludePackages Exclude packages
219
     */
220 0
    public function setExcludePackages($excludePackages)
221
    {
222 0
        $this->excludePackages = [];
223

224 0
        $token = ' ,;';
225 0
        $pattern = strtok($excludePackages, $token);
226

227 0
        while ($pattern !== false) {
228 0
            $this->excludePackages[] = $pattern;
229 0
            $pattern = strtok($token);
230
        }
231
    }
232

233
    /**
234
     * Should the parser ignore doc comment annotations?
235
     *
236
     * @param boolean $withoutAnnotations
237
     */
238 0
    public function setWithoutAnnotations($withoutAnnotations)
239
    {
240 0
        $this->withoutAnnotations = StringHelper::booleanValue($withoutAnnotations);
241
    }
242

243
    /**
244
     * Should PHP_Depend support projects with a bad documentation. If this
245
     * option is set to <b>true</b>, PHP_Depend will treat the default package
246
     * <b>+global</b> as a regular project package.
247
     *
248
     * @param boolean $supportBadDocumentation
249
     */
250 0
    public function setSupportBadDocumentation($supportBadDocumentation)
251
    {
252 0
        $this->supportBadDocumentation = StringHelper::booleanValue($supportBadDocumentation);
253
    }
254

255
    /**
256
     * Set debugging On/Off
257
     *
258
     * @param boolean $debug
259
     */
260 0
    public function setDebug($debug)
261
    {
262 0
        $this->debug = StringHelper::booleanValue($debug);
263
    }
264

265
    /**
266
     * Set halt on error
267
     *
268
     * @param boolean $haltonerror
269
     */
270 0
    public function setHaltonerror($haltonerror)
271
    {
272 0
        $this->haltonerror = StringHelper::booleanValue($haltonerror);
273
    }
274

275
    /**
276
     * Set the configuration file
277
     *
278
     * @param PhingFile $configFile The configuration file
279
     */
280 0
    public function setConfigFile(PhingFile $configFile)
281
    {
282 0
        $this->configFile = $configFile;
283
    }
284

285
    /**
286
     * Create object for nested logger element
287
     *
288
     * @return PhpDependLoggerElement
289
     */
290 1
    public function createLogger()
291
    {
292 1
        $num = array_push($this->loggers, new PhpDependLoggerElement());
293

294 1
        return $this->loggers[$num - 1];
295
    }
296

297
    /**
298
     * Create object for nested analyzer element
299
     *
300
     * @return PhpDependAnalyzerElement
301
     */
302 1
    public function createAnalyzer()
303
    {
304 1
        $num = array_push($this->analyzers, new PhpDependAnalyzerElement());
305

306 1
        return $this->analyzers[$num - 1];
307
    }
308

309
    /**
310
     * @param string $pharLocation
311
     */
312 0
    public function setPharLocation($pharLocation)
313
    {
314 0
        $this->pharLocation = $pharLocation;
315
    }
316

317
    /**
318
     * Executes PHP_Depend_TextUI_Runner against PhingFile or a FileSet
319
     *
320
     * @throws BuildException
321
     */
322 1
    public function main()
323
    {
324 1
        $this->requireDependencies();
325

326 1
        if (!isset($this->file) and count($this->filesets) == 0) {
327 0
            throw new BuildException('Missing either a nested fileset or attribute "file" set');
328
        }
329

330 1
        if (count($this->loggers) == 0) {
331 0
            throw new BuildException('Missing nested "logger" element');
332
        }
333

334 1
        $this->validateLoggers();
335 1
        $this->validateAnalyzers();
336

337 1
        $filesToParse = $this->getFilesToParse();
338

339 1
        $runner = $this->createRunner();
340 1
        $runner->setSourceArguments($filesToParse);
341

342 1
        foreach ($this->loggers as $logger) {
343
            // Register logger
344 1
            if ($this->oldVersion) {
345 0
                $runner->addLogger(
346 0
                    $logger->getType(),
347 0
                    $logger->getOutfile()->__toString()
348
                );
349
            } else {
350 1
                $runner->addReportGenerator(
351 1
                    $logger->getType(),
352 1
                    $logger->getOutfile()->__toString()
353
                );
354
            }
355
        }
356

357 1
        foreach ($this->analyzers as $analyzer) {
358
            // Register additional analyzer
359 1
            $runner->addOption(
360 1
                $analyzer->getType(),
361 1
                $analyzer->getValue()
362
            );
363
        }
364

365
        // Disable annotation parsing
366 1
        if ($this->withoutAnnotations) {
367 0
            $runner->setWithoutAnnotations();
368
        }
369

370
        // Enable bad documentation support
371 1
        if ($this->supportBadDocumentation) {
372 0
            $runner->setSupportBadDocumentation();
373
        }
374

375
        // Check for suffix
376 1
        if (count($this->allowedFileExtensions) > 0) {
377 1
            $runner->setFileExtensions($this->allowedFileExtensions);
378
        }
379

380
        // Check for ignore directories
381 1
        if (count($this->excludeDirectories) > 0) {
382 1
            $runner->setExcludeDirectories($this->excludeDirectories);
383
        }
384

385
        // Check for exclude packages
386 1
        if (count($this->excludePackages) > 0) {
387 0
            $runner->setExcludePackages($this->excludePackages);
388
        }
389

390 1
        $runner->run();
391

392 1
        if ($runner->hasParseErrors() === true) {
393 0
            $this->log('Following errors occurred:');
394

395 0
            foreach ($runner->getParseErrors() as $error) {
396 0
                $this->log($error);
397
            }
398

399 0
            if ($this->haltonerror === true) {
400 0
                throw new BuildException('Errors occurred during parse process');
401
            }
402
        }
403
    }
404

405
    /**
406
     * Validates the available loggers
407
     *
408
     * @throws BuildException
409
     */
410 1
    protected function validateLoggers()
411
    {
412 1
        foreach ($this->loggers as $logger) {
413 1
            if ($logger->getType() === '') {
414 0
                throw new BuildException('Logger missing required "type" attribute');
415
            }
416

417 1
            if ($logger->getOutfile() === null) {
418 0
                throw new BuildException('Logger requires "outfile" attribute');
419
            }
420
        }
421
    }
422

423
    /**
424
     * Validates the available analyzers
425
     *
426
     * @throws BuildException
427
     */
428 1
    protected function validateAnalyzers()
429
    {
430 1
        foreach ($this->analyzers as $analyzer) {
431 1
            if ($analyzer->getType() === '') {
432 0
                throw new BuildException('Analyzer missing required "type" attribute');
433
            }
434

435 1
            if (count($analyzer->getValue()) === 0) {
436 0
                throw new BuildException('Analyzer missing required "value" attribute');
437
            }
438
        }
439
    }
440

441
    /**
442
     * @return array
443
     */
444 1
    private function getFilesToParse()
445
    {
446 1
        $filesToParse = [];
447

448 1
        if ($this->file instanceof PhingFile) {
449 0
            $filesToParse[] = $this->file->__toString();
450 0
            return $filesToParse;
451
        }
452

453
// append any files in filesets
454 1
        foreach ($this->filesets as $fs) {
455 1
            $files = $fs->getDirectoryScanner($this->project)->getIncludedFiles();
456

457 1
            foreach ($files as $filename) {
458 1
                $f = new PhingFile($fs->getDir($this->project), $filename);
459 1
                $filesToParse[] = $f->getAbsolutePath();
460
            }
461
        }
462 1
        return $filesToParse;
463
    }
464

465
    /**
466
     * @return object
467
     */
468 1
    private function createRunner()
469
    {
470 1
        if ($this->oldVersion) {
471 0
            return $this->createLegacyRunner();
472
        }
473

474 1
        $applicationClassName = 'PDepend\\Application';
475 1
        $application = new $applicationClassName();
476

477 1
        $runner = $application->getRunner();
478

479 1
        $configuration = $this->getConfiguration();
480

481 1
        if ($configuration === null) {
482 1
            $configuration = $application->getConfiguration();
483
        }
484

485 1
        if ($this->debug) {
486
            // Enable debug logging
487 0
            PDepend\Util\Log::setSeverity(1);
488
        }
489

490 1
        PDepend\Util\ConfigurationInstance::set($configuration);
491

492 1
        return $runner;
493
    }
494

495
    /**
496
     * @return PHP_Depend_TextUI_Runner
497
     */
498 0
    private function createLegacyRunner()
499
    {
500 0
        $runner = new PHP_Depend_TextUI_Runner();
501 0
        $runner->addProcessListener(new PHP_Depend_TextUI_ResultPrinter());
502

503 0
        if ($this->debug) {
504 0
            include_once 'PHP/Depend/Util/Log.php';
505
            // Enable debug logging
506 0
            PHP_Depend_Util_Log::setSeverity(PHP_Depend_Util_Log::DEBUG);
507
        }
508

509 0
        $configuration = $this->getConfiguration();
510

511 0
        if ($configuration === null) {
512 0
            $configurationFactory = new PHP_Depend_Util_Configuration_Factory();
513 0
            $configuration = $configurationFactory->createDefault();
514
        }
515

516 0
        PHP_Depend_Util_ConfigurationInstance::set($configuration);
517 0
        $runner->setConfiguration($configuration);
518

519 0
        return $runner;
520
    }
521

522
    /**
523
     * Loads configuration file
524
     *
525
     * @return null|PHP_Depend_Util_Configuration
526
     * @throws BuildException
527
     */
528 1
    private function getConfiguration()
529
    {
530
        // Check for configuration option
531 1
        if ($this->configFile == null || !($this->configFile instanceof PhingFile)) {
532 1
            return null;
533
        }
534

535 0
        if (file_exists($this->configFile->__toString()) === false) {
536 0
            throw new BuildException(
537 0
                'The configuration file "' . $this->configFile->__toString() . '" doesn\'t exist.'
538
            );
539
        }
540

541 0
        if ($this->oldVersion) {
542 0
            $configurationClassName = 'PHP_Depend_Util_Configuration';
543
        } else {
544 0
            $configurationClassName = 'PDepend\\Util\\Configuration';
545
        }
546

547 0
        return new $configurationClassName(
548 0
            $this->configFile->__toString(),
549 0
            null,
550 0
            true
551
        );
552
    }
553
}

Read our documentation on viewing source code .

Loading