PHP 부록에 있는 이주 문서를 읽으면서 정리했다. 완전한 번역은 아니며 중요도가 높다고 생각되는 부분을 주로 정리했다. 세세한 부분이나 함수는 각각 문서를 참고하는 것을 권장한다.
목차
- PHP 5.6 (2014년 8월, 공식 문서)
- PHP 7.0 (2015년 12월, 공식 문서)
- PHP 7.1 (2016년 12월, 공식 문서)
- PHP 7.2 (2017년 11월, 공식 문서)
- PHP 7.3 (2018년 12월, 공식 문서)
- PHP 7.4 (2019년 11월, 공식 문서)
- PHP 8.0 (2020년 11월, 공식 문서)
- PHP 8.1 (2021년 11월, 공식 문서)
PHP 5.6
호환성 문제 있는 변경
엄격해진 json_decode()
json_decode()
에서 소문자가 아닌 true
, false
, null
JSON 리터럴을 사용한 경우에는 오류가 발생하도록 변경되었다. 오류는 json_last_error()
로 확인 가능하다.
$json = '{
"is_available": TRUE
}';
$response = json_decode($json);
json_last_error() === JSON_ERROR_SYNTAX; // true
새 기능
상수(constant) 표현식
숫자나 문자열 리터럴, 배열을 상수로 정의할 수 있다.
const ONE = 1;
const TWO = ONE * 2;
const ARR = [ONE, TWO];
class C {
const THREE = TWO + 1;
const ONE_THIRD = ONE / self::THREE;
const SENTENCE = 'The value of THREE is ' . self::THREE;
public function f($a = ONE + self::THREE) {
echo self::SENTENCE;
return $a;
}
}
echo (new C)->f(); // 4
echo C::SENTENCE; // 'The value of THREE is 3'
var_dump(ARR); // [1, 2]
개체도 사용할 수 있다.
class Person {
// ...
}
const ME = new Person('Edward');
var_dump(ME);
// object(Person)#1 (1) {
// ["name":protected]=>
// string(6) "Edward"
// }
ME = new Person('Yong');
// Parse error: syntax error, unexpected token "="
...
연산자 (operator)
함수에서 가변 인자 목록 받기
function school($name, $location = null, ...$students) {
printf('$name: %s, $location: %s, number of students: %d',
$name, $location, count($students));
}
school('Hogwarts School', 'Scotland', 'Harry', 'Ron', 'Hermione');
// $name: Hogwarts School, $location: Scotland, number of students: 3
인자 풀어넣기
배열이나 Traversable
개체를 대상으로 인자를 풀어놓을 때 ...
연산자를 사용할 수 있다. (다른 언어에서는 splat 연산자로 지칭) 이런 문제는 call_user_func_array()
같은 함수로 해결했었는데 더 간단하고 깔끔하게 작성할 수 있게 되었다.
function add($a, $b, $c) {
return $a + $b + $c;
}
$nums = [2, 3];
echo add(1, ...$nums); // 6
**
연산자로 거듭제곱하기
$a = 2 ** 3; // 8
$b = 2;
$b **= 2; // 4
다음 연산 순서를 주의하자.
$a = 2 ** 3 ** 2;
$b = (2 ** 3) ** 2;
$c = 2 ** (3 ** 2);
// $a: 512
// $b: 64
// $c: 512
use function
, use const
함수나 상수도 use
연산자로 불러 사용할 수 있다.
namespace Hello\App {
const NAME = 'hello';
function study() { echo __FUNCTION__; }
}
namespace {
use const Hello\App\NAME;
use function Hello\App\study;
echo NAME; // 'hello'
study(); // 'Hello\App\study'
}
기본 문자열 인코딩
htmlentities()
, html_entity_decode()
, htmlspecialchars()
함수에서 기본 문자열 인코딩을 php.ini에 default_charset
값을 사용한다. 해당 설정은 UTF-8
이 기본값이다.
hash_equals()
시간 차 공격에 안전한 문자열 비교 함수
$expected = crypt('some-password', 'some-unsafe-salt');
$correct = crypt('some-password', 'some-unsafe-salt');
$incorrect = crypt('some-wrong-password', 'some-unsafe-salt');
hash_equals($expected, $correct); // true
hash_equals($expected, $incorrect); // false
비밀번호 관련 함수를 사용할 수 있다면 다음처럼 작성하는 것을 권장한다.
$hash = password_hash('some-password', PASSWORD_DEFAULT);
password_verify('some-password', $hash); // true
password_verify('some-wrong-password', $hash); // false
참고로 password_verify()
함수는 crypt()
함수의 반환값과도 사용할 수 있다.
__debugInfo()
매직 메소드
클래스에 __debugInfo()
를 정의하면 var_dump()
출력을 제어할 수 있다.
class Person {
private $name;
private $secret;
public function __construct($name, $secret) {
$this->name = $name;
$this->secret = $secret;
}
public function __debugInfo() {
return [
'name' => $this->name,
'secret' => '****',
];
}
}
$ed = new Person('Edward', 'have a national treasure');
var_dump($ed);
// object(Person)#1 (2) {
// ["name"]=>
// string(6) "Edward"
// ["secret"]=>
// string(4) "****"
// }
함수 변경점
crypt()
함수 호출 시salt
파라미터가 누락되면 E_NOTICE가 발생.substr_compare()
에length
파라미터로0
을 넣을 수 있음.unserialize()
함수 호출 시 생성자 호출 이전에 직렬화된 데이터를 조작한 시도가 있는 경우 직렬화에 실패하게 됨.
PHP 7.0
호환성 문제 있는 변경
오류/예외 처리 변경
많은 수의 심각한 오류(fatal error)가 예외 처리 형태로 변경되었다. 이 오류 예외는 Error
클래스를 상속하며 Throwable
인터페이스를 구현하고 있다. 직접 구현한 핸들러가 Exception
만 받도록 되어 있다면 Error
를 처리하지 못해서 심각한 오류가 발생할 수 있다.
set_exception_handler()
Throwable
인터페이스를 활용할 수 있다. 호환성을 고려한다면 타입 선언을 제외한다.
// Will break because of `Error`
function handler(Exception $e) { /* ... */ }
set_exception_handler('handler');
// PHP 5 and 7 compatible.
function handler($e) { /* ... */ }
// PHP 7 only.
function handler(Throwable $e) { /* ... */ }
ParseError
eval()
함수에서 오류가 발생한 경우 ParseError
를 catch
로 잡아서 처리할 수 있게 되었다.
변수 사용 변경점
PHP 7부터 abstract syntax tree를 사용하고 있어서 이전에 불가능한 문법을 많이 구현할 수 있게 되었다. 대신 일관성을 유지하기 위해 몇 가지 해석이 달라지는 부분도 생겼다.
// 표현식
$$foo['bar']['baz']
// PHP 5 해석
${$foo['bar']['baz']}
// PHP 7+ 해석
($$foo)['bar']['baz']
// 표현식
$foo->$bar['baz']
// PHP 5 해석
$foo->{$bar['baz']}
// PHP 7+ 해석
($foo->$bar)['baz']
// 표현식
$foo->$bar['baz']()
// PHP 5 해석
$foo->{$bar['baz']}()
// PHP 7+ 해석
($foo->$bar)['baz']()
// 표현식
Foo::$bar['baz']()
// PHP 5 해석
Foo::{$bar['baz']}()
// PHP 7+ 해석
(Foo::$bar)['baz']()
이전과 같은 방식으로 동작하려면 {}
를 사용해서 의미를 더 명확하게 작성해야 한다.
list()
변경점
list()
함수는 원래 역순으로 입력했는데 이제 순서대로 입력된다. 다만 list()
함수의 세부 구현이 변경될 가능성이 있기 때문에 이 함수로 생성한 순서의 의존하는 구현은 권하지 않는다.
list($a[], $a[], $a[]) = [1, 2, 3];
var_dump($a); // [1, 2, 3]
더 이상 문자열을 배열로 변환하는데 사용할 수 없다. str_split()
을 대신 사용한다.
foreach
변경점
foreach
가 배열 커서를 변경하지 않음
$arr = [0, 1, 2];
foreach($arr as &$val) {
echo current($arr); // always 0
}
by-value, by-reference 동작 차이
by-value 반복은 주어진 배열을 복사해서 반복하기 때문에 길이 변화를 인식하지 못한다. 대신 by-reference 반복 중에는 배열에 추가된 항목도 인식한다.
// by-value
$arr = [0];
foreach ($arr as $val) {
var_dump($val);
$arr[1] = 1;
}
var_dump($arr);
// int(0)
// array(2) {
// [0]=>
// int(0)
// [1]=>
// int(1)
// }
// by-reference
$arr = [0];
foreach ($arr as &$val) {
var_dump($val);
$arr[1] = 1;
}
var_dump($arr);
// int(0)
// int(1) <-- 추가된 부분도 인식해서 반복 처리
// array(2) {
// [0]=>
// int(0)
// [1]=>
// int(1)
// }