PHP에서 같은 함수가 여러 번 정의되어 발생하는 "Fatal error: Cannot redeclare function" 오류와 해결책을 알아보겠습니다.
문제: 같은 파일이 여러 번 포함되어 함수가 중복 정의됨
<?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>');
?>
문제: 서로 다른 라이브러리에서 같은 이름의 함수 사용
<?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";
?>
문제: 전역 함수 대신 클래스로 구조화하여 충돌 방지
<?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";
}
}
?>
문제: 라이브러리 버전에 따라 다른 함수 구현 필요
<?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);
});
?>
문제: 대규모 프로젝트에서 함수 충돌 방지 및 체계적 관리
<?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());
}
?>