PHP 함수 중복 정의 오류 해결하기

프리터코더·2025년 5월 27일
0

php 문제 해결

목록 보기
36/79

PHP에서 같은 함수가 여러 번 정의되어 발생하는 "Fatal error: Cannot redeclare function" 오류와 해결책을 알아보겠습니다.

1. function_exists()로 중복 정의 방지

문제: 같은 파일이 여러 번 포함되어 함수가 중복 정의됨

<?php
// 함수 존재 여부 확인 후 정의
if (!function_exists('formatCurrency')) {
    function formatCurrency($amount, $currency = 'KRW') {
        switch ($currency) {
            case 'KRW':
                return number_format($amount) . '원';
            case 'USD':
                return '$' . number_format($amount, 2);
            default:
                return number_format($amount, 2) . ' ' . $currency;
        }
    }
}

if (!function_exists('sanitizeInput')) {
    function sanitizeInput($input) {
        if (is_array($input)) {
            return array_map('sanitizeInput', $input);
        }
        
        return htmlspecialchars(trim($input), ENT_QUOTES, 'UTF-8');
    }
}

if (!function_exists('generateToken')) {
    function generateToken($length = 32) {
        return bin2hex(random_bytes($length / 2));
    }
}

if (!function_exists('debugLog')) {
    function debugLog($message, $level = 'INFO') {
        $timestamp = date('Y-m-d H:i:s');
        $log_entry = "[$timestamp] [$level] $message" . PHP_EOL;
        
        error_log($log_entry, 3, __DIR__ . '/../logs/debug.log');
    }
}
?>
<?php
// 여러 번 포함되어도 안전
require_once __DIR__ . '/../includes/helpers.php';
require_once __DIR__ . '/../includes/helpers.php'; // 중복 포함

// 함수 사용
echo formatCurrency(1000000);
echo sanitizeInput('<script>alert("xss")</script>');
?>

2. 네임스페이스를 이용한 함수 분리

문제: 서로 다른 라이브러리에서 같은 이름의 함수 사용

<?php
namespace Utils;

function formatString($str) {
    return ucwords(strtolower(trim($str)));
}

function truncateString($str, $length = 100) {
    if (strlen($str) <= $length) {
        return $str;
    }
    
    return substr($str, 0, $length - 3) . '...';
}

function slugify($str) {
    $str = strtolower($str);
    $str = preg_replace('/[^a-z0-9\s-]/', '', $str);
    $str = preg_replace('/[\s-]+/', '-', $str);
    return trim($str, '-');
}
?>
<?php
namespace Helpers;

function formatString($str) {
    // 다른 구현
    return strtoupper(trim($str));
}

function validateEmail($email) {
    return filter_var($email, FILTER_VALIDATE_EMAIL) !== false;
}

function generateSlug($text) {
    return preg_replace('/[^a-z0-9]+/i', '-', strtolower($text));
}
?>
<?php
require_once 'src/Utils/StringHelper.php';
require_once 'src/Helpers/StringHelper.php';

// 네임스페이스로 구분하여 사용
use Utils\StringHelper as UtilsString;
use Helpers\StringHelper as HelpersString;

$text = "Hello World!";

echo UtilsString\formatString($text) . "\n"; // "Hello World!"
echo HelpersString\formatString($text) . "\n"; // "HELLO WORLD!"

// 별칭 사용
use function Utils\slugify;
use function Helpers\generateSlug;

echo slugify("Hello World!") . "\n";
echo generateSlug("Hello World!") . "\n";
?>

3. 클래스 기반 정적 메서드로 변환

문제: 전역 함수 대신 클래스로 구조화하여 충돌 방지

<?php
namespace Core;

class Validator {
    
    public static function email($email) {
        return filter_var($email, FILTER_VALIDATE_EMAIL) !== false;
    }
    
    public static function required($value) {
        return !empty(trim($value));
    }
    
    public static function minLength($value, $min) {
        return strlen(trim($value)) >= $min;
    }
    
    public static function maxLength($value, $max) {
        return strlen(trim($value)) <= $max;
    }
    
    public static function numeric($value) {
        return is_numeric($value);
    }
    
    public static function url($url) {
        return filter_var($url, FILTER_VALIDATE_URL) !== false;
    }
}
?>
<?php
namespace Utils;

class FileManager {
    
    public static function upload($file, $destination) {
        if (!isset($file['error']) || $file['error'] !== UPLOAD_ERR_OK) {
            throw new \Exception('파일 업로드 오류');
        }
        
        $filename = self::generateUniqueFilename($file['name']);
        $filepath = $destination . '/' . $filename;
        
        if (!move_uploaded_file($file['tmp_name'], $filepath)) {
            throw new \Exception('파일 이동 실패');
        }
        
        return $filename;
    }
    
    public static function delete($filepath) {
        if (file_exists($filepath)) {
            return unlink($filepath);
        }
        return false;
    }
    
    private static function generateUniqueFilename($original_name) {
        $extension = pathinfo($original_name, PATHINFO_EXTENSION);
        return uniqid() . '.' . $extension;
    }
    
    public static function getFileSize($filepath) {
        return file_exists($filepath) ? filesize($filepath) : false;
    }
}
?>
<?php
require_once 'src/Core/Validator.php';
require_once 'src/Utils/FileManager.php';

use Core\Validator;
use Utils\FileManager;

// 검증
$email = 'user@example.com';
if (Validator::email($email)) {
    echo "유효한 이메일입니다.\n";
}

// 파일 관리
if ($_FILES['upload']) {
    try {
        $filename = FileManager::upload($_FILES['upload'], './uploads');
        echo "업로드 완료: $filename\n";
    } catch (Exception $e) {
        echo "오류: " . $e->getMessage() . "\n";
    }
}
?>

4. 조건부 함수 정의 및 버전 관리

문제: 라이브러리 버전에 따라 다른 함수 구현 필요

<?php
// PHP 버전별 호환성 함수

// array_key_first (PHP 7.3+)
if (!function_exists('array_key_first')) {
    function array_key_first(array $array) {
        foreach ($array as $key => $value) {
            return $key;
        }
        return null;
    }
}

// array_key_last (PHP 7.3+)
if (!function_exists('array_key_last')) {
    function array_key_last(array $array) {
        return array_key_first(array_reverse($array, true));
    }
}

// str_contains (PHP 8.0+)
if (!function_exists('str_contains')) {
    function str_contains(string $haystack, string $needle): bool {
        return $needle !== '' && strpos($haystack, $needle) !== false;
    }
}

// str_starts_with (PHP 8.0+)
if (!function_exists('str_starts_with')) {
    function str_starts_with(string $haystack, string $needle): bool {
        return $needle !== '' && strncmp($haystack, $needle, strlen($needle)) === 0;
    }
}

// str_ends_with (PHP 8.0+)
if (!function_exists('str_ends_with')) {
    function str_ends_with(string $haystack, string $needle): bool {
        return $needle !== '' && substr($haystack, -strlen($needle)) === $needle;
    }
}
?>
<?php
namespace Core;

class FunctionRegistry {
    private static $functions = [];
    
    public static function register($name, callable $callback) {
        if (isset(self::$functions[$name])) {
            throw new \Exception("함수 '$name'이 이미 등록되어 있습니다.");
        }
        
        self::$functions[$name] = $callback;
    }
    
    public static function call($name, ...$args) {
        if (!isset(self::$functions[$name])) {
            throw new \Exception("등록되지 않은 함수: '$name'");
        }
        
        return call_user_func_array(self::$functions[$name], $args);
    }
    
    public static function exists($name) {
        return isset(self::$functions[$name]);
    }
    
    public static function unregister($name) {
        unset(self::$functions[$name]);
    }
    
    public static function getRegistered() {
        return array_keys(self::$functions);
    }
}

// 사용 예시
FunctionRegistry::register('format_price', function($price) {
    return number_format($price) . '원';
});

FunctionRegistry::register('validate_phone', function($phone) {
    return preg_match('/^01[0-9]-?[0-9]{4}-?[0-9]{4}$/', $phone);
});
?>

5. 오토로더와 함수 관리

문제: 대규모 프로젝트에서 함수 충돌 방지 및 체계적 관리

<?php
namespace Core;

class FunctionLoader {
    private static $loaded_modules = [];
    
    public static function loadModule($module_name) {
        if (in_array($module_name, self::$loaded_modules)) {
            return; // 이미 로드됨
        }
        
        $module_file = __DIR__ . "/../Functions/{$module_name}.php";
        
        if (!file_exists($module_file)) {
            throw new \Exception("함수 모듈을 찾을 수 없습니다: $module_name");
        }
        
        // 모듈 로드 전 현재 정의된 함수 목록 저장
        $functions_before = get_defined_functions()['user'];
        
        require_once $module_file;
        
        // 새로 정의된 함수 확인
        $functions_after = get_defined_functions()['user'];
        $new_functions = array_diff($functions_after, $functions_before);
        
        if (!empty($new_functions)) {
            error_log("모듈 '$module_name'에서 로드된 함수: " . implode(', ', $new_functions));
        }
        
        self::$loaded_modules[] = $module_name;
    }
    
    public static function getLoadedModules() {
        return self::$loaded_modules;
    }
}
?>
<?php
// 문자열 관련 함수들

if (!function_exists('str_random')) {
    function str_random($length = 16) {
        $characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
        $result = '';
        
        for ($i = 0; $i < $length; $i++) {
            $result .= $characters[rand(0, strlen($characters) - 1)];
        }
        
        return $result;
    }
}

if (!function_exists('str_limit')) {
    function str_limit($value, $limit = 100, $end = '...') {
        if (mb_strwidth($value, 'UTF-8') <= $limit) {
            return $value;
        }
        
        return rtrim(mb_strimwidth($value, 0, $limit, '', 'UTF-8')) . $end;
    }
}
?>
<?php
require_once 'src/Core/FunctionLoader.php';

use Core\FunctionLoader;

// 필요한 함수 모듈들 로드
try {
    FunctionLoader::loadModule('StringFunctions');
    FunctionLoader::loadModule('DateFunctions');
    FunctionLoader::loadModule('ValidationFunctions');
    
    echo "로드된 모듈: " . implode(', ', FunctionLoader::getLoadedModules()) . "\n";
    
} catch (Exception $e) {
    die("함수 모듈 로드 오류: " . $e->getMessage());
}
?>

주의사항

  • require_once 사용: 파일 중복 포함 방지
  • function_exists() 확인: 함수 정의 전 존재 여부 확인
  • 네임스페이스 활용: 함수명 충돌 방지
  • 클래스 기반 설계: 전역 함수보다 클래스 메서드 권장
  • 모듈화: 관련 함수들을 그룹별로 분리하여 관리
profile
일용직 개발자. freetercoder@gmail.com

0개의 댓글