pax_global_header00006660000000000000000000000064125050160120014502gustar00rootroot0000000000000052 comment=d3ad100fdf15805495f6ff19f473f4314c99390c phpcpd-2.0.2/000077500000000000000000000000001250501601200127615ustar00rootroot00000000000000phpcpd-2.0.2/.gitattributes000066400000000000000000000000171250501601200156520ustar00rootroot00000000000000*.php diff=php phpcpd-2.0.2/.gitignore000066400000000000000000000002311250501601200147450ustar00rootroot00000000000000build/phar build/SebastianBergmann build/phpcpd.bat build/phpcpd.php build/*.phar* build/*.tgz .idea cache.properties composer.phar composer.lock vendor phpcpd-2.0.2/.travis.yml000066400000000000000000000004351250501601200150740ustar00rootroot00000000000000language: php install: - travis_retry composer install --no-interaction --prefer-source php: - 5.3.3 - 5.3 - 5.4 - 5.5 - 5.6 - 7.0 - hhvm script: phpunit --configuration ./build/travis-ci.xml matrix: allow_failures: - php: hhvm notifications: email: false phpcpd-2.0.2/LICENSE000066400000000000000000000030051250501601200137640ustar00rootroot00000000000000phpcpd Copyright (c) 2009-2015, 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-2.0.2/README.md000066400000000000000000000036451250501601200142500ustar00rootroot00000000000000[![Latest Stable Version](https://poser.pugx.org/sebastian/phpcpd/v/stable.png)](https://packagist.org/packages/sebastian/phpcpd) [![Build Status](https://travis-ci.org/sebastianbergmann/phpcpd.png?branch=master)](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 Simply add a dependency on `sebastian/phpcpd` to your project's `composer.json` file if you use [Composer](http://getcomposer.org/) to manage the dependencies of your project. Here is a minimal example of a `composer.json` file that just defines a development-time dependency on PHPCPD: { "require-dev": { "sebastian/phpcpd": "*" } } For a system-wide installation via Composer, you can run: composer global require "sebastian/phpcpd=*" Make sure you have `~/.composer/vendor/bin/` in your path. ## Usage Example ➜ ~ phpcpd /tmp/wordpress-3.8.1/wp-includes phpcpd 2.0.1 by Sebastian Bergmann. Found 34 exact clones with 1273 duplicated lines in 11 files: - /tmp/wordpress-3.8.1/wp-includes/class-snoopy.php:165-195 /tmp/wordpress-3.8.1/wp-includes/class-snoopy.php:225-255 . . . - /tmp/wordpress-3.8.1/wp-includes/SimplePie/Misc.php:1769-1830 /tmp/wordpress-3.8.1/wp-includes/SimplePie/Parse/Date.php:710-771 0.86% duplicated lines out of 147877 total lines of code. Time: 24.67 seconds, Memory: 159.00Mb phpcpd-2.0.2/build.xml000066400000000000000000000106471250501601200146120ustar00rootroot00000000000000 phpcpd-2.0.2/build/000077500000000000000000000000001250501601200140605ustar00rootroot00000000000000phpcpd-2.0.2/build/phar-autoload.php.in000066400000000000000000000016401250501601200177370ustar00rootroot00000000000000#!/usr/bin/env php run(); __HALT_COMPILER(); phpcpd-2.0.2/build/phar-manifest.php000077500000000000000000000013421250501601200173320ustar00rootroot00000000000000#!/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-2.0.2/build/phpunit.xml000066400000000000000000000010521250501601200162670ustar00rootroot00000000000000 ../tests ../src ../src/autoload.php phpcpd-2.0.2/build/travis-ci.xml000066400000000000000000000013021250501601200164770ustar00rootroot00000000000000 ../tests ../src ../src/autoload.php phpcpd-2.0.2/composer.json000066400000000000000000000015741250501601200155120ustar00rootroot00000000000000{ "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" }, "require": { "php": ">=5.3.3", "sebastian/finder-facade": "~1.1", "sebastian/version": "~1.0", "symfony/console": "~2.2", "phpunit/php-timer": "~1.0", "theseer/fdomdocument": "~1.4" }, "autoload": { "classmap": [ "src/" ] }, "bin": [ "phpcpd" ], "extra": { "branch-alias": { "dev-master": "2.0-dev" } } } phpcpd-2.0.2/composer/000077500000000000000000000000001250501601200146105ustar00rootroot00000000000000phpcpd-2.0.2/composer/bin/000077500000000000000000000000001250501601200153605ustar00rootroot00000000000000phpcpd-2.0.2/composer/bin/phpcpd000077500000000000000000000015131250501601200165640ustar00rootroot00000000000000#!/usr/bin/env php * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ $files = array( __DIR__ . '/../../vendor/autoload.php', __DIR__ . '/../../../../autoload.php' ); $found = FALSE; foreach ($files as $file) { if (file_exists($file)) { require $file; $found = TRUE; break; } } if (!$found) { die( 'You need to set up the project dependencies using the following commands:' . PHP_EOL . 'curl -s http://getcomposer.org/installer | php' . PHP_EOL . 'php composer.phar install' . PHP_EOL ); } $application = new SebastianBergmann\PHPCPD\CLI\Application; $application->run(); phpcpd-2.0.2/phpcpd000077500000000000000000000017561250501601200141760ustar00rootroot00000000000000#!/usr/bin/env php * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ // @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-2.0.2/src/000077500000000000000000000000001250501601200135505ustar00rootroot00000000000000phpcpd-2.0.2/src/CLI/000077500000000000000000000000001250501601200141575ustar00rootroot00000000000000phpcpd-2.0.2/src/CLI/Application.php000066400000000000000000000056131250501601200171400ustar00rootroot00000000000000 * * 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\Input\ArgvInput; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Input\ArrayInput; /** * TextUI frontend for PHPCPD. * * @author Sebastian Bergmann * @copyright Sebastian Bergmann * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License * @link http://github.com/sebastianbergmann/phpcpd/tree * @since Class available since Release 2.0.0 */ class Application extends AbstractApplication { public function __construct() { $version = new Version('2.0.2', 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 integer 0 if everything went fine, or an error code */ public function doRun(InputInterface $input, OutputInterface $output) { 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(array('--help')); } parent::doRun($input, $output); } } phpcpd-2.0.2/src/CLI/Command.php000066400000000000000000000131731250501601200162530ustar00rootroot00000000000000 * * 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; /** * @author Sebastian Bergmann * @copyright Sebastian Bergmann * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License * @link http://github.com/sebastianbergmann/phpcpd/tree * @since Class available since Release 2.0.0 */ class Command extends AbstractCommand { /** * Configures the current command. */ protected function configure() { $this->setName('phpcpd') ->setDefinition( array( 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', array('*.php') ) ->addOption( 'names-exclude', null, InputOption::VALUE_REQUIRED, 'A comma-separated list of file names to exclude', array() ) ->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|integer 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') ); $files = $finder->findFiles(); if (empty($files)) { $output->writeln('No files found to scan'); exit(1); } $progressHelper = null; if ($input->getOption('progress')) { $progressHelper = $this->getHelperSet()->get('progress'); $progressHelper->start($output, count($files)); } $strategy = new DefaultStrategy; $detector = new Detector($strategy, $progressHelper); $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')) { $progressHelper->finish(); $output->writeln(''); } 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\InputOption $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-2.0.2/src/CodeClone.php000066400000000000000000000067241250501601200161250ustar00rootroot00000000000000 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\PHPCPD; use SebastianBergmann\PHPCPD\CodeCloneFile; /** * Represents an exact code clone. * * @author Sebastian Bergmann * @copyright Sebastian Bergmann * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License * @link http://github.com/sebastianbergmann/phpcpd/tree * @since Class available since Release 1.1.0 */ class CodeClone { /** * @var integer Size of the clone (lines) */ private $size; /** * @var integer Size of the clone (tokens) */ private $tokens; /** * @var CodeCloneFile[] Files with this code clone */ private $files = array(); /** * @var string Unique ID of Code Duplicate Fragment */ private $id; /** * @var Lines of the clone */ private $lines = ''; /** * Constructor. * * @param CodeCloneFile $fileA * @param CodeCloneFile $fileB * @param integer $size * @param integer $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 $prefix * @return string The lines of the clone */ public function getLines($prefix = '') { $file = current($this->files); if (empty($this->lines)) { $lines = array_slice( file($file->getName()), $file->getStartLine() - 1, $this->size ); $indent = array(); foreach ($lines as &$line) { $line = rtrim($line, " \t\0\x0B"); $line = str_replace("\t", " ", $line); $_indent = strlen($line) - strlen(ltrim($line)); if ($_indent > 1) { $indent[] = $_indent; } } $indent = empty($indent) ? 0 : min($indent); if ($indent > 0) { foreach ($lines as &$line) { if (strlen($line > 1)) { $line = $prefix . substr($line, $indent); } } } $this->lines = join('', $lines); } return $this->lines; } /** * @return string */ public function getId() { return $this->id; } /** * @return integer */ public function getSize() { return $this->size; } /** * @return integer */ public function getTokens() { return $this->tokens; } } phpcpd-2.0.2/src/CodeCloneFile.php000066400000000000000000000061011250501601200167120ustar00rootroot00000000000000. * 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. * * @package phpcpd * @author Sebastian Bergmann * @copyright Sebastian Bergmann * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License * @since File available since Release 1.5.0 */ namespace SebastianBergmann\PHPCPD; /** * Represents an exact code clone file. * * @author Alexander Kazakov * @copyright Sebastian Bergmann * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License * @link http://github.com/sebastianbergmann/phpcpd/tree * @since Class available since Release 1.5.0 */ class CodeCloneFile { /** * @var string */ private $id; /** * @var string */ private $name; /** * @var integer */ private $startLine; /** * @param string $name * @param integer $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 integer */ public function getStartLine() { return $this->startLine; } } phpcpd-2.0.2/src/CodeCloneMap.php000066400000000000000000000072351250501601200165610ustar00rootroot00000000000000 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace SebastianBergmann\PHPCPD; use SebastianBergmann\PHPCPD\CodeClone; /** * A map of exact clones. * * @author Sebastian Bergmann * @copyright Sebastian Bergmann * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License * @link http://github.com/sebastianbergmann/phpcpd/tree * @since Class available since Release 1.1.0 */ class CodeCloneMap implements \Countable, \Iterator { /** * @var CodeClone[] The clones in the clone map */ protected $clones = array(); /** * @var CodeClone[] The clones in the clone map, stored by ID */ protected $clonesById = array(); /** * @var integer Current position while iterating the clone map */ protected $position = 0; /** * @var integer Number of duplicate lines in the clone map */ protected $numDuplicateLines = 0; /** * @var integer Number of lines analyzed */ protected $numLines = 0; /** * 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->numDuplicateLines += $clone->getSize(); } /** * 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->numDuplicateLines / $this->numLines) * 100; } else { $percent = 100; } return sprintf('%01.2F%%', $percent); } /** * Returns the number of lines analyzed. * * @return integer */ public function getNumLines() { return $this->numLines; } /** * Sets the number of physical source code lines in the project. * * @param integer $numLines */ public function setNumLines($numLines) { $this->numLines = $numLines; } /** * Returns the number of clones stored in this map. */ public function count() { return count($this->clones); } /** * 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 boolean */ public function valid() { return $this->position < count($this->clones); } /** * Returns the key of the current element. * * @return integer */ 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-2.0.2/src/Detector/000077500000000000000000000000001250501601200153215ustar00rootroot00000000000000phpcpd-2.0.2/src/Detector/Detector.php000066400000000000000000000044771250501601200176170ustar00rootroot00000000000000 * * 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\ProgressHelper; /** * PHPCPD code analyser. * * @author Johann-Peter Hartmann * @author Sebastian Bergmann * @copyright Sebastian Bergmann * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License * @link http://github.com/sebastianbergmann/phpcpd/tree * @since Class available since Release 1.0.0 */ class Detector { /** * @var SebastianBergmann\PHPCPD\Detector\Strategy\AbstractStrategy */ protected $strategy; /** * @var Symfony\Component\Console\Helper\ProgressHelper */ protected $progressHelper; /** * Constructor. * * @param AbstractStrategy $strategy * @since Method available since Release 1.3.0 */ public function __construct(AbstractStrategy $strategy, ProgressHelper $progressHelper = null) { $this->strategy = $strategy; $this->progressHelper = $progressHelper; } /** * Copy & Paste Detection (CPD). * * @param Iterator|array $files List of files to process * @param integer $minLines Minimum number of identical lines * @param integer $minTokens Minimum number of identical tokens * @param boolean $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->progressHelper !== null) { $this->progressHelper->advance(); } } return $result; } } phpcpd-2.0.2/src/Detector/Strategy/000077500000000000000000000000001250501601200171235ustar00rootroot00000000000000phpcpd-2.0.2/src/Detector/Strategy/Abstract.php000066400000000000000000000030211250501601200213730ustar00rootroot00000000000000 * * 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 base class for strategies to detect code clones. * * @author Sebastian Bergmann * @copyright Sebastian Bergmann * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License * @link http://github.com/sebastianbergmann/phpcpd/tree * @since Class available since Release 1.4.0 */ abstract class AbstractStrategy { /** * @var integer[] List of tokens to ignore */ protected $tokensIgnoreList = array( 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 = array(); /** * Copy & Paste Detection (CPD). * * @param string $file * @param integer $minLines * @param integer $minTokens * @param CodeCloneMap $result * @param boolean $fuzzy */ abstract public function processFile($file, $minLines, $minTokens, CodeCloneMap $result, $fuzzy = false); } phpcpd-2.0.2/src/Detector/Strategy/Default.php000066400000000000000000000136511250501601200212260ustar00rootroot00000000000000 * * 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; /** * Default strategy for detecting code clones. * * @author Johann-Peter Hartmann * @author Sebastian Bergmann * @copyright Sebastian Bergmann * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License * @link http://github.com/sebastianbergmann/phpcpd/tree * @since Class available since Release 1.4.0 */ class DefaultStrategy extends AbstractStrategy { /** * Copy & Paste Detection (CPD). * * @param string $file * @param integer $minLines * @param integer $minTokens * @param CodeCloneMap $result * @param boolean $fuzzy * @author Johann-Peter Hartmann */ public function processFile($file, $minLines, $minTokens, CodeCloneMap $result, $fuzzy = false) { $buffer = file_get_contents($file); $currentTokenPositions = array(); $currentTokenRealPositions = array(); $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] = array($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 ) ); } $found = false; } } } phpcpd-2.0.2/src/Log/000077500000000000000000000000001250501601200142715ustar00rootroot00000000000000phpcpd-2.0.2/src/Log/AbstractXmlLogger.php000066400000000000000000000051631250501601200203730ustar00rootroot00000000000000 * * 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; /** * Base class for XML loggers. * * @author Sebastian Bergmann * @copyright Sebastian Bergmann * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License * @link http://github.com/sebastianbergmann/phpcpd/tree * @since Class available since Release 1.0.0 */ 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 boolean */ 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; } /** * Processes a list of clones. * * @param CodeCloneMap $clones */ abstract public function processClones(CodeCloneMap $clones); } phpcpd-2.0.2/src/Log/PMD.php000066400000000000000000000040371250501601200154260ustar00rootroot00000000000000 * * 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; /** * Implementation of AbstractXmlLogger that writes in PMD-CPD format. * * @author Sebastian Bergmann * @copyright Sebastian Bergmann * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License * @link http://github.com/sebastianbergmann/phpcpd/tree * @since Class available since Release 1.0.0 */ 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', htmlspecialchars( $this->convertToUtf8($clone->getLines()), ENT_COMPAT, 'UTF-8' ) ) ); } $this->flush(); } } phpcpd-2.0.2/src/Log/Text.php000066400000000000000000000052721250501601200157340ustar00rootroot00000000000000 * * 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 SebastianBergmann\PHPCPD\CodeClone; use Symfony\Component\Console\Output\OutputInterface; /** * A ResultPrinter for the TextUI. * * @author Sebastian Bergmann * @copyright Sebastian Bergmann * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License * @link http://github.com/sebastianbergmann/phpcpd/tree * @since Class available since Release 2.0.0 */ class Text { /** * Prints a result set from Detector::copyPasteDetection(). * * @param OutputInterface $output * @param CodeCloneMap $clones */ public function printResult(OutputInterface $output, CodeCloneMap $clones) { $numClones = count($clones); $verbose = $output->getVerbosity() > OutputInterface::VERBOSITY_NORMAL; if ($numClones > 0) { $buffer = ''; $files = array(); $lines = 0; foreach ($clones as $clone) { foreach ($clone->getFiles() as $file) { $filename = $file->getName(); if (!isset($files[$filename])) { $files[$filename] = true; } } $lines += $clone->getSize() * (count($clone->getFiles()) - 1); $buffer .= "\n -"; foreach ($clone->getFiles() as $file) { $buffer .= sprintf( "\t%s:%d-%d\n ", $file->getName(), $file->getStartLine(), $file->getStartLine() + $clone->getSize() ); } if ($verbose) { $buffer .= "\n" . $clone->getLines(' '); } } $output->write( sprintf( "Found %d exact clones with %d duplicated lines in %d files:\n%s", $numClones, $lines, count($files), $buffer ) ); } $output->write( sprintf( "%s%s duplicated lines out of %d total lines of code.\n\n", $numClones > 0 ? "\n" : '', $clones->getPercentage(), $clones->getNumLines() ) ); } } phpcpd-2.0.2/tests/000077500000000000000000000000001250501601200141235ustar00rootroot00000000000000phpcpd-2.0.2/tests/DetectorTest.php000066400000000000000000000176501250501601200172560ustar00rootroot00000000000000 * * 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 ); } /** * Tests for the PHPCPD code analyser. * * @author Sebastian Bergmann * @copyright Sebastian Bergmann * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License * @link http://github.com/sebastianbergmann/phpcpd/tree * @since Class available since Release 1.0.0 */ class PHPCPD_DetectorTest extends PHPUnit_Framework_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( array(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(85, $file->getStartLine()); $file = next($files); $this->assertEquals(TEST_FILES_PATH . 'Math.php', $file->getName()); $this->assertEquals(149, $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(array( 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( array( TEST_FILES_PATH . 'a.php', TEST_FILES_PATH . 'b.php', TEST_FILES_PATH . 'c.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()); $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( array( 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( array( 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( array( 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( array( TEST_FILES_PATH . 'e.php', TEST_FILES_PATH . 'f.php' ), 8, 10, TRUE ); $clones = $clones->getClones(); $this->assertCount(0, $clones); $clones = $detector->copyPasteDetection( array( TEST_FILES_PATH . 'e.php', TEST_FILES_PATH . 'f.php' ), 7, 10, TRUE ); $clones = $clones->getClones(); $this->assertCount(1, $clones); } public function strategyProvider() { return array( array('SebastianBergmann\\PHPCPD\\Detector\\Strategy\\DefaultStrategy') ); } } phpcpd-2.0.2/tests/_files/000077500000000000000000000000001250501601200153645ustar00rootroot00000000000000phpcpd-2.0.2/tests/_files/Math.php000066400000000000000000000124631250501601200167740ustar00rootroot00000000000000. * 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. * * @package Example * @author Manuel Pichler * @copyright 2007-2011 Manuel Pichler. All rights reserved. * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License * @version SVN: $Id$ * @link http://www.phpundercontrol.org/ */ /** * Simple math class. * * @package Example * @author Manuel Pichler * @copyright 2007-2011 Manuel Pichler. All rights reserved. * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License * @link http://www.phpundercontrol.org/ */ 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-2.0.2/tests/_files/a.php000066400000000000000000000006011250501601200163120ustar00rootroot00000000000000