pax_global_header00006660000000000000000000000064132032504300014502gustar00rootroot0000000000000052 comment=dfed51c1288790fc957c9433e2f49ab152e8a564 phpcpd-3.0.1/000077500000000000000000000000001320325043000127615ustar00rootroot00000000000000phpcpd-3.0.1/.gitattributes000066400000000000000000000000171320325043000156520ustar00rootroot00000000000000*.php diff=php phpcpd-3.0.1/.gitignore000066400000000000000000000001161320325043000147470ustar00rootroot00000000000000/build/phar /build/*.phar* /.idea /composer.lock /vendor /tools /.php_cs.cachephpcpd-3.0.1/.php_cs000066400000000000000000000061461320325043000142450ustar00rootroot00000000000000 For the full copyright and license information, please view the LICENSE file that was distributed with this source code. EOF; return PhpCsFixer\Config::create() ->setRiskyAllowed(true) ->setRules( [ 'array_syntax' => ['syntax' => 'short'], 'binary_operator_spaces' => [ 'align_double_arrow' => true, 'align_equals' => true ], 'blank_line_after_namespace' => true, 'blank_line_before_statement' => [ 'statements' => [ 'break', 'continue', 'return', 'throw', 'try', ], ], 'braces' => true, 'cast_spaces' => true, 'concat_space' => ['spacing' => 'one'], 'elseif' => true, 'encoding' => true, 'full_opening_tag' => true, 'function_declaration' => true, 'header_comment' => ['header' => $header, 'separate' => 'none'], 'indentation_type' => true, 'line_ending' => true, 'lowercase_constants' => true, 'lowercase_keywords' => true, 'method_argument_space' => true, 'native_function_invocation' => true, 'no_alias_functions' => true, 'no_blank_lines_after_class_opening' => true, 'no_blank_lines_after_phpdoc' => true, 'no_closing_tag' => true, 'no_empty_phpdoc' => true, 'no_empty_statement' => true, 'no_extra_consecutive_blank_lines' => true, 'no_leading_namespace_whitespace' => true, 'no_singleline_whitespace_before_semicolons' => true, 'no_spaces_after_function_name' => true, 'no_spaces_inside_parenthesis' => true, 'no_trailing_comma_in_list_call' => true, 'no_trailing_whitespace' => true, 'no_unused_imports' => true, 'no_whitespace_in_blank_line' => true, 'phpdoc_align' => true, 'phpdoc_indent' => true, 'phpdoc_no_access' => true, 'phpdoc_no_empty_return' => true, 'phpdoc_no_package' => true, 'phpdoc_scalar' => true, 'phpdoc_separation' => true, 'phpdoc_to_comment' => true, 'phpdoc_trim' => true, 'phpdoc_types' => true, 'phpdoc_var_without_name' => true, 'self_accessor' => true, 'simplified_null_return' => true, 'single_blank_line_at_eof' => true, 'single_import_per_statement' => true, 'single_line_after_imports' => true, 'single_quote' => true, 'ternary_operator_spaces' => true, 'trim_array_spaces' => true, 'visibility_required' => true, ] ) ->setFinder( PhpCsFixer\Finder::create() ->files() ->in(__DIR__ . '/src') ->name('*.php') ); phpcpd-3.0.1/.travis.yml000066400000000000000000000012021320325043000150650ustar00rootroot00000000000000language: php sudo: false php: - 5.6 - 7.0 - 7.0snapshot - 7.1 - 7.1snapshot - 7.2 - 7.2snapshot - master env: matrix: - DEPENDENCIES="high" - DEPENDENCIES="low" global: - DEFAULT_COMPOSER_FLAGS="--no-interaction --no-ansi --no-progress --no-suggest" before_install: - composer self-update - composer clear-cache install: - if [[ "$DEPENDENCIES" = 'high' ]]; then travis_retry composer update $DEFAULT_COMPOSER_FLAGS; fi - if [[ "$DEPENDENCIES" = 'low' ]]; then travis_retry composer update $DEFAULT_COMPOSER_FLAGS --prefer-lowest; fi script: - php phpunit.phar notifications: email: false phpcpd-3.0.1/ChangeLog.md000066400000000000000000000020161320325043000151310ustar00rootroot00000000000000# Changes in PHPCPD All notable changes in PHPCPD are documented in this file using the [Keep a CHANGELOG](http://keepachangelog.com/) principles. ## [3.0.1] - 2017-11-16 ### Fixed * Fixed [#147](https://github.com/sebastianbergmann/phpcpd/issues/147): Wrong exit code when no files were found to be scanned * Fixed [#152](https://github.com/sebastianbergmann/phpcpd/issues/152): Version requirement for `sebastian/version` is too strict ## [3.0.0] - 2017-02-05 ### Added * Merged [#90](https://github.com/sebastianbergmann/phpcpd/pull/90): The PMD logger now replaces all characters that are invalid XML with `U+FFFD` * Merged [#100](https://github.com/sebastianbergmann/phpcpd/pull/100): Added the `--regexps-exclude` option ### Changed * When the Xdebug extension is loaded, PHPCPD disables as much of Xdebug's functionality as possible to minimize the performance impact ### Removed * PHPCPD is no longer supported on PHP 5.3, PHP 5.4, and PHP 5.5 [3.0.0]: https://github.com/sebastianbergmann/phpcpd/compare/2.0...3.0.0 phpcpd-3.0.1/LICENSE000066400000000000000000000030051320325043000137640ustar00rootroot00000000000000phpcpd Copyright (c) 2009-2017, Sebastian Bergmann . All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Sebastian Bergmann nor the names of his contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. phpcpd-3.0.1/README.md000066400000000000000000000034761320325043000142520ustar00rootroot00000000000000[![Latest Stable Version](https://img.shields.io/packagist/v/sebastian/phpcpd.svg?style=flat-square)](https://packagist.org/packages/sebastian/phpcpd) [![Minimum PHP Version](https://img.shields.io/badge/php-%3E%3D%205.6-8892BF.svg?style=flat-square)](https://php.net/) [![Build Status](https://img.shields.io/travis/sebastianbergmann/phpcpd/master.svg?style=flat-square)](https://travis-ci.org/sebastianbergmann/phpcpd) # PHP Copy/Paste Detector (PHPCPD) `phpcpd` is a Copy/Paste Detector (CPD) for PHP code. ## Installation ### PHP Archive (PHAR) The easiest way to obtain PHPCPD is to download a [PHP Archive (PHAR)](http://php.net/phar) that has all required dependencies of PHPCPD bundled in a single file: $ wget https://phar.phpunit.de/phpcpd.phar $ chmod +x phpcpd.phar $ mv phpcpd.phar /usr/local/bin/phpcpd You can also immediately use the PHAR after you have downloaded it, of course: $ wget https://phar.phpunit.de/phpcpd.phar $ php phpcpd.phar ### Composer You can add this tool as a local, per-project, development-time dependency to your project using [Composer](https://getcomposer.org/): $ composer require --dev sebastian/phpcpd You can then invoke it using the `vendor/bin/phpcpd` executable. ## Usage Example $ phpcpd wordpress-4.7.1 phpcpd 3.0.0 by Sebastian Bergmann. Found 59 clones with 2548 duplicated lines in 39 files: - /home/sb/wordpress-4.7.1/wp-admin/includes/class-ftp-pure.php:99-114 /home/sb/wordpress-4.7.1/wp-admin/includes/class-ftp-sockets.php:119-134 . . . - /home/sb/wordpress-4.7.1/wp-includes/class-wp-customize-manager.php:277-329 /home/sb/wordpress-4.7.1/wp-includes/class-wp-customize-control.php:652-704 0.77% duplicated lines out of 332387 total lines of code. Time: 2.91 seconds, Memory: 232.00MB phpcpd-3.0.1/build.xml000066400000000000000000000110611320325043000146010ustar00rootroot00000000000000 phpcpd-3.0.1/build/000077500000000000000000000000001320325043000140605ustar00rootroot00000000000000phpcpd-3.0.1/build/phar-autoload.php.in000066400000000000000000000024131320325043000177360ustar00rootroot00000000000000#!/usr/bin/env php ')) { fwrite( STDERR, sprintf( 'This version of PHPCPD is supported on PHP 5.6, PHP 7.0, and PHP 7.1.' . PHP_EOL . 'You are using PHP %s%s.' . PHP_EOL, PHP_VERSION, defined('PHP_BINARY') ? ' (' . PHP_BINARY . ')' : '' ) ); die(1); } if ($_SERVER['SCRIPT_NAME'] != '-') { $phar = realpath($_SERVER['SCRIPT_NAME']); } else { $files = get_included_files(); $phar = $files[0]; } define('__PHPCPD_PHAR__', str_replace(DIRECTORY_SEPARATOR, '/', $phar)); define('__PHPCPD_PHAR_ROOT__', 'phar://___PHAR___'); spl_autoload_register( function ($class) { static $classes = NULL; if ($classes === NULL) { $classes = array( ___CLASSLIST___ ); } $class = strtolower($class); if (isset($classes[$class])) { require 'phar://___PHAR___' . $classes[$class]; } } ); Phar::mapPhar('___PHAR___'); if (isset($_SERVER['argv'][1]) && $_SERVER['argv'][1] == '--manifest') { print file_get_contents(__PHPCPD_PHAR_ROOT__ . '/manifest.txt'); exit; } $application = new SebastianBergmann\PHPCPD\CLI\Application; $application->run(); __HALT_COMPILER(); phpcpd-3.0.1/build/phar-manifest.php000077500000000000000000000013421320325043000173320ustar00rootroot00000000000000#!/usr/bin/env php &1'); if (strpos($tag, '-') === false && strpos($tag, 'No names found') === false) { print $tag; } else { $branch = @exec('git rev-parse --abbrev-ref HEAD'); $hash = @exec('git log -1 --format="%H"'); print $branch . '@' . $hash; } print "\n"; $lock = json_decode(file_get_contents(__DIR__ . '/../composer.lock')); foreach ($lock->packages as $package) { print $package->name . ': ' . $package->version; if (!preg_match('/^[v= ]*(([0-9]+)(\\.([0-9]+)(\\.([0-9]+)(-([0-9]+))?(-?([a-zA-Z-+][a-zA-Z0-9\\.\\-:]*)?)?)?)?)$/', $package->version)) { print '@' . $package->source->reference; } print "\n"; } phpcpd-3.0.1/composer.json000066400000000000000000000020301320325043000154760ustar00rootroot00000000000000{ "name": "sebastian/phpcpd", "description": "Copy/Paste Detector (CPD) for PHP code.", "homepage": "https://github.com/sebastianbergmann/phpcpd", "license": "BSD-3-Clause", "authors": [ { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de", "role": "lead" } ], "support": { "issues": "https://github.com/sebastianbergmann/phpcpd/issues" }, "config": { "platform": { "php": "5.6.0" }, "optimize-autoloader": true, "sort-packages": true }, "prefer-stable": true, "require": { "php": "^5.6|^7.0", "sebastian/finder-facade": "^1.1", "sebastian/version": "^1.0|^2.0", "symfony/console": "^2.7|^3.0|^4.0", "phpunit/php-timer": "^1.0.6" }, "autoload": { "classmap": [ "src/" ] }, "bin": [ "phpcpd" ], "extra": { "branch-alias": { "dev-master": "3.0-dev" } } } phpcpd-3.0.1/phive.xml000066400000000000000000000003671320325043000146240ustar00rootroot00000000000000 phpcpd-3.0.1/phpcpd000077500000000000000000000025311320325043000141660ustar00rootroot00000000000000#!/usr/bin/env php * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ if (version_compare('5.6.0', PHP_VERSION, '>')) { fwrite( STDERR, sprintf( 'This version of PHPCPD is supported on PHP 5.6, PHP 7.0, and PHP 7.1.' . PHP_EOL . 'You are using PHP %s%s.' . PHP_EOL, PHP_VERSION, defined('PHP_BINARY') ? ' (' . PHP_BINARY . ')' : '' ) ); die(1); } // @see https://github.com/sebastianbergmann/phpcpd/issues/18 ini_set('mbstring.func_overload', 0); if (ini_get('mbstring.internal_encoding')) { ini_set('mbstring.internal_encoding', NULL); } $loaded = false; foreach (array(__DIR__ . '/../../autoload.php', __DIR__ . '/vendor/autoload.php') as $file) { if (file_exists($file)) { require $file; $loaded = true; break; } } if (!$loaded) { die( 'You need to set up the project dependencies using the following commands:' . PHP_EOL . 'wget http://getcomposer.org/composer.phar' . PHP_EOL . 'php composer.phar install' . PHP_EOL ); } $application = new SebastianBergmann\PHPCPD\CLI\Application; $application->run(); phpcpd-3.0.1/phpunit.xml000066400000000000000000000013461320325043000151760ustar00rootroot00000000000000 tests src phpcpd-3.0.1/src/000077500000000000000000000000001320325043000135505ustar00rootroot00000000000000phpcpd-3.0.1/src/CLI/000077500000000000000000000000001320325043000141575ustar00rootroot00000000000000phpcpd-3.0.1/src/CLI/Application.php000066400000000000000000000055541320325043000171440ustar00rootroot00000000000000 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\PHPCPD\CLI; use SebastianBergmann\Version; use Symfony\Component\Console\Application as AbstractApplication; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Input\ArrayInput; class Application extends AbstractApplication { public function __construct() { $version = new Version('3.0.1', \dirname(\dirname(__DIR__))); parent::__construct('phpcpd', $version->getVersion()); } /** * Gets the name of the command based on input. * * @param InputInterface $input The input interface * * @return string The command name */ protected function getCommandName(InputInterface $input) { return 'phpcpd'; } /** * Gets the default commands that should always be available. * * @return array An array of default Command instances */ protected function getDefaultCommands() { $defaultCommands = parent::getDefaultCommands(); $defaultCommands[] = new Command; return $defaultCommands; } /** * Overridden so that the application doesn't expect the command * name to be the first argument. */ public function getDefinition() { $inputDefinition = parent::getDefinition(); $inputDefinition->setArguments(); return $inputDefinition; } /** * Runs the current application. * * @param InputInterface $input An Input instance * @param OutputInterface $output An Output instance * * @return int 0 if everything went fine, or an error code */ public function doRun(InputInterface $input, OutputInterface $output) { $this->disableXdebug(); if (!$input->hasParameterOption('--quiet')) { $output->write( \sprintf( "phpcpd %s by Sebastian Bergmann.\n\n", $this->getVersion() ) ); } if ($input->hasParameterOption('--version') || $input->hasParameterOption('-V')) { exit; } if (!$input->getFirstArgument()) { $input = new ArrayInput(['--help']); } parent::doRun($input, $output); } private function disableXdebug() { if (!\extension_loaded('xdebug')) { return; } \ini_set('xdebug.scream', 0); \ini_set('xdebug.max_nesting_level', 8192); \ini_set('xdebug.show_exception_trace', 0); \ini_set('xdebug.show_error_trace', 0); \xdebug_disable(); } } phpcpd-3.0.1/src/CLI/Command.php000066400000000000000000000132201320325043000162440ustar00rootroot00000000000000 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\PHPCPD\CLI; use SebastianBergmann\PHPCPD\Detector\Detector; use SebastianBergmann\PHPCPD\Detector\Strategy\DefaultStrategy; use SebastianBergmann\PHPCPD\Log\PMD; use SebastianBergmann\PHPCPD\Log\Text; use SebastianBergmann\FinderFacade\FinderFacade; use Symfony\Component\Console\Command\Command as AbstractCommand; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Helper\ProgressBar; class Command extends AbstractCommand { /** * Configures the current command. */ protected function configure() { $this->setName('phpcpd') ->setDefinition( [ new InputArgument( 'values', InputArgument::IS_ARRAY, 'Files and directories to analyze' ) ] ) ->addOption( 'names', null, InputOption::VALUE_REQUIRED, 'A comma-separated list of file names to check', ['*.php'] ) ->addOption( 'names-exclude', null, InputOption::VALUE_REQUIRED, 'A comma-separated list of file names to exclude', [] ) ->addOption( 'regexps-exclude', null, InputOption::VALUE_REQUIRED, 'A comma-separated list of paths regexps to exclude (example: "#var/.*_tmp#")', [] ) ->addOption( 'exclude', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'Exclude a directory from code analysis (must be relative to source)' ) ->addOption( 'log-pmd', null, InputOption::VALUE_REQUIRED, 'Write result in PMD-CPD XML format to file' ) ->addOption( 'min-lines', null, InputOption::VALUE_REQUIRED, 'Minimum number of identical lines', 5 ) ->addOption( 'min-tokens', null, InputOption::VALUE_REQUIRED, 'Minimum number of identical tokens', 70 ) ->addOption( 'fuzzy', null, InputOption::VALUE_NONE, 'Fuzz variable names' ) ->addOption( 'progress', null, InputOption::VALUE_NONE, 'Show progress bar' ); } /** * Executes the current command. * * @param InputInterface $input An InputInterface instance * @param OutputInterface $output An OutputInterface instance * * @return null|int null or 0 if everything went fine, or an error code */ protected function execute(InputInterface $input, OutputInterface $output) { $finder = new FinderFacade( $input->getArgument('values'), $input->getOption('exclude'), $this->handleCSVOption($input, 'names'), $this->handleCSVOption($input, 'names-exclude'), $this->handleCSVOption($input, 'regexps-exclude') ); $files = $finder->findFiles(); if (empty($files)) { $output->writeln('No files found to scan'); exit(0); } $progressBar = null; if ($input->getOption('progress')) { $progressBar = new ProgressBar($output, \count($files)); $progressBar->start(); } $strategy = new DefaultStrategy; $detector = new Detector($strategy, $progressBar); $quiet = $output->getVerbosity() == OutputInterface::VERBOSITY_QUIET; $clones = $detector->copyPasteDetection( $files, $input->getOption('min-lines'), $input->getOption('min-tokens'), $input->getOption('fuzzy') ); if ($input->getOption('progress')) { $progressBar->finish(); $output->writeln("\n"); } if (!$quiet) { $printer = new Text; $printer->printResult($output, $clones); unset($printer); } $logPmd = $input->getOption('log-pmd'); if ($logPmd) { $pmd = new PMD($logPmd); $pmd->processClones($clones); unset($pmd); } if (!$quiet) { print \PHP_Timer::resourceUsage() . "\n"; } if (\count($clones) > 0) { exit(1); } } /** * @param \Symfony\Component\Console\Input\InputInterface $input * @param string $option * * @return array */ private function handleCSVOption(InputInterface $input, $option) { $result = $input->getOption($option); if (!\is_array($result)) { $result = \explode(',', $result); \array_map('trim', $result); } return $result; } } phpcpd-3.0.1/src/CodeClone.php000066400000000000000000000052121320325043000161140ustar00rootroot00000000000000 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\PHPCPD; class CodeClone { /** * @var int Size of the clone (lines) */ private $size; /** * @var int Size of the clone (tokens) */ private $tokens; /** * @var CodeCloneFile[] Files with this code clone */ private $files = []; /** * @var string Unique ID of Code Duplicate Fragment */ private $id; /** * @var string Lines of the clone */ private $lines = ''; /** * Constructor. * * @param CodeCloneFile $fileA * @param CodeCloneFile $fileB * @param int $size * @param int $tokens */ public function __construct(CodeCloneFile $fileA, CodeCloneFile $fileB, $size, $tokens) { $this->addFile($fileA); $this->addFile($fileB); $this->size = $size; $this->tokens = $tokens; $this->id = \md5($this->getLines()); } /** * Add file with clone * * @param CodeCloneFile $file */ public function addFile(CodeCloneFile $file) { $id = $file->getId(); if (!isset($this->files[$id])) { $this->files[$id] = $file; } } /** * Get files with clone * * @return CodeCloneFile[] */ public function getFiles() { return $this->files; } /** * Returns the lines of the clone. * * @param string $indent * * @return string The lines of the clone */ public function getLines($indent = '') { if (empty($this->lines)) { $file = \current($this->files); $this->lines = \implode( '', \array_map( function ($line) use ($indent) { return $indent . $line; }, \array_slice( \file($file->getName()), $file->getStartLine() - 1, $this->size ) ) ); } return $this->lines; } /** * @return string */ public function getId() { return $this->id; } /** * @return int */ public function getSize() { return $this->size; } /** * @return int */ public function getTokens() { return $this->tokens; } } phpcpd-3.0.1/src/CodeCloneFile.php000066400000000000000000000020751320325043000167200ustar00rootroot00000000000000 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\PHPCPD; class CodeCloneFile { /** * @var string */ private $id; /** * @var string */ private $name; /** * @var int */ private $startLine; /** * @param string $name * @param int $startLine */ public function __construct($name, $startLine) { $this->name = $name; $this->startLine = $startLine; $this->id = $this->name . ':' . $this->startLine; } /** * @return string */ public function getId() { return $this->id; } /** * @return string */ public function getName() { return $this->name; } /** * @return int */ public function getStartLine() { return $this->startLine; } } phpcpd-3.0.1/src/CodeCloneMap.php000066400000000000000000000075241320325043000165620ustar00rootroot00000000000000 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\PHPCPD; class CodeCloneMap implements \Countable, \Iterator { /** * @var CodeClone[] The clones in the clone map */ private $clones = []; /** * @var CodeClone[] The clones in the clone map, stored by ID */ private $clonesById = []; /** * @var int Current position while iterating the clone map */ private $position = 0; /** * @var int Number of duplicate lines in the clone map */ private $numberOfDuplicatedLines = 0; /** * @var int Number of lines analyzed */ private $numLines = 0; /** * @var array */ private $filesWithClones = []; /** * Adds a clone to the map. * * @param CodeClone $clone */ public function addClone(CodeClone $clone) { $id = $clone->getId(); if (!isset($this->clonesById[$id])) { $this->clones[] = $clone; $this->clonesById[$id] = $clone; } else { $existClone = $this->clonesById[$id]; foreach ($clone->getFiles() as $file) { $existClone->addFile($file); } } $this->numberOfDuplicatedLines += $clone->getSize() * (\count($clone->getFiles()) - 1); foreach ($clone->getFiles() as $file) { if (!isset($this->filesWithClones[$file->getName()])) { $this->filesWithClones[$file->getName()] = true; } } } /** * Returns the clones stored in this map. * * @return CodeClone[] */ public function getClones() { return $this->clones; } /** * Returns the percentage of duplicated code lines in the project. * * @return string */ public function getPercentage() { if ($this->numLines > 0) { $percent = ($this->numberOfDuplicatedLines / $this->numLines) * 100; } else { $percent = 100; } return \sprintf('%01.2F%%', $percent); } /** * Returns the number of lines analyzed. * * @return int */ public function getNumLines() { return $this->numLines; } /** * Sets the number of physical source code lines in the project. * * @param int $numLines */ public function setNumLines($numLines) { $this->numLines = $numLines; } /** * Returns the number of clones stored in this map. */ public function count() { return \count($this->clones); } /** * @return int */ public function getNumberOfFilesWithClones() { return \count($this->filesWithClones); } /** * @return int */ public function getNumberOfDuplicatedLines() { return $this->numberOfDuplicatedLines; } /** * Rewinds the Iterator to the first element. */ public function rewind() { $this->position = 0; } /** * Checks if there is a current element after calls to rewind() or next(). * * @return bool */ public function valid() { return $this->position < \count($this->clones); } /** * Returns the key of the current element. * * @return int */ public function key() { return $this->position; } /** * Returns the current element. * * @return CodeClone */ public function current() { return $this->clones[$this->position]; } /** * Moves forward to next element. */ public function next() { $this->position++; } } phpcpd-3.0.1/src/Detector/000077500000000000000000000000001320325043000153215ustar00rootroot00000000000000phpcpd-3.0.1/src/Detector/Detector.php000066400000000000000000000035411320325043000176060ustar00rootroot00000000000000 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\PHPCPD\Detector; use SebastianBergmann\PHPCPD\Detector\Strategy\AbstractStrategy; use SebastianBergmann\PHPCPD\CodeCloneMap; use Symfony\Component\Console\Helper\ProgressBar; class Detector { /** * @var \SebastianBergmann\PHPCPD\Detector\Strategy\AbstractStrategy */ protected $strategy; /** * @var \Symfony\Component\Console\Helper\ProgressBar */ protected $progressBar; /** * @param AbstractStrategy $strategy * @param ProgressBar|null $progressBar */ public function __construct(AbstractStrategy $strategy, ProgressBar $progressBar = null) { $this->strategy = $strategy; $this->progressBar = $progressBar; } /** * Copy & Paste Detection (CPD). * * @param \Iterator|array $files List of files to process * @param int $minLines Minimum number of identical lines * @param int $minTokens Minimum number of identical tokens * @param bool $fuzzy * * @return CodeCloneMap Map of exact clones found in the list of files */ public function copyPasteDetection($files, $minLines = 5, $minTokens = 70, $fuzzy = false) { $result = new CodeCloneMap; foreach ($files as $file) { $this->strategy->processFile( $file, $minLines, $minTokens, $result, $fuzzy ); if ($this->progressBar !== null) { $this->progressBar->advance(); } } return $result; } } phpcpd-3.0.1/src/Detector/Strategy/000077500000000000000000000000001320325043000171235ustar00rootroot00000000000000phpcpd-3.0.1/src/Detector/Strategy/Abstract.php000066400000000000000000000023141320325043000213770ustar00rootroot00000000000000 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\PHPCPD\Detector\Strategy; use SebastianBergmann\PHPCPD\CodeCloneMap; abstract class AbstractStrategy { /** * @var int[] List of tokens to ignore */ protected $tokensIgnoreList = [ T_INLINE_HTML => true, T_COMMENT => true, T_DOC_COMMENT => true, T_OPEN_TAG => true, T_OPEN_TAG_WITH_ECHO => true, T_CLOSE_TAG => true, T_WHITESPACE => true, T_USE => true, T_NS_SEPARATOR => true ]; /** * @var string[] */ protected $hashes = []; /** * Copy & Paste Detection (CPD). * * @param string $file * @param int $minLines * @param int $minTokens * @param CodeCloneMap $result * @param bool $fuzzy */ abstract public function processFile($file, $minLines, $minTokens, CodeCloneMap $result, $fuzzy = false); } phpcpd-3.0.1/src/Detector/Strategy/Default.php000066400000000000000000000126011320325043000212200ustar00rootroot00000000000000 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\PHPCPD\Detector\Strategy; use SebastianBergmann\PHPCPD\CodeClone; use SebastianBergmann\PHPCPD\CodeCloneFile; use SebastianBergmann\PHPCPD\CodeCloneMap; class DefaultStrategy extends AbstractStrategy { /** * Copy & Paste Detection (CPD). * * @param string $file * @param int $minLines * @param int $minTokens * @param CodeCloneMap $result * @param bool $fuzzy */ public function processFile($file, $minLines, $minTokens, CodeCloneMap $result, $fuzzy = false) { $buffer = \file_get_contents($file); $currentTokenPositions = []; $currentTokenRealPositions = []; $currentSignature = ''; $tokens = \token_get_all($buffer); $tokenNr = 0; $lastTokenLine = 0; $result->setNumLines( $result->getNumLines() + \substr_count($buffer, "\n") ); unset($buffer); foreach (\array_keys($tokens) as $key) { $token = $tokens[$key]; if (\is_array($token)) { if (!isset($this->tokensIgnoreList[$token[0]])) { if ($tokenNr == 0) { $currentTokenPositions[$tokenNr] = $token[2] - $lastTokenLine; } else { $currentTokenPositions[$tokenNr] = $currentTokenPositions[$tokenNr - 1] + $token[2] - $lastTokenLine; } $currentTokenRealPositions[$tokenNr++] = $token[2]; if ($fuzzy && $token[0] == T_VARIABLE) { $token[1] = 'variable'; } $currentSignature .= \chr($token[0] & 255) . \pack('N*', \crc32($token[1])); } $lastTokenLine = $token[2]; } } $count = \count($currentTokenPositions); $firstLine = 0; $firstRealLine = 0; $found = false; $tokenNr = 0; while ($tokenNr <= $count - $minTokens) { $line = $currentTokenPositions[$tokenNr]; $realLine = $currentTokenRealPositions[$tokenNr]; $hash = \substr( \md5( \substr( $currentSignature, $tokenNr * 5, $minTokens * 5 ), true ), 0, 8 ); if (isset($this->hashes[$hash])) { $found = true; if ($firstLine === 0) { $firstLine = $line; $firstRealLine = $realLine; $firstHash = $hash; $firstToken = $tokenNr; } } else { if ($found) { $fileA = $this->hashes[$firstHash][0]; $firstLineA = $this->hashes[$firstHash][1]; $lastToken = ($tokenNr - 1) + $minTokens - 1; $lastLine = $currentTokenPositions[$lastToken]; $lastRealLine = $currentTokenRealPositions[$lastToken]; $numLines = $lastLine + 1 - $firstLine; $realNumLines = $lastRealLine + 1 - $firstRealLine; if ($numLines >= $minLines && ($fileA != $file || $firstLineA != $firstRealLine)) { $result->addClone( new CodeClone( new CodeCloneFile($fileA, $firstLineA), new CodeCloneFile($file, $firstRealLine), $realNumLines, $lastToken + 1 - $firstToken ) ); } $found = false; $firstLine = 0; } $this->hashes[$hash] = [$file, $realLine]; } $tokenNr++; } if ($found) { $fileA = $this->hashes[$firstHash][0]; $firstLineA = $this->hashes[$firstHash][1]; $lastToken = ($tokenNr - 1) + $minTokens - 1; $lastLine = $currentTokenPositions[$lastToken]; $lastRealLine = $currentTokenRealPositions[$lastToken]; $numLines = $lastLine + 1 - $firstLine; $realNumLines = $lastRealLine + 1 - $firstRealLine; if ($numLines >= $minLines && ($fileA != $file || $firstLineA != $firstRealLine)) { $result->addClone( new CodeClone( new CodeCloneFile($fileA, $firstLineA), new CodeCloneFile($file, $firstRealLine), $realNumLines, $lastToken + 1 - $firstToken ) ); } } } } phpcpd-3.0.1/src/Log/000077500000000000000000000000001320325043000142715ustar00rootroot00000000000000phpcpd-3.0.1/src/Log/AbstractXmlLogger.php000066400000000000000000000060051320325043000203670ustar00rootroot00000000000000 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\PHPCPD\Log; use SebastianBergmann\PHPCPD\CodeCloneMap; abstract class AbstractXmlLogger { protected $document; /** * Constructor. * * @param string $filename */ public function __construct($filename) { $this->document = new \DOMDocument('1.0', 'UTF-8'); $this->document->formatOutput = true; $this->filename = $filename; } /** * Writes the XML document to the file. */ protected function flush() { \file_put_contents($this->filename, $this->document->saveXML()); } /** * Converts a string to UTF-8 encoding. * * @param string $string * * @return string */ protected function convertToUtf8($string) { if (!$this->isUtf8($string)) { if (\function_exists('mb_convert_encoding')) { $string = \mb_convert_encoding($string, 'UTF-8'); } else { $string = \utf8_encode($string); } } return $string; } /** * Checks a string for UTF-8 encoding. * * @param string $string * * @return bool */ protected function isUtf8($string) { $length = \strlen($string); for ($i = 0; $i < $length; $i++) { if (\ord($string[$i]) < 0x80) { $n = 0; } elseif ((\ord($string[$i]) & 0xE0) == 0xC0) { $n = 1; } elseif ((\ord($string[$i]) & 0xF0) == 0xE0) { $n = 2; } elseif ((\ord($string[$i]) & 0xF0) == 0xF0) { $n = 3; } else { return false; } for ($j = 0; $j < $n; $j++) { if ((++$i == $length) || ((\ord($string[$i]) & 0xC0) != 0x80)) { return false; } } } return true; } /** * Escapes a string for inclusion inside an XML tag. * * Converts the string to UTF-8, substitutes the unicode replacement * character for every character disallowed in XML, and escapes * special characters. * * @param string $string * * @return string */ protected function escapeForXml($string) { $string = $this->convertToUtf8($string); // Substitute the unicode replacement character for disallowed chars $string = \preg_replace( '/[^\x09\x0A\x0D\x{0020}-\x{D7FF}\x{E000}-\x{FFFD}]/u', "\xEF\xBF\xBD", $string ); return \htmlspecialchars($string, ENT_COMPAT, 'UTF-8'); } /** * Processes a list of clones. * * @param CodeCloneMap $clones */ abstract public function processClones(CodeCloneMap $clones); } phpcpd-3.0.1/src/Log/PMD.php000066400000000000000000000030171320325043000154230ustar00rootroot00000000000000 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\PHPCPD\Log; use SebastianBergmann\PHPCPD\CodeCloneMap; class PMD extends AbstractXmlLogger { /** * Processes a list of clones. * * @param CodeCloneMap $clones */ public function processClones(CodeCloneMap $clones) { $cpd = $this->document->createElement('pmd-cpd'); $this->document->appendChild($cpd); foreach ($clones as $clone) { $duplication = $cpd->appendChild( $this->document->createElement('duplication') ); $duplication->setAttribute('lines', $clone->getSize()); $duplication->setAttribute('tokens', $clone->getTokens()); foreach ($clone->getFiles() as $codeCloneFile) { $file = $duplication->appendChild( $this->document->createElement('file') ); $file->setAttribute('path', $codeCloneFile->getName()); $file->setAttribute('line', $codeCloneFile->getStartLine()); } $duplication->appendChild( $this->document->createElement( 'codefragment', $this->escapeForXml($clone->getLines()) ) ); } $this->flush(); } } phpcpd-3.0.1/src/Log/Text.php000066400000000000000000000040031320325043000157230ustar00rootroot00000000000000 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\PHPCPD\Log; use SebastianBergmann\PHPCPD\CodeCloneMap; use Symfony\Component\Console\Output\OutputInterface; class Text { /** * Prints a result set from Detector::copyPasteDetection(). * * @param OutputInterface $output * @param CodeCloneMap $clones */ public function printResult(OutputInterface $output, CodeCloneMap $clones) { $verbose = $output->getVerbosity() > OutputInterface::VERBOSITY_NORMAL; if (\count($clones) > 0) { $output->write( \sprintf( 'Found %d clones with %d duplicated lines in %d files:' . PHP_EOL . PHP_EOL, \count($clones), $clones->getNumberOfDuplicatedLines(), $clones->getNumberOfFilesWithClones() ) ); } foreach ($clones as $clone) { $firstOccurrence = true; foreach ($clone->getFiles() as $file) { $output->writeln( \sprintf( ' %s%s:%d-%d', $firstOccurrence ? '- ' : ' ', $file->getName(), $file->getStartLine(), $file->getStartLine() + $clone->getSize() ) ); $firstOccurrence = false; } if ($verbose) { $output->write(PHP_EOL . $clone->getLines(' ')); } $output->writeln(''); } $output->write( \sprintf( "%s duplicated lines out of %d total lines of code.\n\n", $clones->getPercentage(), $clones->getNumLines() ) ); } } phpcpd-3.0.1/tests/000077500000000000000000000000001320325043000141235ustar00rootroot00000000000000phpcpd-3.0.1/tests/DetectorTest.php000066400000000000000000000170621320325043000172530ustar00rootroot00000000000000 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ if (!defined('TEST_FILES_PATH')) { define( 'TEST_FILES_PATH', dirname(__FILE__) . DIRECTORY_SEPARATOR . '_files' . DIRECTORY_SEPARATOR ); } use PHPUnit\Framework\TestCase; class PHPCPD_DetectorTest extends TestCase { /** * @covers SebastianBergmann\PHPCPD\Detector\Detector::copyPasteDetection * @covers SebastianBergmann\PHPCPD\CodeClone::getLines * @dataProvider strategyProvider */ public function testDetectingSimpleClonesWorks($strategy) { $detector = new SebastianBergmann\PHPCPD\Detector\Detector(new $strategy); $clones = $detector->copyPasteDetection( [TEST_FILES_PATH . 'Math.php'] ); $clones = $clones->getClones(); $files = $clones[0]->getFiles(); $file = current($files); $this->assertEquals(TEST_FILES_PATH . 'Math.php', $file->getName()); $this->assertEquals(75, $file->getStartLine()); $file = next($files); $this->assertEquals(TEST_FILES_PATH . 'Math.php', $file->getName()); $this->assertEquals(139, $file->getStartLine()); $this->assertEquals(59, $clones[0]->getSize()); $this->assertEquals(136, $clones[0]->getTokens()); $this->assertEquals( ' public function div($v1, $v2) { $v3 = $v1 / ($v2 + $v1); if ($v3 > 14) { $v4 = 0; for ($i = 0; $i < $v3; $i++) { $v4 += ($v2 * $i); } } $v5 = ($v4 < $v3 ? ($v3 - $v4) : ($v4 - $v3)); $v6 = ($v1 * $v2 * $v3 * $v4 * $v5); $d = array($v1, $v2, $v3, $v4, $v5, $v6); $v7 = 1; for ($i = 0; $i < $v6; $i++) { shuffle( $d ); $v7 = $v7 + $i * end($d); } $v8 = $v7; foreach ( $d as $x ) { $v8 *= $x; } $v3 = $v1 / ($v2 + $v1); if ($v3 > 14) { $v4 = 0; for ($i = 0; $i < $v3; $i++) { $v4 += ($v2 * $i); } } $v5 = ($v4 < $v3 ? ($v3 - $v4) : ($v4 - $v3)); $v6 = ($v1 * $v2 * $v3 * $v4 * $v5); $d = array($v1, $v2, $v3, $v4, $v5, $v6); $v7 = 1; for ($i = 0; $i < $v6; $i++) { shuffle( $d ); $v7 = $v7 + $i * end($d); } $v8 = $v7; foreach ( $d as $x ) { $v8 *= $x; } return $v8; ', $clones[0]->getLines() ); } /** * @covers SebastianBergmann\PHPCPD\Detector\Detector::copyPasteDetection * @dataProvider strategyProvider */ public function testDetectingExactDuplicateFilesWorks($strategy) { $detector = new SebastianBergmann\PHPCPD\Detector\Detector(new $strategy); $clones = $detector->copyPasteDetection([ TEST_FILES_PATH . 'a.php', TEST_FILES_PATH . 'b.php' ], 20, 60); $clones = $clones->getClones(); $files = $clones[0]->getFiles(); $file = current($files); $this->assertCount(1, $clones); $this->assertEquals(TEST_FILES_PATH . 'a.php', $file->getName()); $this->assertEquals(4, $file->getStartLine()); $file = next($files); $this->assertEquals(TEST_FILES_PATH . 'b.php', $file->getName()); $this->assertEquals(4, $file->getStartLine()); $this->assertEquals(20, $clones[0]->getSize()); $this->assertEquals(60, $clones[0]->getTokens()); } /** * @covers SebastianBergmann\PHPCPD\Detector\Detector::copyPasteDetection * @dataProvider strategyProvider */ public function testDetectingClonesInMoreThanTwoFiles($strategy) { $detector = new SebastianBergmann\PHPCPD\Detector\Detector(new $strategy); $clones = $detector->copyPasteDetection( [ TEST_FILES_PATH . 'a.php', TEST_FILES_PATH . 'b.php', TEST_FILES_PATH . 'c.php', ], 20, 60 ); $clones = $clones->getClones(); $files = $clones[0]->getFiles(); sort($files); $file = current($files); $this->assertCount(1, $clones); $this->assertEquals(TEST_FILES_PATH . 'a.php', $file->getName()); $this->assertEquals(4, $file->getStartLine()); $file = next($files); $this->assertEquals(TEST_FILES_PATH . 'b.php', $file->getName()); $this->assertEquals(4, $file->getStartLine()); $file = next($files); $this->assertEquals(TEST_FILES_PATH . 'c.php', $file->getName()); $this->assertEquals(4, $file->getStartLine()); } /** * @covers SebastianBergmann\PHPCPD\Detector\Detector::copyPasteDetection * @dataProvider strategyProvider */ public function testClonesAreIgnoredIfTheySpanLessTokensThanMinTokens($strategy) { $detector = new SebastianBergmann\PHPCPD\Detector\Detector(new $strategy); $clones = $detector->copyPasteDetection( [ TEST_FILES_PATH . 'a.php', TEST_FILES_PATH . 'b.php' ], 20, 61 ); $this->assertCount(0, $clones->getClones()); } /** * @covers SebastianBergmann\PHPCPD\Detector\Detector::copyPasteDetection * @dataProvider strategyProvider */ public function testClonesAreIgnoredIfTheySpanLessLinesThanMinLines($strategy) { $detector = new SebastianBergmann\PHPCPD\Detector\Detector(new $strategy); $clones = $detector->copyPasteDetection( [ TEST_FILES_PATH . 'a.php', TEST_FILES_PATH . 'b.php' ], 21, 60 ); $this->assertCount(0, $clones->getClones()); } /** * @covers SebastianBergmann\PHPCPD\Detector\Detector::copyPasteDetection * @dataProvider strategyProvider */ public function testFuzzyClonesAreFound($strategy) { $detector = new SebastianBergmann\PHPCPD\Detector\Detector(new $strategy); $clones = $detector->copyPasteDetection( [ TEST_FILES_PATH . 'a.php', TEST_FILES_PATH . 'd.php' ], 5, 20, true ); $clones = $clones->getClones(); $this->assertCount(1, $clones); } /** * @covers SebastianBergmann\PHPCPD\Detector\Detector::copyPasteDetection * @dataProvider strategyProvider */ public function testStripComments($strategy) { $detector = new SebastianBergmann\PHPCPD\Detector\Detector(new $strategy); $clones = $detector->copyPasteDetection( [ TEST_FILES_PATH . 'e.php', TEST_FILES_PATH . 'f.php' ], 8, 10, true ); $clones = $clones->getClones(); $this->assertCount(0, $clones); $clones = $detector->copyPasteDetection( [ TEST_FILES_PATH . 'e.php', TEST_FILES_PATH . 'f.php' ], 7, 10, true ); $clones = $clones->getClones(); $this->assertCount(1, $clones); } public function strategyProvider() { return [ ['SebastianBergmann\\PHPCPD\\Detector\\Strategy\\DefaultStrategy'] ]; } } phpcpd-3.0.1/tests/Log/000077500000000000000000000000001320325043000146445ustar00rootroot00000000000000phpcpd-3.0.1/tests/Log/PMDTest.php000066400000000000000000000041771320325043000166460ustar00rootroot00000000000000testFile1 = __DIR__ . '/_files/with_ascii_escape.php'; $this->testFile2 = __DIR__ . '/_files/with_ascii_escape2.php'; $this->pmdLogFile = tempnam(sys_get_temp_dir(), 'pmd'); $this->expectedPmdLogFile = tempnam(sys_get_temp_dir(), 'pmd'); $expectedPmdLogTemplate = __DIR__ . '/_files/pmd_expected.xml'; $expectedPmdLogContents = strtr( file_get_contents($expectedPmdLogTemplate), array( '%file1%' => $this->testFile1, '%file2%' => $this->testFile2 ) ); file_put_contents($this->expectedPmdLogFile, $expectedPmdLogContents); $this->pmdLogger = new PMD($this->pmdLogFile); } protected function tearDown() { if (file_exists($this->pmdLogFile)) { unlink($this->pmdLogFile); } if (file_exists($this->expectedPmdLogFile)) { unlink($this->expectedPmdLogFile); } } /** * @covers SebastianBergmann\PHPCPD\Log\PMD * @covers SebastianBergmann\PHPCPD\Log\AbstractXmlLogger */ public function testSubstitutesDisallowedCharacters() { $file1 = new CodeCloneFile($this->testFile1, 8); $file2 = new CodeCloneFile($this->testFile2, 8); $clone = new CodeClone($file1, $file2, 4, 4); $cloneMap = new CodeCloneMap(); $cloneMap->addClone($clone); $this->pmdLogger->processClones($cloneMap); $this->assertXmlFileEqualsXmlFile( $this->expectedPmdLogFile, $this->pmdLogFile ); } } phpcpd-3.0.1/tests/Log/_files/000077500000000000000000000000001320325043000161055ustar00rootroot00000000000000phpcpd-3.0.1/tests/Log/_files/pmd_expected.xml000066400000000000000000000004401320325043000212660ustar00rootroot00000000000000 function getAsciiEscapeChar() { return "�"; } phpcpd-3.0.1/tests/Log/_files/with_ascii_escape.php000066400000000000000000000002121320325043000222540ustar00rootroot00000000000000. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * * Neither the name of Manuel Pichler nor the names of his * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * @version SVN: $Id$ */ /** * Simple math class. * */ class PhpUnderControl_Example_Math { /** * Adds the two given values. * * @param integer $v1 Value one. * @param integer $v2 Value two. * * @return integer. */ public function add($v1, $v2) { return ($v1 + $v2); } /** * Subtract param two from param one * * @param integer $v1 Value one. * @param integer $v2 Value two. * * @return integer. */ public function sub($v1, $v2) { return ($v1 - $v2); } /** * Not tested method that should be visible with low coverage. */ public function div($v1, $v2) { $v3 = $v1 / ($v2 + $v1); if ($v3 > 14) { $v4 = 0; for ($i = 0; $i < $v3; $i++) { $v4 += ($v2 * $i); } } $v5 = ($v4 < $v3 ? ($v3 - $v4) : ($v4 - $v3)); $v6 = ($v1 * $v2 * $v3 * $v4 * $v5); $d = array($v1, $v2, $v3, $v4, $v5, $v6); $v7 = 1; for ($i = 0; $i < $v6; $i++) { shuffle( $d ); $v7 = $v7 + $i * end($d); } $v8 = $v7; foreach ( $d as $x ) { $v8 *= $x; } $v3 = $v1 / ($v2 + $v1); if ($v3 > 14) { $v4 = 0; for ($i = 0; $i < $v3; $i++) { $v4 += ($v2 * $i); } } $v5 = ($v4 < $v3 ? ($v3 - $v4) : ($v4 - $v3)); $v6 = ($v1 * $v2 * $v3 * $v4 * $v5); $d = array($v1, $v2, $v3, $v4, $v5, $v6); $v7 = 1; for ($i = 0; $i < $v6; $i++) { shuffle( $d ); $v7 = $v7 + $i * end($d); } $v8 = $v7; foreach ( $d as $x ) { $v8 *= $x; } return $v8; } /** * Simple copy for cpd detection. */ public function complex($v1, $v2) { $v3 = $v1 / ($v2 + $v1); if ($v3 > 14) { $v4 = 0; for ($i = 0; $i < $v3; $i++) { $v4 += ($v2 * $i); } } $v5 = ($v4 < $v3 ? ($v3 - $v4) : ($v4 - $v3)); $v6 = ($v1 * $v2 * $v3 * $v4 * $v5); $d = array($v1, $v2, $v3, $v4, $v5, $v6); $v7 = 1; for ($i = 0; $i < $v6; $i++) { shuffle( $d ); $v7 = $v7 + $i * end( $d ); } $v8 = $v7; foreach ( $d as $x ) { $v8 *= $x; } $v3 = $v1 / ($v2 + $v1); if ($v3 > 14) { $v4 = 0; for ($i = 0; $i < $v3; $i++) { $v4 += ($v2 * $i); } } $v5 = ($v4 < $v3 ? ($v3 - $v4) : ($v4 - $v3)); $v6 = ($v1 * $v2 * $v3 * $v4 * $v5); $d = array($v1, $v2, $v3, $v4, $v5, $v6); $v7 = 1; for ($i = 0; $i < $v6; $i++) { shuffle( $d ); $v7 = $v7 + $i * end($d); } $v8 = $v7; foreach ( $d as $x ) { $v8 *= $x; } return $v8; } } phpcpd-3.0.1/tests/_files/a.php000066400000000000000000000006011320325043000163120ustar00rootroot00000000000000