작은숲:위키노트/PHP 7.0/하위 호환성이 없는 변경 사항
|
이 글은 http://php.net/manual/en/migration70.incompatible.php의 내용을 번역한 것입니다. 원문의 저작권은 PHP Documentation Group에게 있으며, 이 글의 라이선스는 원문의 라이선스(Creative Commons Attribution 3.0 License)를 따릅니다. |
오류와 예외 처리에서의 변경 사항
이전 버전까지 치명적인 오류(fatal error)나 복구할 수 있는 치명적인 오류(recoverable fatal error)로 처리되던 오류 중에서 PHP 7에서는 예외(exception)로 바뀐 것들이 많다. 이 오류 예외들은 Error 클래스를 상속받은 것으로 이 클래스는 Throwable 인터페이스를 구현한다. PHP 7의 모든 예외 클래스들이 이 새로운 기본 인터페이스를 상속받고 있다. PHP 7에서는 오류가 발생했을 때 예외 처리로 연계된다. 그래서 이제 사용자 오류 핸들러는 더 이상 작동하지 않는다. Error 예외를 잡아내지 않으면 새로운 치명적인 오류(fatal error)가 발생한다. PHP 7에서 오류를 처리하는 방법은 PHP 7 오류 문서에 자세히 나와있다. 이 문서에서는 하위 호환성이 없는 변경 사항에 대해서만 간략하게 소개한다.
set_exception_handler() 함수가 항상 Exception 객체를 받는다는 것을 보장하지 않음
Exception 형으로 선언해서 set_exception_handler() 함수로 등록한 예외 핸들러를 구현한 코드는 Error 객체가 던져졌을 때 치명적인 오류를 일으키게 된다. PHP 5와 PHP 7에서 모두 동작하는 핸들러가 필요하다면 핸들러에서 형 선언을 제거해야 한다. 기존의 코드를 PHP 7에서만 동작하도록 수정할 때는 Throwable 인터페이스를 갖는 Exception 형 선언으로 바꾸기만 하면 된다.
<?php
// PHP 5 era code that will break.
function handler(Exception $e) { ... }
set_exception_handler('handler');
// PHP 5 and 7 compatible.
function handler($e) { ... }
// PHP 7 only.
function handler(Throwable $e) { ... }
?>
내부 생성자는 실패했을 때 언제나 예외 발생
지금까지는 생성자의 처리가 실패했을 때 내부 클래스는 NULL이나 사용할 수 없는 객체를 반환했다. 사용자 클래스는 이미 그렇게 작성해 왔던 것처럼 모든 내부 클래스도 생성자가 제대로 처리하지 못하면 Exception을 던지게 되었다.
코드 분석 오류는 ParseError 발생
파서(parser)에서 오류가 있으면 ParseError 객체가 발생한다. eval() 함수로 오류를 처리할 때는 이 오류를 잡아낼 수 있는 catch 구문을 작성해야 한다.
E_STRICT 알림의 엄정성 변경
모든 E_STRICT 알림이 다른 단계로 재분류되었다. E_STRICT 상수는 그대로 남아있기 때문에 error_reporting(E_ALL|E_STRICT) 같은 코드에서 오류가 나지는 않는다.
| 상황 | 새로운 단계/동작 |
|---|---|
| 리소스에 의한 인덱싱(Indexing by a resource) | E_NOTICE |
| 추상 정적 메소드(Abstract static methods) | 알림은 제거되었고, '오류 아님' 발생 |
| 생성자의 재정의("Redefining" a constructor) | 알림은 제거되었고, '오류 아님' 발생 |
| 상속 중 서명 불일치(Signature mismatch during inheritance) | E_WARNING |
| 사용된 두 특성에서 호환이 되는 같은 프로퍼티(Same compatible property in two used traits)) | 알림은 제거되었고, '오류 아님' 발생 |
| 잘못된 방법으로 정적 프로퍼티에 접근(Accessing static property non-statically) | E_NOTICE |
| 오직 변수만 레퍼런스로 할당 가능(Only variables should be assigned by reference) | E_NOTICE |
| 오직 변수만 레퍼런스로 넘져주기 가능(Only variables should be passed by reference) | E_NOTICE |
| 잘못된 방법으로 정적 메소드 호출(Calling non-static methods statically) | E_DEPRECATED |
변수 처리 방법의 변경
PHP 7에서는 소스 파일을 해석하기 위해 추상 구문 트리(abstract syntax tree) 방식을 사용한다. 그래서 이전 버전들에서 사용하던 파서로 인해 발생했던 여러 문제를 해결하고 프로그래밍 언어로서 많은 향상이 있었다. 그러나 일관성을 유지하기 위해 기존에 있던 몇가지 기능을 제거했다. 이것은 하위 호환성을 깨는 것이다. 여기에서 그런 제거된 기능들에 대해 알아본다.
변수와 프로퍼티, 메서드의 간접 접근의 처리 방식 변경
변수와 프로퍼티(property), 메서드(method)의 간접 접근은 왼쪽에서 오른쪽으로 엄격히 처리된다. 이전에는 처리 순서가 바뀌는 경우도 있었다. 아래 표에 처리 순서가 어떻게 바뀌었는지 자세히 나와있다.
| 식 | PHP 5의 해석 | PHP 7의 해석 |
|---|---|---|
| $$foo['bar']['baz'] | ${$foo['bar']['baz']} | ($$foo)['bar']['baz'] |
| $foo->$bar['baz'] | $foo->{$bar['baz']} | ($foo->$bar)['baz'] |
| $foo->$bar['baz']() | $foo->{$bar['baz']}() | ($foo->$bar)['baz']() |
| Foo::$bar['baz']() | Foo::{$bar['baz']}() | (Foo::$bar)['baz']() |
이전 버전에서 오른쪽에서 왼쪽으로 해석하는 것으르 가정하고 작성한 코드는 위 표의 가운데 열처럼 중괄호({})에 넣어 해석 순서를 명확하게 해야 한다. 그러면 PHP7.x와 호환성을 유지하면서 PHP 5.x와 하위 호환성도 유지할 수 있다.
list() 함수의 처리 방법 변경
list() 함수에서 변수 역순 할당 기능 제거
이제 list() 함수는 이전과는 달리 매개변수로 넘겨진 순서에 따라 변수를 할당하게 된다. 보통 이 경우는 list() 함수에서 배열 [] 연산자와 함께 쓰일 때 일어난다.
<?php
list($a[], $a[], $a[]) = [1, 2, 3];
var_dump($a);
?>
위와 같은 코드는 PHP 5에서 아래와 같은 결과를 얻는다.
array(3) {
[0]=>
int(3)
[1]=>
int(2)
[2]=>
int(1)
}
하지만 PHP 7에서는 아래와 같은 결과를 얻게 된다.
array(3) {
[0]=>
int(1)
[1]=>
int(2)
[2]=>
int(3)
}
list() 함수에서 할당이 어떤 순서로 일어나는지에 의존하게 되는 코드는 쓰지 않기를 권고한다. 할당하는 순서는 앞으로도 변경될 수 있다.
list() 함수에서 매개변수를 넘기지 않은 빈 할당 기능 제거
이제 list() 함수는 아래와 같이 매개변수를 넣지 않고 쓸 수 없다. list() 함수에는 반드시 값을 할당할 변수를 매개변수로 넘겨줘야 한다.
<?php
list() = $a;
list(,,) = $a;
list($x, list(), $y) = $a;
?>
list() 함수의 문자열 전개 기능 제거
이제 list() 함수는 문자열 변수를 전개할 수 없다. 따라서 이런 경우 str_split() 함수를 써야 한다.
참조에 의한 할당에서 자동으로 생성된 배열 요소의 정렬 순서 변경
참조에 의한 할당에서 배열의 요소를 자동으로 생성한 경우 배열 요소의 정렬 순서가 바뀌었다.
<?php
$array = [];
$array["a"] =& $array["b"];
$array["b"] = 1;
var_dump($array);
?>
위와 같은 코드는 PHP 5에서 아래와 같은 결과가 나온다.
array(2) {
["b"]=>
&int(1)
["a"]=>
&int(1)
}
하지만 PHP 7에서는 아래와 같이 나온다.
array(2) {
["a"]=>
&int(1)
["b"]=>
&int(1)
}
global 키워드에서 단순 변수만 사용 가능
이제 global 키워드는 가변 변수(variable variables)와 함께 쓸 수 없다. 꼭 가변 변수를 써야 한다면 중괄호({})에 넣어 쓸 수 있다.
<?php
function f() {
// Valid in PHP 5 only.
global $$foo->bar;
// Valid in PHP 5 and 7.
global ${$foo->bar};
}?>
가능한 한 단순 변수(bare variable)가 아닌 것들은 global 키워드와 같이 쓰지 않는 것이 좋다.
함수의 매개변수를 괄호로 싸는 효과
PHP 5에서는 함수가 참조에 의한 호출을 하는 경우 함수의 매개변수를 괄호로 싸면 경고 메시지를 나오지 않게 할 수 있었다. 하지만 PHP 7에서는 괄호로 둘러싸더라도 경고 메시지가 나온다.
<?php
function getArray() {
return [1, 2, 3];
}function squareArray(array &$a) {
foreach ($a as &$v) {
$v **= 2;
}
}// Generates a warning in PHP 7.
squareArray((getArray()));
?>
위 코드는 PHP 7에서는 아래와 같은 오류 메시지를 출력한다.
Notice: Only variables should be passed by reference in /tmp/test.php on line 13
foreach 동작 방식의 부분 변경
foreach 제어 구문의 동작 방식 중에 바뀐 부분이 있다. 바뀐 것은 내부 배열 포인터에 대한 처리나 반복 처리하고 있는 배열의 변경에 대한 부분이다.
foreach에서 내부 배열 포인터 처리 방식 변경
PHP 7 이전 버전에서는 foreach에서 배열을 반복 처리할 때 내부 배열 포인터를 변경했다. 하지만 PHP 7에서는 변경하지 않는다.
<?php
$array = [0, 1, 2];
foreach ($array as &$val) {
var_dump(current($array));
}?>
위 코드는 PHP 5에서는 아래와 같은 결과가 나온다.
int(1) int(2) bool(false)
하지만 PHP 7에서는 아래와 같은 결과가 나온다.
int(0) int(0) int(0)
foreach 구문에서 값에 의한 동작인 경우 배열의 복사본을 사용
foreach 구문의 기본 동작인 값에 의한 동작인 경우 배열 그 자체가 아니라 배열을 복사해서 복사한 배열을 통해 동작하게 된다. 따라서 처리 도중 원본 배열의 값이 바뀌더라도 반복 처리되는 대상의 값에는 영향을 주지 않는다.
foreach 구문에서 참조에 의한 동작인 경우 동작 방식 변경
참조에 의한 foreach 반복문에서 반복 처리 중인 배열에 대한 변경 추적 방식이 더 나아졌다. 예를 들어, 처리 도중 배열에 새로운 요소가 추가되면 추가된 요소도 반복 처리 대상에 들어가게 된다.
<?php
$array = [0];
foreach ($array as &$val) {
var_dump($val);
$array[1] = 1;
}?>
위 코드는 PHP 5에서는 아래와 같은 결과가 나온다.
int(0)
하지만 PHP 7에서는 아래와 같은 결과가 나온다.
int(0) int(1)
Traversable 인터페이스를 상속하지 않은 객체의 반복 처리
Traversable 인터페이스를 상속하지 않은 객체의 반복 처리 방식이 참조에 의한 반복 처리 방식과 같아졌다. 반복 처리 중에 객체에 속성(property)가 추가되거나 삭제되면 반복 처리에 반영된다.
정수형 값의 처리 방식 변경
유효하지 않은 8진수 리터럴
이전 버전에서는 유효하지 않은 값이 8진수 리터럴에 포함된 경우 그냥 그 값을 무시했다. 예를 들어 0128은 012로 취급했다. 하지만 PHP 7에서는 이런 경우 파서 오류가 발생한다.
비트 시프트에 음수 사용
비트 시프트(bit shift)에 음수(negative)를 사용하면 ArithmeticError 예외가 발생한다.
<?php
var_dump(1 >> -1);
?>
위 코드는 PHP 5에서는 아래와 같은 결과가 나온다.
int(0)
반면 PHP 7에서는 아래와 같은 결과가 나온다.
Fatal error: Uncaught ArithmeticError: Bit shift by negative number in /tmp/test.php:2
Stack trace:
#0 {main}
thrown in /tmp/test.php on line 2
범위가 넘어가는 비트 시프트
어떤 방향으로든 비트 시프트를 할 때 정수형(integer)의 범위를 넘어가면 결과값은 0이 된다. 이전 버전에서는 아키텍처에 따라 달랐다.
0으로 나누었을 때의 변화
이전 버전에서는 나누기 연산자(/)와 나머지 연산자(%)에서 나눗수(제수, divisor)로 0을 쓰면 E_WARNING이 발생하고 false를 반환했다. PHP 7에서는 나누기 연산의 경우 반환값이 실수형(float)으로 +INF, -INF, 혹은 MAN 중 하나가 된다. 나머지 연산에서는 E_WARNING은 발생하지 않고 DivisionByZeroError 예외가 발생한다.
<?php
var_dump(3/0);
var_dump(0/0);
var_dump(0%0);
?>
위 코드는 PHP 5에서는 아래와 같은 결과가 나온다.
Warning: Division by zero in %s on line %d bool(false) Warning: Division by zero in %s on line %d bool(false) Warning: Division by zero in %s on line %d bool(false)
반면 PHP 7에서는 아래와 같은 결과가 나온다.
Warning: Division by zero in %s on line %d float(INF) Warning: Division by zero in %s on line %d float(NAN) PHP Fatal error: Uncaught DivisionByZeroError: Modulo by zero in %s line %d
문자열의 처리 방식 변경
16진수 형태의 문자열을 수치로 취급하지 않음
16진수 숫자를 포함하는 문자열은 더 이상 수치로 취급하지 않는다.
<?php
var_dump("0x123" == "291");
var_dump(is_numeric("0x123"));
var_dump("0xe" + "0x1");
var_dump(substr("foo", "0x1"));
?>
위 코드는 PHP 5에서는 아래와 같은 결과가 나온다.
bool(true) bool(true) int(15) string(2) "oo"
반면 PHP 7에서는 아래와 같은 결과가 나온다.
bool(false) bool(false) int(0) Notice: A non well formed numeric value encountered in /tmp/test.php on line 5 string(3) "foo"
filter_var() 함수를 사용하면 문자열(string)에 16진수 숫자가 있는지 검사할 수 있어 그 문자열을 정수형(integer)으로 바꿀 수 있다.
<?php
$str = "0xffff";
$int = filter_var($str, FILTER_VALIDATE_INT, FILTER_FLAG_ALLOW_HEX);
if (false === $int) {
throw new Exception("Invalid integer!");
}var_dump($int); // int(65535)
?>
\u{ 리터럴에서의 오류
새로운 유니코드 코드포인트 확장 구문(Unicode codepoint escape syntax)이 도입되면서 부적절한 문자열이 뒤에 따라오는 \u{ 리터럴(literal)이 치명적인 오류(fatal error)를 만들 수도 있다. 이 문제를 피하려면 앞에 나오는 역슬래시를 확장하는(escaped) 것이 좋다.
제거된 함수
call_user_method() 함수와 call_user_method_array() 함수
이 함수들은 PHP 4.1.0부터 폐기 예정(deprecated)이었고, call_user_func() 함수와 call_user_func_array() 함수를 대신 사용하도록 권고했다. 이 함수들 대신 가변 함수나 ... 연산자를 쓰는 것도 가능하다.
mcrypt의 별칭
폐기 예정이었던 mcrypt_generic_end() 함수가 제거되었고, 대신 mcrypt_generic_deinit() 함수를 쓸 수 있다. 또한 mcrypt_ecb() 함수, mcrypt_cbc() 함수, mcrypt_cfb() 함수, mcrypt_ofb() 함수도 제거되었으니 적절한 MCRYPT_MODE_* 상수와 함께 mcrypt_decrypt() 함수를 쓰도록 하자.
intl의 별칭
폐기 예정이었던 datefmt_set_timezone_id() 함수와 IntlDateFormatter::setTimeZoneID() 메소드가 제거되었다. 대신 datefmt_set_timezone() 함수와 IntlDateFormatter::setTimeZone() 메소드를 쓰도록 하자.
set_magic_quotes_runtime() 함수
set_magic_quotes_runtime() 함수와 이 함수의 별칭인 magic_quotes_runtime() 함수가 제거되었다. 이 함수들은 PHP 5.3.0에서 폐기 예정이 되었고, PHP 5.4.0에서 특수 인용(magic quotes)이 제거되면서 사실상 무의해졌다.
set_socket_blocking() 함수
폐기 예정이던 set_socket_blocking() 함수가 제거되었다. 대신 stream_set_blocking() 함수를 쓸 수 있다.
PHP-FPM에서 dl() 함수
이제 PHP-FPM에서 dl() 함수를 쓸 수 없다. CLI와 SAPI에서는 아직 쓸 수 있다.
GD Type1 함수
이제 GD 확장 기능에서 더 이상 PostScript Type1 글꼴에 대한 지원을 하지 않는다. 이에 따라 아래 함수들이 제거되었다.
- imagepsbbox()
- imagepsencodefont()
- imagepsextendfont()
- imagepsfreefont()
- imagepsloadfont()
- imagepsslantfont()
- imagepstext()
앞으로는 TryeType 글꼴과 이에 관련된 함수들을 써야 한다.
제거된 INI 지시자
제거된 기능
관련 기능이 제거됨에 따라 아래 INI 지사자도 제거되었다.
xsl.security_prefs
xsl.security_prefs 지시자가 제거되었다. 이 지시자 대신 XsltProcessor::setSecurityPrefs() 메소드를 써서 프로세스 단위로 보안 설정을 해야 한다.
다른 하위 호환성 없는 변경
새로운 객체의 참조에 의한 할당 금지
이제 더 이상 new 구문으로 새로운 객체를 만들어 변수에 참조에 의한 할당을 할 수 없다.
<?php
class C {}
$c =& new C;
?>
위 코드는 PHP 5에서는 아래와 같은 결과가 나왔다.
Deprecated: Assigning the return value of new by reference is deprecated in /tmp/test.php on line 3
하지만 PHP 7에서는 아래와 같은 결과가 나온다.
Parse error: syntax error, unexpected 'new' (T_NEW) in /tmp/test.php on line 3
적절하지 못한 클래스, 인터페이스, 트레이트(trait) 이름
아래 단어는 클래스, 인터페이스, 트레이트의 이름으로 사용할 수 없다.
또 아래 단어들도 사용하지 않는 것이 좋다. 아래 단어들을 쓴다고 해서 PHP 7에서 오류가 나는 것은 아니지만, 다음 버전에서 사용하기로 예약되어 있으니 쓰지 않는 것이 좋다.
ASP 태그와 스크립트 PHP 태그 폐지
ASP 태그와 스크립트 PHP 태그가 폐지되었다.
| 시작 태그 | 종료 태그 |
|---|---|
| <% | %> |
| <%= | %> |
| <script language="php"> | </script> |
비호환 상황에서의 호출 제거
이미 PHP 5.6에서 폐기 예정이 되었던, 비호환 상황에서 static이 아닌 메소드를 static 호출하면 정의되지 않은 $this 변수를 가진 메소드를 호출한 것이 되어 경고가 발생한다.
<?php
class A {
public function test() { var_dump($this); }
}// Note: Does NOT extend A
class B {
public function callNonStaticMethodOfA() { A::test(); }
}(new B)->callNonStaticMethodOfA();
?>
위 코드를 PHP 5에서 실행하면 아래와 같은 결과가 나온다.
Deprecated: Non-static method A::test() should not be called statically, assuming $this from incompatible context in /tmp/test.php on line 8
object(B)#1 (0) {
}
반면 PHP 7에서 실행하면 아래와 같은 결과가 나온다.
Deprecated: Non-static method A::test() should not be called statically in /tmp/test.php on line 8 Notice: Undefined variable: this in /tmp/test.php on line 3 NULL
yield 생성자가 오른쪽 대입 연산자로 변경
이제 yield 생성자에 괄호가 필수가 아니며, 오른쪽 대입 연산자로 바뀌었다. 우선순위는 print와 => 연산자의 중간이다.
<?php
echo yield -1;
// 위 코드는 PHP 5에서 아래와 같이 해석되었다
echo (yield) - 1;
// 하지만 PHP 7에서는 아래와 같이 해석된다
echo yield (-1);
yield $foo or die;
// 위 코드는 PHP 5에서 아래와 같이 해석되었다
yield ($foo or die);
// 하지만 PHP 7에서는 아래와 같이 해석된다
(yield $foo) or die;
?>
이러한 모호함을 없애려면 괄호를 쓰는 것이 좋다.
함수에 같은 이름의 매개변수 사용 금지
이제 함수에서 같은 이름의 매개변수를 쓸 수 없다. 예를 들어 아래와 같이 함수를 정의하면 E_COMPILE_ERROR 오류가 발생한다.
<?php
function foo($a, $b, $unused, $unused) {
//
}?>
switch 문에서 하나 이상의 default 블럭 사용 금지
하나의 switch 문에서 여러 개의 default 블럭을 쓸 수 없다. 아래와 같이 여러 개의 default 블럭을 쓰면 E_COMPILE_ERROR 오류가 발생한다.
<?php
switch (1) {
default:
break;
default:
break;
}?>
$HTTP_RAW_POST_DATA 전역 변수 제거
이제 더 이상 $HTTP_RAW_POST_DATA 전역 변수를 사용할 수 없다. 이 변수 대신 php://input 스트림을 써야 한다.
INI 파일에서 # 형식의 주석 기능 제거
INI 파일에서 #로 시작하는 줄을 주석으로 취급하던 기능이 제거되었다. 이제 ;으로 시작하는 줄만 주석으로 취급한다. 이것은 php.ini 파일뿐 아니라 parse_ini_file() 함수와 parse_ini_string() 함수에서도 그대로 적용된다.
JSON 확장 기능이 JSOND로 대체
JSON 확장 기능이 JSOND로 대체되었다. 이에 따라 하위 호환성을 깨는 두 가지 변경 사항이 있다. 먼저 십진수 표기에서 숫자 마지막에 소수점이 올 수 없다. 즉 34.라고 쓸 수 없고 34.0이나 34로 써야 한다. 두 번째로 지수 표기를 할 때 지수를 나타내는 e가 소수점 다음에 올 수 없다. 즉 3.e3가 아니라 3.0e3이나 3e3으로 써야 한다.
내부 함수의 오버플로우 오류
지금까지는 내부 함수의 반환값이 정수이지만 결과값이 정수로 나타내기 너무 큰 실수인 경우에는 실수를 정수로 바꾼 값을 적당히 잘라낸 후 반환했다. 하지만 이제는 E_WARNING 오류를 내고 NULL 값을 반환한다.
사용자 정의 세션 핸들러 반환값의 변경
사용자 정의 세션 핸들러(custom session handler)에 의해 구현된 함수가 FALSE 혹은 -1을 반환하는 경우 치명적인 오류(fatal error)가 발생한다. 그리고 이러한 함수가 불린값(boolen)이나 -1, 0이 아닌 다른 값을 반환하게 되면 E_WARNING 오류가 발생한다.
func_get_arg() 함수와 func_get_args() 함수가 현재의 매개변수 값을 반환하도록 변경
이전에는 중간에 매개변수 값이 바뀌었더라도 func_get_arg() 함수와 func_get_args() 함수가 원래의 매개변수 값을 반환했다. 하지만 이제는 현재 저장되어 있는 값을 반환한다.
| PHP 7.0 | |
|---|---|
| PHP 5.6 | |
| PHP 5.5 | |
| 확장 기능 | |
| 활용 | |
| 소프트웨어 | |