관리 메뉴

웹개발자의 기지개

[PHP] PHP7 에서 PHP8 마이그레이션할때 주의할 점 본문

PHP

[PHP] PHP7 에서 PHP8 마이그레이션할때 주의할 점

웹개발자 워니 2026. 3. 20. 14:20

 

[주의할 점]

 

1. 정의되지 않은 배열 인덱스 접근
2. count()에 배열 아닌 값 전달
3. 문자열/숫자 비교의 엄격성 변화
4. 오래된 함수 사용
5. each(), create_function() 등 제거
6. mysqli / PDO 사용 방식 문제
7. null 처리 문제
8. 필수/선택 파라미터 순서 문제

 

 

1. 짧은 배열/변수 접근 전 undefined 문제

[PHP7]

$user_id = $_POST['user_id'];
$user_name = $_GET['user_name'];

 

[PHP8]

$user_id = $_POST['user_id'] ?? '';
$user_name = $_GET['user_name'] ?? '';

 

$page = isset($_GET['page']) ? (int)$_GET['page'] : 1;

$page = (int)($_GET['page'] ?? 1);

 

 

[PHP7]

$mode = $_REQUEST['mode'];
if ($mode == 'write') {
...
}

 

[PHP8]

$mode = $_REQUEST['mode'] ?? '';
if ($mode === 'write') {
...
}

 

 

2. count() 에 null 넣는 문제

 

[PHP7]

if (count($arr) > 0) {

...

}

 

[PHP8]

if (is_array($arr) && count($arr) > 0) {
...
}

$arr = $arr ?? [];
if (count($arr) > 0) {
...
}

 

파일업로드 예시 코드

[PHP7]

if (count($_FILES['upfile']['name']) > 0) {
...
}

 

[PHP8]

$fileNames = $_FILES['upfile']['name'] ?? [];
if (is_array($fileNames) && count($fileNames) > 0) {
...
}

 

3. explode(), trim(), strlen() 등에 null 전달 문제

 

[PHP7]

$name = trim($_POST['name']);
$tags = explode(',', $_POST['tags']);
$len = strlen($_POST['title']);

 

[PHP8]

$name = trim($_POST['name'] ?? '');
$tags = explode(',', $_POST['tags'] ?? '');
$len = strlen($_POST['title'] ?? '');

 

4. 숫자/문자열 비교 느슨한 코드 수정 - 비교 동작이 더 엄격하게 적용됨

 

[PHP7]

if ($_GET['no'] == 0) {
...
}

 

if ($member_level == '1') {
...
}

 

[PHP8]

$no = (int)($_GET['no'] ?? 0);
if ($no === 0) {
...
}

$member_level = (int)($member_level ?? 0);
if ($member_level === 1) {
...
}

 

5. optional parameter before required parameter 문제

: 선택 파라미터가 필수 파라미터보다 앞에 있으면 문제

 

[PHP7]

function myFunc($title = '', $name) {
return $title . $name;
}

function uploadFile($dir = '', $fileName, $tmpName)

 

 

[PHP8]

function myFunc($name, $title = '') {
return $title . $name;
}

function uploadFile($fileName, $tmpName, $dir = '')

 

 

6. each() 함수 제거 

 

[PHP7]

reset($arr);
while (list($key, $val) = each($arr)) {
echo $key . ' => ' . $val;
}

 

[PHP8]

foreach ($arr as $key => $val) {
echo $key . ' => ' . $val;
}

 

7. create_function() 제거

[PHP7]

$func = create_function('$a,$b', 'return $a+$b;');
echo $func(1, 2);

 

[PHP8]

$func = function($a, $b) {

  return $a + $b;

};

echo $func(1, 2);
$func = fn($a, $b) => $a + $b;
 
 

8. preg_replace / 문자열 처리 부분 점검

: 정규식 옵션이나 null 전달 문제를 확인해야 함

 

[PHP7]

$str = preg_replace("/\s+/", " ", $str);

preg_replace("/(\d+)/e", "myfunc('\\1')", $str);


: /e modifier 쓰는 경우가 있으면 반드시 수정해야 함

 

[PHP8]

$str = preg_replace("/\s+/", " ", $str ?? '');

 

$str = preg_replace_callback("/(\d+)/", function($matches) {
return myfunc($matches[1]);
}, $str ?? '');

 

9. session / cookie 처리 점검

[PHP7]

$_SESSION['user_id'] = $user_id;
setcookie("save_id", $user_id, time()+86400);
 
[PHP8]
 
$_SESSION['user_id'] = $user_id;

setcookie('save_id', $user_id, [
'expires' => time() + 86400,
'path' => '/',
'secure' => false, // HTTPS면 true 권장
'httponly' => true,
'samesite' => 'Lax'
]);

 

 

10. mysqli 구형 스타일 혼합 코드 정리

[PHP7]

$conn = mysqli_connect($db_host, $db_user, $db_pass, $db_name);
$sql = "select * from member where user_id='$user_id'";
$result = mysqli_query($conn, $sql);
$row = mysqli_fetch_array($result);

 

[PHP8] - SQL 인젝션 방지

$conn = mysqli_connect($db_host, $db_user, $db_pass, $db_name);
if (!$conn) {
die('DB 연결 실패: ' . mysqli_connect_error());
}

 

$user_id = $_POST['user_id'] ?? '';

$stmt = mysqli_prepare($conn, "SELECT * FROM member WHERE user_id = ?");
mysqli_stmt_bind_param($stmt, "s", $user_id);
mysqli_stmt_execute($stmt);

$result = mysqli_stmt_get_result($stmt);
$row = mysqli_fetch_assoc($result);

 

 

11. mysql_* 함수 사용 중이면 반드시 수정

PHP5 에서 이용하던 
mysql_connect(...);
mysql_query(...);
mysql_fetch_array(...);

코드 수정할 것.

 

[PHP8]

$conn = mysqli_connect($db_host, $db_user, $db_pass, $db_name);
$result = mysqli_query($conn, "SELECT * FROM board");
while ($row = mysqli_fetch_assoc($result)) {
echo $row['subject'];
}

 

 

12. include / require 경로 문제 더 엄격하게 점검

 

[PHP7]

include "lib.php";
include "../dbconn.php";

 

[PHP8]

include __DIR__ . "/lib.php";

include __DIR__ . "/../dbconn.php";

 

 

13. 회원가입 입력값 처리

 

[PHP7]

$user_id = trim($_POST['user_id']);
$user_pw = trim($_POST['user_pw']);
$user_name = trim($_POST['user_name']);

if ($user_id == '' || $user_pw == '' || $user_name == '') {
die('필수값 누락');
}

 

[PHP8]

$user_id = trim($_POST['user_id'] ?? '');
$user_pw = trim($_POST['user_pw'] ?? '');
$user_name = trim($_POST['user_name'] ?? '');

if ($user_id === '' || $user_pw === '' || $user_name === '') {
die('필수값 누락');
}

 

14. 비밀번호 저장 방식 개선

 

[PHP7]

$user_pw = md5($_POST['user_pw'] ?? '');

$user_pw = sha1($_POST['user_pw'] ?? '');

 

[PHP8]

$user_pw = $_POST['user_pw'] ?? '';
$hash_pw = password_hash($user_pw, PASSWORD_DEFAULT);

 

// 로그인 확인

$input_pw = $_POST['user_pw'] ?? '';
$db_pw = $row['user_pw'] ?? '';

if (password_verify($input_pw, $db_pw)) {
echo "로그인 성공";
} else {
echo "로그인 실패";
}

 

15. 게시판 글쓰기 처리

[PHP7]

$subject = addslashes($_POST['subject']);
$content = addslashes($_POST['content']);
$sql = "insert into board set subject='$subject', content='$content'";
mysqli_query($conn, $sql);

 

[PHP8]

$subject = trim($_POST['subject'] ?? '');

$content = trim($_POST['content'] ?? '');

$stmt = mysqli_prepare($conn, "INSERT INTO board (subject, content) VALUES (?, ?)");
mysqli_stmt_bind_param($stmt, "ss", $subject, $content);
mysqli_stmt_execute($stmt);

 

 

16. 게시판 상세보기

[PHP7]

$no = $_GET['no'];
$sql = "select * from board where no='$no'";
$result = mysqli_query($conn, $sql);
$row = mysqli_fetch_array($result);

 

[PHP8]

$no = (int)($_GET['no'] ?? 0);

$stmt = mysqli_prepare($conn, "SELECT * FROM board WHERE no = ?");
mysqli_stmt_bind_param($stmt, "i", $no);
mysqli_stmt_execute($stmt);

$result = mysqli_stmt_get_result($stmt);
$row = mysqli_fetch_assoc($result);

 

 

17. 게시판 목록 페이징

[PHP7]

$page = $_GET['page'];
if (!$page) $page = 1;

$start = ($page - 1) * 10;
$sql = "select * from board order by no desc limit $start, 10";

 

[PHP8]

$page = (int)($_GET['page'] ?? 1);
if ($page < 1) $page = 1;

$start = ($page - 1) * 10;
$limit = 10;

$sql = "SELECT * FROM board ORDER BY no DESC LIMIT $start, $limit";

 

$page = (int)($_GET['page'] ?? 1);
$start = (int)$start;
$limit = (int)$limit;

 

18. 문자열 함수에 배열 전달

[PHP7]

echo htmlspecialchars($_POST['name']);

 

[PHP8]

$name = $_POST['name'] ?? '';
if (!is_string($name)) {
$name = '';
}
echo htmlspecialchars($name, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');

 

 

19. foreach 대상이 배열이 아닌 경우

[PHP7]

foreach ($list as $row) {
echo $row['name'];
}

 

[PHP8]

$list = is_array($list ?? null) ? $list : [];

foreach ($list as $row) {
echo $row['name'] ?? '';
}

 

 

20. implode() 인자 순서 헷갈림 - 표준 순서대로

[PHP7]

$str = implode($arr, ',');

 

[PHP8]

$str = implode(',', $arr);

 

 

21. substr(), strpos() 에서 false/null 처리

 

[PHP7] - 맨 앞에 @ 이 있으면 0 이어서 false 가 된다.

if (strpos($email, '@')) {
echo '이메일형식';
}
 
[PHP8]
 
if (strpos($email ?? '', '@') !== false) {
echo '이메일형식';
}

 

 

22. 파일업로드 기능도 꼭 점검

[PHP7]

$filename = $_FILES['upfile']['name'];
$tmp_name = $_FILES['upfile']['tmp_name'];

move_uploaded_file($tmp_name, "./upload/".$filename);

 

[PHP8]

$uploadDir = __DIR__ . '/upload/';

if (!isset($_FILES['upfile']) || !is_array($_FILES['upfile'])) {
die('파일 정보가 없습니다.');
}

$filename = $_FILES['upfile']['name'] ?? '';
$tmpName = $_FILES['upfile']['tmp_name'] ?? '';
$error = $_FILES['upfile']['error'] ?? UPLOAD_ERR_NO_FILE;

if ($error !== UPLOAD_ERR_OK) {
die('업로드 오류');
}

$basename = basename($filename);
$saveName = time() . '_' . preg_replace('/[^a-zA-Z0-9._-]/', '_', $basename);

if (!move_uploaded_file($tmpName, $uploadDir . $saveName)) {
die('파일 저장 실패');
}

 

23. 마이그레이션용 공통 함수 만들어 놓기

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?
function post($key$default = '')
{
    return $_POST[$key] ?? $default;
}
 
function get($key$default = '')
{
    return $_GET[$key] ?? $default;
}
 
function h($str)
{
    return htmlspecialchars((string)$str, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
}
?>
cs

 

$user_id = trim(post('user_id'));
$name = trim(post('name'));
$page = (int)get('page', 1);

echo h($name);

 

24. 마이그레이션 수정 우선 순위 

1순위: 사이트가 죽는 치명 오류

each()
create_function()
함수 선언 파라미터 순서
null/array 타입 오류
include 경로 문제


2순위: 경고/Deprecated

undefined array key
trim/explode/strlen null 전달
count(null)
foreach(null)

3순위: 보안 개선

SQL 직접 문자열 조합
addslashes()
md5() 비밀번호
파일업로드 검증 부족

4순위: 코드 정리

공통 함수화
mysqli → PDO 또는 prepared statement 정리
중복 코드 제거

 

 

25. 예시 코드 정리

 

[PHP7]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?php
include "../dbconn.php";
 
$id = $_POST['id'];
$pw = $_POST['pw'];
 
if (!$id || !$pw) {
    die("값이 없습니다.");
}
 
$sql = "select * from member where id='$id'";
$result = mysqli_query($conn$sql);
$row = mysqli_fetch_array($result);
 
if ($row['pw'== md5($pw)) {
    $_SESSION['ss_id'= $row['id'];
    echo "로그인 성공";
else {
    echo "로그인 실패";
}
?>
cs

 

[PHP8]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<?php
session_start();
include __DIR__ . "/../dbconn.php";
 
$id = trim($_POST['id'] ?? '');
$pw = trim($_POST['pw'] ?? '');
 
if ($id === '' || $pw === '') {
    die("값이 없습니다.");
}
 
$stmt = mysqli_prepare($conn"SELECT id, pw FROM member WHERE id = ?");
mysqli_stmt_bind_param($stmt"s"$id);
mysqli_stmt_execute($stmt);
 
$result = mysqli_stmt_get_result($stmt);
$row = mysqli_fetch_assoc($result);
 
if ($row && password_verify($pw$row['pw'])) {
    $_SESSION['ss_id'= $row['id'];
    echo "로그인 성공";
else {
    echo "로그인 실패";
}
?>
cs

 

 

26. 마지막 정리

 

 

  • $_GET, $_POST, $_SESSION 접근 시 ?? 처리
  • count(null), foreach(null) 수정
  • trim(null), explode(null) 수정
  • each(), create_function() 제거
  • 함수 파라미터 순서 수정
  • SQL 직접 문자열 조합 → prepared statement
  • md5/sha1 비밀번호 → password_hash / password_verify
  • 파일업로드 검증 강화
  • include 경로를 __DIR__ 기반으로 정리

 

 

Comments