<?php
/**
 * Symbolic Evaluation Test File - PHP
 *
 * This file contains multiple source→sink flows for testing symbolic evaluation.
 * Each section demonstrates dangerous (unsafe) and safe patterns.
 *
 * Patterns covered:
 * - SQL injection (string concat vs prepared statements)
 * - Command injection (exec, shell_exec, system)
 * - Path traversal (file_get_contents, include)
 * - Superglobals as sources ($_GET, $_POST, $_REQUEST)
 * - PDO and mysqli patterns
 * - XSS (echo with user input)
 */

// =============================================================================
// SQL INJECTION PATTERNS
// =============================================================================

class UserRepository
{
    private PDO $pdo;
    private mysqli $mysqli;

    public function __construct(PDO $pdo, mysqli $mysqli)
    {
        $this->pdo = $pdo;
        $this->mysqli = $mysqli;
    }

    // UNSAFE: String concatenation with user input (PDO)
    public function getUserUnsafeConcat(string $userId): ?array
    {
        $query = "SELECT * FROM users WHERE id = '" . $userId . "'";
        $stmt = $this->pdo->query($query);  // Sink: danger
        return $stmt->fetch(PDO::FETCH_ASSOC);
    }

    // UNSAFE: Variable interpolation in double quotes
    public function getUserUnsafeInterpolation(string $username): ?array
    {
        $query = "SELECT * FROM users WHERE username = '$username'";
        $stmt = $this->pdo->query($query);  // Sink: danger
        return $stmt->fetch(PDO::FETCH_ASSOC);
    }

    // UNSAFE: sprintf with user input
    public function getUserUnsafeSprintf(string $email): ?array
    {
        $query = sprintf("SELECT * FROM users WHERE email = '%s'", $email);
        $stmt = $this->pdo->query($query);  // Sink: danger
        return $stmt->fetch(PDO::FETCH_ASSOC);
    }

    // UNSAFE: mysqli_query with concatenation
    public function getUserUnsafeMysqli(string $userId): ?array
    {
        $query = "SELECT * FROM users WHERE id = '" . $userId . "'";
        $result = mysqli_query($this->mysqli, $query);  // Sink: danger
        return mysqli_fetch_assoc($result);
    }

    // SAFE: PDO prepared statement
    public function getUserSafePrepared(string $userId): ?array
    {
        $query = "SELECT * FROM users WHERE id = :userId";
        $stmt = $this->pdo->prepare($query);
        $stmt->execute(['userId' => $userId]);  // Safe: parameterized
        return $stmt->fetch(PDO::FETCH_ASSOC);
    }

    // SAFE: mysqli prepared statement
    public function getUserSafeMysqliPrepared(string $userId): ?array
    {
        $query = "SELECT * FROM users WHERE id = ?";
        $stmt = $this->mysqli->prepare($query);
        $stmt->bind_param("s", $userId);  // Safe: parameterized
        $stmt->execute();
        return $stmt->get_result()->fetch_assoc();
    }

    // SAFE: Constant query
    public function getAllUsersSafe(): array
    {
        $query = "SELECT * FROM users";
        $stmt = $this->pdo->query($query);  // Safe: constant
        return $stmt->fetchAll(PDO::FETCH_ASSOC);
    }
}

// =============================================================================
// COMMAND INJECTION PATTERNS
// =============================================================================

class CommandService
{
    // UNSAFE: exec with concatenation
    public function executeUnsafeExec(string $filename): string
    {
        $command = "cat " . $filename;
        exec($command, $output);  // Sink: danger
        return implode("\n", $output);
    }

    // UNSAFE: shell_exec with interpolation
    public function executeUnsafeShellExec(string $path): string
    {
        $command = "ls $path";
        return shell_exec($command);  // Sink: danger
    }

    // UNSAFE: system with user input
    public function executeUnsafeSystem(string $filename): void
    {
        $command = "cat " . $filename;
        system($command);  // Sink: danger
    }

    // UNSAFE: passthru with backticks
    public function executeUnsafeBackticks(string $path): string
    {
        return `ls $path`;  // Sink: danger (backtick operator)
    }

    // SAFE: escapeshellarg for arguments
    public function executeSafeEscaped(string $filename): string
    {
        $command = "cat " . escapeshellarg($filename);
        exec($command, $output);  // Safer: escaped
        return implode("\n", $output);
    }

    // SAFE: Constant command
    public function listDirectorySafe(): string
    {
        return shell_exec("ls -la");  // Safe: constant
    }
}

// =============================================================================
// FILE OPERATIONS PATTERNS
// =============================================================================

class FileService
{
    private string $basePath;

    public function __construct(string $basePath)
    {
        $this->basePath = $basePath;
    }

    // UNSAFE: file_get_contents with concatenation
    public function readFileUnsafeConcat(string $filename): string
    {
        $path = $this->basePath . "/" . $filename;
        return file_get_contents($path);  // Sink: path traversal
    }

    // UNSAFE: include with user input
    public function includeFileUnsafe(string $filename): void
    {
        $path = $this->basePath . "/" . $filename;
        include($path);  // Sink: LFI danger
    }

    // UNSAFE: require with user input
    public function requireFileUnsafe(string $filename): void
    {
        $path = $this->basePath . "/" . $filename;
        require($path);  // Sink: LFI danger
    }

    // SAFE: Validated path
    public function readFileSafe(string $filename): string
    {
        if (strpos($filename, "..") !== false || $filename[0] === "/") {
            throw new InvalidArgumentException("Invalid filename");
        }
        $path = $this->basePath . "/" . $filename;
        return file_get_contents($path);  // Safe after validation
    }

    // SAFE: basename sanitization
    public function readFileSafeBasename(string $filename): string
    {
        $sanitized = basename($filename);  // Removes path components
        $path = $this->basePath . "/" . $sanitized;
        return file_get_contents($path);  // Safer
    }
}

// =============================================================================
// XSS PATTERNS
// =============================================================================

class OutputService
{
    // UNSAFE: echo with user input
    public function renderUnsafeEcho(string $userInput): void
    {
        echo "<div>$userInput</div>";  // Sink: XSS
    }

    // UNSAFE: print with concatenation
    public function renderUnsafePrint(string $username): void
    {
        print "<span>Welcome, " . $username . "</span>";  // Sink: XSS
    }

    // SAFE: htmlspecialchars escaping
    public function renderSafeEscaped(string $userInput): void
    {
        echo "<div>" . htmlspecialchars($userInput, ENT_QUOTES, 'UTF-8') . "</div>";
    }

    // SAFE: Constant output
    public function renderSafeConstant(): void
    {
        echo "<div>Welcome</div>";  // Safe: constant
    }
}

// =============================================================================
// VARIABLE PROPAGATION PATTERNS
// =============================================================================

class DataProcessor
{
    // UNSAFE: Assignment propagates danger
    public function processUnsafe(string $input): string
    {
        $processed = $input;  // Assignment propagates
        $transformed = strtoupper($processed);  // Function on dangerous
        return $transformed;
    }

    // UNSAFE: Multiple assignments
    public function chainAssignmentUnsafe(string $value): string
    {
        $a = $value;
        $b = $a;
        $c = $b;
        return $c;  // Danger propagates
    }

    // SAFE: Reassignment to constant
    public function processInputSafe(string $input): string
    {
        $processed = $input;
        $processed = "constant_value";  // Breaks danger
        return $processed;
    }
}

// =============================================================================
// CONTROL FLOW PATTERNS
// =============================================================================

class ControlFlowService
{
    // UNSAFE: Danger through conditional
    public function conditionalUnsafe(string $userInput): string
    {
        if (strlen($userInput) > 0) {
            $result = $userInput;  // Danger in true branch
        } else {
            $result = "default";
        }
        return $result;
    }

    // UNSAFE: Ternary with danger
    public function ternaryUnsafe(string $userInput): string
    {
        return strlen($userInput) > 0 ? $userInput : "default";
    }

    // UNSAFE: Null coalescing with danger
    public function nullCoalescingUnsafe(?string $userInput): string
    {
        return $userInput ?? "default";  // Danger if not null
    }

    // SAFE: Both branches are constants
    public function conditionalSafe(bool $flag): string
    {
        return $flag ? "option_a" : "option_b";  // Both safe
    }
}

// =============================================================================
// REQUEST HANDLERS (Superglobal Sources)
// =============================================================================

// UNSAFE: $_GET to SQL
function handleSearchUnsafe(): ?array
{
    $userId = $_GET['id'];  // Source: superglobal
    $pdo = new PDO('mysql:host=localhost;dbname=test', 'user', 'pass');
    $repo = new UserRepository($pdo, new mysqli());
    return $repo->getUserUnsafeConcat($userId);  // Danger
}

// SAFE: $_GET with prepared statement
function handleSearchSafe(): ?array
{
    $userId = $_GET['id'];  // Source: superglobal
    $pdo = new PDO('mysql:host=localhost;dbname=test', 'user', 'pass');
    $repo = new UserRepository($pdo, new mysqli());
    return $repo->getUserSafePrepared($userId);  // Safe
}

// UNSAFE: $_POST to command
function handleExecUnsafe(): string
{
    $filename = $_POST['filename'];  // Source: superglobal
    $service = new CommandService();
    return $service->executeUnsafeExec($filename);  // Danger
}

// UNSAFE: $_REQUEST to file read
function handleFileUnsafe(): string
{
    $filename = $_REQUEST['file'];  // Source: superglobal
    $service = new FileService("/var/data");
    return $service->readFileUnsafeConcat($filename);  // Danger
}

// SAFE: $_GET with validation
function handleFileSafe(): string
{
    $filename = $_GET['file'];  // Source: superglobal
    $service = new FileService("/var/data");
    return $service->readFileSafe($filename);  // Safe after validation
}
