| Duplication Type | Short Description |
|---|---|
| Data Duplication | Repeated constants, literals, configuration values, or raw data across the codebase. |
| Type Duplication | Similar logic implemented separately for different data types/classes. |
| Algorithm Duplication | Repeated computational logic or processing algorithms with minor variations. |
| Structural Duplication | Repeated control-flow or architectural structure even when logic differs slightly. |
| Semantic Duplication | Different code expressing the same business meaning or rule. |
| Temporal Duplication | Operations that must always occur together or in a fixed sequence. |
| Knowledge Duplication | The same business fact or rule represented in multiple places/systems. |
| Representation Duplication | Multiple nearly-identical models, DTOs, schemas, or data representations. |
| Behavioral Duplication | Different implementations producing the same observable behavior. |
| UI Duplication | Repeated interface layouts, components, interaction logic, or styling patterns. |
| Configuration Duplication | Repeated infrastructure, deployment, runtime, or application configuration. |
| Test Duplication | Repeated test setup, fixtures, assertions, or validation logic. |
| Query Duplication | Repeated SQL, ORM queries, filters, or data-access patterns. |
| Protocol Duplication | Repeated handling logic for APIs, transports, retries, auth, or serialization. |
| Copy-Paste Duplication | Directly copied source code with little or no modification. |
| Textual Duplication | Similar or identical raw source text regardless of meaning. |
| Lexical Duplication | Similar token sequences after parsing source code. |
| Syntactic Duplication | Similar AST/code structure despite formatting or naming differences. |
| Functional Duplication | Different implementations achieving the same functional/business outcome. |
| Architectural Duplication | Repeated subsystem, service, module, or application architecture patterns. |
| Process Duplication | Repeated manual operational or development workflows/processes. |
| Cross-Service Duplication | Duplicate business logic or validation spread across microservices/systems. |
| Clone Type-1 Duplication | Exact copied code differing only in whitespace/comments. |
| Clone Type-2 Duplication | Copied code with renamed variables, methods, or types. |
| Clone Type-3 Duplication | Copied code with partial modifications, insertions, or deletions. |
| Clone Type-4 Duplication | Different implementations that perform the same behavior semantically. |
| Logic Duplication | Repeated conditional or decision-making logic throughout the system. |
| Validation Duplication | Repeated input validation or business validation rules. |
| Error-Handling Duplication | Repeated exception handling, retry, fallback, or logging patterns. |
| State Duplication | Multiple independent representations of the same mutable state. |
| Documentation Duplication | Repeated or mirrored documentation that can drift out of sync. |
| Dependency Duplication | Multiple versions or repeated inclusion of similar libraries/packages. |
| Schema Duplication | Repeated database, API, event, or serialization schema definitions. |
| Mapping Duplication | Repeated object conversion or transformation code between layers/models. |
| Permission Duplication | Repeated authorization or access-control rules across components. |
| Workflow Duplication | Repeated orchestration or business-process flow logic. |
| Event Duplication | Multiple systems independently reacting to or recreating the same event semantics. |
| Caching Duplication | Duplicate caching logic, invalidation rules, or cache representations. |
| Localization Duplication | Repeated translation strings or locale-specific formatting rules. |
| Build Duplication | Repeated CI/CD, build scripts, packaging, or compilation logic. |
| Monitoring Duplication | Repeated metrics, logging, tracing, or observability instrumentation. |
| Serialization Duplication | Repeated encoding/decoding logic for JSON, XML, protobuf, etc. |
| Integration Duplication | Repeated glue code integrating with third-party systems/services. |
| Policy Duplication | Repeated business policy or governance rules in multiple locations. |
| Domain Rule Duplication | Core domain/business invariants implemented independently in multiple places. |
| Security Duplication | Repeated authentication, encryption, sanitization, or security checks. |
| Concurrency Duplication | Repeated synchronization, locking, threading, or async coordination patterns. |
| Resource Lifecycle Duplication | Repeated acquire/use/release patterns for files, sockets, DB connections, etc. |
| Boilerplate Duplication | Repeated scaffolding or framework-required code with minimal variation. |
| Template Duplication | Repeated code/templates differing only by injected values or parameters. |
| Generated-Code Duplication | Duplicate generated artifacts caused by code generators or schema compilers. |
| Intentional Duplication | Deliberate duplication to preserve isolation, simplicity, or deployability. |
| Accidental Duplication | Unplanned duplication caused by copy-paste, poor abstraction, or drift. |
This document shows common duplication patterns in PHP codebases. Each section includes:
- Duplicate implementations
- Why the duplication is problematic
- A refactored/deduplicated approach
function calculateSalesTax(float $amount): float {
return $amount * 0.07;
}function calculateInvoiceTax(float $amount): float {
return $amount * 0.07;
}function calculateRefundTax(float $amount): float {
return $amount * 0.07;
}const TAX_RATE = 0.07;
function calculateTax(float $amount): float {
return $amount * TAX_RATE;
}function processUser(User $user): void {
echo $user->getName();
}function processAdmin(Admin $admin): void {
echo $admin->getName();
}function processCustomer(Customer $customer): void {
echo $customer->getName();
}interface NamedEntity {
public function getName(): string;
}
function processNamedEntity(NamedEntity $entity): void {
echo $entity->getName();
}function calculateRegularDiscount(float $price): float {
return $price * 0.90;
}function calculateHolidayDiscount(float $price): float {
return $price * 0.85;
}function calculateVipDiscount(float $price): float {
return $price * 0.80;
}function applyDiscount(float $price, float $discountRate): float {
return $price * (1 - $discountRate);
}function loadUsers(PDO $db): array {
$stmt = $db->query('SELECT * FROM users');
return $stmt->fetchAll();
}function loadOrders(PDO $db): array {
$stmt = $db->query('SELECT * FROM orders');
return $stmt->fetchAll();
}function loadInvoices(PDO $db): array {
$stmt = $db->query('SELECT * FROM invoices');
return $stmt->fetchAll();
}function loadTable(PDO $db, string $table): array {
$stmt = $db->query("SELECT * FROM {$table}");
return $stmt->fetchAll();
}function isAdult(User $user): bool {
return $user->age >= 18;
}function canRegister(User $user): bool {
return $user->birthDate <= new DateTime('-18 years');
}function allowPurchase(User $user): bool {
return !$user->isMinor();
}class AgePolicy {
public static function isAdult(User $user): bool {
return $user->birthDate <= new DateTime('-18 years');
}
}$lock->acquire();
updateInventory();
$lock->release();$lock->acquire();
updateOrders();
$lock->release();$lock->acquire();
sendInvoice();
$lock->release();function withLock(Lock $lock, callable $callback): void {
$lock->acquire();
try {
$callback();
} finally {
$lock->release();
}
}const PASSWORD_MAX_LENGTH = 32;$validator->maxLength('password', 32);VARCHAR(32)class PasswordRules {
public const MAX_LENGTH = 32;
}class UserDTO {
public string $name;
public string $email;
}class UserApiModel {
public string $name;
public string $email;
}class UserViewModel {
public string $name;
public string $email;
}class UserData {
public function __construct(
public string $name,
public string $email,
) {}
}function isActive(User $user): bool {
return !$user->disabled && $user->confirmed;
}function canLogin(User $user): bool {
return $user->confirmed && !$user->disabled;
}function canAccessDashboard(User $user): bool {
return !$user->disabled && $user->confirmed;
}class UserStatus {
public static function isActive(User $user): bool {
return !$user->disabled && $user->confirmed;
}
}<input type="text" name="first_name" class="form-control"><input type="text" name="last_name" class="form-control"><input type="text" name="city" class="form-control">function renderInput(string $name): string {
return sprintf(
'<input type="text" name="%s" class="form-control">',
htmlspecialchars($name)
);
}redis_timeout: 30
redis_retries: 3cache_timeout: 30
cache_retries: 3queue_timeout: 30
queue_retries: 3shared_defaults:
timeout: 30
retries: 3$user = UserFactory::create();
$this->actingAs($user);
$response = $this->get('/dashboard');
$response->assertOk();$user = UserFactory::create();
$this->actingAs($user);
$response = $this->get('/settings');
$response->assertOk();function authenticatedGet(string $url): TestResponse {
$user = UserFactory::create();
$this->actingAs($user);
return $this->get($url);
}$db->query("SELECT * FROM users WHERE deleted = 0");$db->query("SELECT * FROM admins WHERE deleted = 0");$db->query("SELECT * FROM customers WHERE deleted = 0");function selectActive(PDO $db, string $table): array {
$stmt = $db->query("SELECT * FROM {$table} WHERE deleted = 0");
return $stmt->fetchAll();
}$response = $client->get($url, [
'headers' => ['Authorization' => 'Bearer ' . $token],
]);$response = $client->post($url, [
'headers' => ['Authorization' => 'Bearer ' . $token],
]);class ApiClient {
public function request(string $method, string $url, array $options = []) {
$options['headers']['Authorization'] = 'Bearer ' . $this->token;
return $this->client->request($method, $url, $options);
}
}$total = $subtotal + $tax;
$formatted = number_format($total, 2);
echo $formatted;$total = $subtotal + $tax;
$formatted = number_format($total, 2);
echo $formatted;function renderTotal(float $subtotal, float $tax): void {
$total = $subtotal + $tax;
echo number_format($total, 2);
}if ($user->role === 'admin' || $user->role === 'manager') {
grantAccess();
}if (in_array($user->role, ['admin', 'manager'])) {
showDashboard();
}function hasElevatedAccess(User $user): bool {
return in_array($user->role, ['admin', 'manager']);
}if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
throw new InvalidArgumentException();
}if (!filter_var($customerEmail, FILTER_VALIDATE_EMAIL)) {
throw new InvalidArgumentException();
}function validateEmail(string $email): void {
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
throw new InvalidArgumentException('Invalid email');
}
}try {
syncUsers();
} catch (Throwable $e) {
logger()->error($e->getMessage());
}try {
syncOrders();
} catch (Throwable $e) {
logger()->error($e->getMessage());
}function safely(callable $callback): void {
try {
$callback();
} catch (Throwable $e) {
logger()->error($e->getMessage());
}
}$user->balance = 100;
$cache['balance'] = 100;
$_SESSION['balance'] = 100;class BalanceService {
public function set(User $user, float $balance): void {
$user->balance = $balance;
}
}// Max upload size: 20MBMaximum upload size is 20MB.upload_limit: 20MBclass UploadConfig {
public const MAX_SIZE_MB = 20;
}"monolog/monolog": "^2.0""monolog/monolog": "^2.1""monolog/monolog": "^3.0""monolog/monolog": "^3.0"'name' => 'required|string|max:255'#[Assert\Length(max: 255)]VARCHAR(255)class UserSchema {
public const NAME_MAX_LENGTH = 255;
}$userDto->name = $user->name;
$userDto->email = $user->email;$payload['name'] = $user->name;
$payload['email'] = $user->email;function mapUser(User $user): array {
return [
'name' => $user->name,
'email' => $user->email,
];
}if (!$user->isAdmin()) {
abort(403);
}if (!$user->isAdmin()) {
throw new AuthorizationException();
}function requireAdmin(User $user): void {
if (!$user->isAdmin()) {
throw new AuthorizationException();
}
}validateOrder();
chargeCard();
sendReceipt();validateSubscription();
chargeCard();
sendReceipt();function executePurchaseWorkflow(callable $validator): void {
$validator();
chargeCard();
sendReceipt();
}$password = password_hash($password, PASSWORD_BCRYPT);$pin = password_hash($pin, PASSWORD_BCRYPT);function hashSecret(string $secret): string {
return password_hash($secret, PASSWORD_BCRYPT);
}$file = fopen($path, 'r');
$data = fread($file, filesize($path));
fclose($file);$file = fopen($otherPath, 'r');
$data = fread($file, filesize($otherPath));
fclose($file);function readFileContents(string $path): string {
$file = fopen($path, 'r');
try {
return fread($file, filesize($path));
} finally {
fclose($file);
}
}class UserController {
public function __construct(private Logger $logger) {}
}class OrderController {
public function __construct(private Logger $logger) {}
}abstract class BaseController {
public function __construct(protected Logger $logger) {}
}<h1>User Report</h1>
<p>Total: <?= $total ?></p><h1>Sales Report</h1>
<p>Total: <?= $total ?></p>function renderReport(string $title, float $total): void {
echo "<h1>{$title}</h1>";
echo "<p>Total: {$total}</p>";
}$mutex->lock();
updateStats();
$mutex->unlock();$mutex->lock();
updateCache();
$mutex->unlock();function synchronized(Mutex $mutex, callable $callback): void {
$mutex->lock();
try {
$callback();
} finally {
$mutex->unlock();
}
}$metrics->increment('users.created');$metrics->increment('orders.created');$metrics->increment('tickets.created');function trackCreated(string $entity): void {
metrics()->increment("{$entity}.created");
}json_encode([
'id' => $user->id,
'name' => $user->name,
]);json_encode([
'id' => $admin->id,
'name' => $admin->name,
]);function serializeIdentity(object $entity): string {
return json_encode([
'id' => $entity->id,
'name' => $entity->name,
]);
}$mailchimp->subscribe($email);
logger()->info('Subscribed');$sendgrid->subscribe($email);
logger()->info('Subscribed');function subscribe(NewsletterProvider $provider, string $email): void {
$provider->subscribe($email);
logger()->info('Subscribed');
}if ($invoice->amount > 10000) {
requireManagerApproval();
}if ($purchase->amount > 10000) {
requireManagerApproval();
}class ApprovalPolicy {
public static function requiresManagerApproval(float $amount): bool {
return $amount > 10000;
}
}if ($account->balance < 0) {
throw new RuntimeException('Overdraft not allowed');
}if ($wallet->balance < 0) {
throw new RuntimeException('Overdraft not allowed');
}class BalanceRules {
public static function ensureNonNegative(float $balance): void {
if ($balance < 0) {
throw new RuntimeException('Overdraft not allowed');
}
}
}