PHP 8
2015년 php7 이 등장한 이후 5년만에 php8 이 시동을 걸었다. 아직 베타 버전이지만, 2020년 11월 26일에 정식으로 8.0 버전이 릴리즈 되었으며, 기능은 확정되었고 문법적으로 큰 변화가 있었다. 페이스북의 HHVM(Hiphop Virtual Machine)이 그랬던 것 처럼 JIT(Just In Time) 컴파일러를 도입하여 속도는 크게 향상되었다. 물론 HHVM 보다도 속도는 이미 PHP 7.x 에서 넘어섰기 때문에 이 둘에 대한 비교는 하지 않아도 된다. 이 포스트에서는 대체로 문법적인 부분들을 살펴본다.
https://www.php.net/releases/8.0/en.php
PHP: PHP 8.0.0 Release Announcement
PHP 8.0 is a major update of the PHP language. It contains many new features and optimizations including named arguments, union types, attributes, constructor property promotion, match expression, nullsafe operator, JIT, and improvements in the type system
www.php.net
각 부분에 대한 포스트를 자세하게 작성할 필요는 없겠지만, 속성(a.k.a 어노테이션)같은 경우에는 완전 신 기능이라 이후에 구현이 온전히 되었을 때는 알아봐야 할 지도 모르겠다.
https://www.inflearn.com/course/php8-new-features
PHP 8, 새로운 기능 살펴보기 - 인프런
5년 만에 등장한 PHP 8의 새로운 기능들을 간단하게 살펴봅니다. PHP 8의 신기능이 궁금하다면 한 번 들어보세요! 초급 프로그래밍 언어 PHP 온라인 강의 새롭게 등장한 최신 PHP 8의 기능을 가볍게 ��
www.inflearn.com
https://github.com/pronist/php8-feature-examples
pronist/php8-feature-examples
This Repo contains PHP 8 feature Examples. Contribute to pronist/php8-feature-examples development by creating an account on GitHub.
github.com
JIT(Just In Time)
JIT 는 런타임 중에 코드를 파싱하며 자주 연산되는 부분이나 계산을 처음부터 다시 파싱하는 일이 없도록 만드는 캐시라고 할 수 있다. 똑같이 반복되는 코드에 대해 또 다시 파싱하는 행위는 바람직하지 않은 행위이므로 성능의 저하를 초래했는데, JIT 를 도입하여 그러한 부분을 해결하였다. JIT 는 컴파일러이므로 런타임 중 별도의 IR(Intermediate Representation)을 사용하지 않고 PHP 바이트 코드에서 기계어로 바꾼다음 OPCache 의 공유 메모리에 추가한다. OPCache, JIT 에 대한 일부 설명은 아래의 포스트를 참고하자.
https://pronist.tistory.com/72
PHP: Interpreter, OPCache, ― JIT
이번 시간에는 기존의 포스팅과는 깊이에서 차이가 있다. PHP 를 문법을 넘어 그 너머 살펴보고 코드가 해석되고 실행되는 과정을 살펴본다. 이를 고차원의 관점에서 볼 것이며 그러한 과정이 어
pronist.tistory.com
Trailing Comma 파라매터 목록
Trailing Comma(뭐라고 해석해야?) 파라매터 목록을 허용한다. 단순한 변화이지만, 코딩 스타일 표준에 따라 유동적으로 사용할 수 있다.
class A
{
public function __construct(
string $a,
string $b,
) {}
}
혼합 타입
자료형을 명시해줄 수 있는데, 여기에 여러 타입을 같이 적어줄 수는 없었으나, 이제는 함수나 메서드, 리턴 타입에 다중 타입을 명시해줄 수 있다.
function foo(int|string $bar): int|string {}
https://wiki.php.net/rfc/union_types_v2
타입 확장
여기서 말하는 확장은 슈퍼/서브 클래스를 말하는 것이 아니라, 파라매터가 리턴 타입에 다른 타입도 받을 수 있도록 확장하는 것을 말한다.
interface AInterface
{
public function foo(int $bar): int|string;
}
class A implements AInterface
{
public function foo(int|string $bar): int|string
{
return $bar;
}
}
슈퍼/서브 클래스에서 타입 축소
이번에는 타입을 축소할 수 있다. 슈퍼/서브 클래스 간에는 서브 클래스를 부모 클래스로 교환할 수 있기에 가능한 일이다.
interface BInterface
{
public function foo(B|C $bar): B|C;
}
class B {}
class C extends B {}
class D implements BInterface
{
public function foo(B $bar): B
{
return new B();
}
}
NULL
null
타입을 핸들링하기 위해서는 두 가지 방법을 따른다. 먼저 첫 번째는 유니온 타입을 사용하는 것이고, 두 번째는 타입 앞에 ?
를 사용하는 것이다.
function foo(null|string $bar): void {}
// function foo(?string $bar): void {}
mixed, static 타입 추가
static, mixed
타입이 추가되었는데, 나도 가끔씩 실수로 mixed
타입을 쓸 때가 있었다. mixed
타입은 본래 문서상에는 존재했으나 in Code 에서는 사용할 수 없는 것이었다.
mixed
mixed
타입은 여러 타입을 반환하거나 받을 수 있다. 타입스크립트의 any
같은 느낌이라고나 할까.
function foo(mixed $bar)
{
var_dump($bar);
}
foo('Hello, world');
foo(fn () => 'Hello, world');
https://wiki.php.net/rfc/mixed_type_v2
static
static
타입은 에서도 보았듯, static
키워드를 사용하여 늦은 정적 바인딩을 할 때 유용할 것이다.
class A
{
public function getClass(): static
{
return new static();
}
}
class B extends A
{
}
var_dump((new B())->getClass()); // -> B
https://wiki.php.net/rfc/static_return_type
이 타입은 또한 클래스 프로퍼티나 함수/메서드 파라매터가 될 수 없다. 더군다나 프로퍼티로 쓸 때 정적 프로퍼티를 의미하는 건지 타입을 의미하는 건지 알 수 없기 때문인데, 정적 프로퍼티이면서 정적 타입이라면 static static
으로 쓰는건 어떤가?
따라서 아래의 문법은 허용하지 않는다.
class C extends A
{
// Is this an untyped static property,
// or an instance property of type static?
public static $a;
// FUNCTION PARAMS
public function foo(static $a): void
{
}
}
self
여담으로 static
타입은 self
타입의 서브 타입이다. 따라서 self
를 리턴 타입으로 쓰면 static
타입도 반환할 수 있지만, static
타입만 명시하면 self
는 반환할 수 없다.
class A
{
public function getClass(): self
{
return $this;
}
}
class B extends A {}
var_dump((new B())->getClass()); // -> B
nullsafe 연산자
객체에서 어떤 메서드나 프로퍼티에 접근할 때, 객체가 null
인 경우 에러를 던졌었으나, nullsafe
연산자를 사용하면 이것을 방지하고 객체가 null
인경우, 말 그대로 null
을 리턴한다. 동적타입을 가졌고, 런타임 동안에 null
이 될 수도 있기 때문에 이를 방지할 수 있다는 것은 아주 좋은 기능이라고 생각한다.
$class = null;
var_dump($class?->getMessage()); // -> NULL
https://wiki.php.net/rfc/nullsafe_operator
명명된 파라매터
파이썬이나 다른 언어처럼 키워드 파라매터를 사용할 수 있다. 코드의 가독성이 한결 상승할 것은 물론 기본값과 섞여있을 때 빛을 발휘하게 될 예정이다. 굳이 기본값에 있는 내용에 값을 채운다거나 파라매터의 순서를 신경쓰면서 함수를 만들 필요가 없어진다.
function foo(string $bar): void
{
}
foo(bar: 'Hello, world');
https://wiki.php.net/rfc/named_params
속성(a.k.a 어노테이션)
어노테이션이 추가된다. 따라서 자바처럼 어노테이션 단위로 기능을 분리하여 나타낼 수 있게된다. PHP 8.0 Beta4 버전에서는 파스 에러를 뱉는데, 그 이유는 아직 문법적으로 명확하게 확정이 되지 않았기 때문인 듯 보인다.
https://wiki.php.net/rfc/shorter_attribute_syntax_change
PHP: rfc:shorter_attribute_syntax_change
PHP RFC: Shorter Attribute Syntax Change Introduction With the continued discussion over the currently selected attribute syntax @@ from the Shorter Attribute Syntax RFC we want to revisit the syntax choice once and for all, so that we can be as sure as po
wiki.php.net
RFC(Request for Comments)를 보면 여전히 표현의 선택을 고민하고 있다. 따라서 속성은 추가는 되겠지만 표현의 방법에 있어서 어떠한 것이 선택될지는 아직 모른다. 후보로는 #[], @@
등이 있는 것 같다. 다른 언어에서 쓰고 있는 @
는 이미 에러와 관련된 연산자로 예약이 되어 있다.
class PhpAttribute
{
public const int IS_REPEATABLE = ((1 << 10));
}
<<PhpAttribute(self::IS_REPEATABLE)>>
class Route
{
}
class HomepageController
{
<<Route("/")>>
<<Route("/homepage")>>
public function indexAction()
{
}
}
https://wiki.php.net/rfc/attributes_v2
match 표현식
match
표현식은 switch ~ case
문을 조금 더 보기좋게 표현한다. 다른 언어에서는 본 적이 없으나 이러한 표현식은 구문을 단축하고 가독성을 높히는데 도움을 준다. 참고로 break
키워드는 사용되지 않는다. 또한 swtich ~ case
문과는 다르게 값을 리턴한다는 특징이 있다. 이 표현식은 단일 라인만 사용이 가능하다.
$errorCode = 400;
function getErrorCode() {
return 400;
}
// switch ($errorCode) {
// case getErrorCode():
// $msg = 'Bad Reqeust';
// break;
// default:
// $msg = 'Hello, world';
// }
$msg = match ($errorCode) {
getErrorCode() => 'Bad Request',
200, 201, 202, 203 => 'OK',
default => 'Hello, world'
};
var_dump($msg); // -> Bad Request
https://wiki.php.net/rfc/match_expression_v2
UnhandledMatchError
매치가 올바르게 이루어지지 않은 경우 매치에 대한 UnhandledMatchError
가 발생한다.
// UnhandledMatchError
match (500) {
200, 201, 202, 203 => 'OK'
};
생성자
생성자를 사용할 때도 단축해서 사용할 수 있도록 변경되었다. 자바스크립트처럼 된 듯하다. 생성자 파라매터를 줄 때 프로퍼티까지 같이 선언할 수 있는 것이다. 참고로 타입은 필수가 아니며, 기본값을 넣어줄 수도 있고, 일반적인 생명자 파라매터를 만들어 줄 수도 있다.
// class MyClass
// {
// public string $message;
// public function __construct(string $message)
// {
// $this->$message = $message;
// }
// }
class MyClass
{
public function __construct(public string $message)
{
}
}
https://wiki.php.net/rfc/constructor_promotion
함수에서 예외 반환하기
이전까지는 할 수 없었지만, 이제부터는 예외를 반환할 수 있게되었다. 이는 내가 가끔 실수로 적었다고 수정하는 것들인데 합법적으로 할 수 있게 되었다.
function foo()
{
return throw new Exception();
}
$foo = fn () => throw new Exception();
https://wiki.php.net/rfc/throw_expression
WeakMap
WeakMap
은 WeakReference
처럼 객체의 참조만을 가지고 있어서 키로 가지고 있는 객체를 메모리에서 해제할 경우 WeakMap
객체 내부에서도 날라가게 된다. Cache 로 사용하는 것이 가장 무난한 예제다.
$weakMap = new WeakMap();
$class = new stdClass();
$weakMap[$class] = 'Hello, world';
/*
[0]=>
array(2) {
["key"]=>
object(stdClass)#2 (0) {
}
["value"]=>
string(12) "Hello, world"
}
}
*/
var_dump($weakMap);
unset($class);
/*
object(WeakMap)#1 (0) {
}
*/
var_dump($weakMap);
https://wiki.php.net/rfc/weak_maps
오브젝트에서 ::class 사용하기
본래는 클래스 자체를 대상으로 할 수만 있었던 것을, 이제 오브젝트에서도 ::class
를 사용할 수 있게된다.
$class = new stdClass();
var_dump($class::class); // -> stdClass
https://wiki.php.net/rfc/class_name_literal_on_object
try ~ catch 에서 변수 생략하기
try ~ catch
를 사용할 때 본래 catch
블럭에 변수를 넣어야 했는데, 생략할 수 있도록 바뀌었다.
try {
throw new Exception();
} catch (Exception) {
}
https://wiki.php.net/rfc/non-capturing_catches
Stringable 인터페이스
Stringable
인터페이스가 추가되었다. 해당 인터페이스는 별거없고, 그냥 매직메서드 __toString()
을 통해 객체가 스트링으로 표현될 수 있냐에 따른 것이다.
class MyClass implements Stringable
{
public function __toString(): string
{
return 'Hello, world';
}
}
echo new MyClass(); // -> Hello, world
https://wiki.php.net/rfc/stringable
참고 자료
참고 자료에는 내가 포스트에 적지 않은 내용이나 자잘한 변화가 나열되어 있다. 새로운 내장 함수의 추가나 작은 변화들을 더 살펴보고 싶다면 아래의 문서를 참고하길. 물론 공식 문서는 내용이 너무 길어서 이 글을 썼다. 영어가 싫진 않지만 길어지니까 싫어진다. RFC(Request for Comments) 문서를 볼 때는 PHP 8 섹션에서 찾아보면 된다.
- php/php-src: https://github.com/php/php-src/blob/master/UPGRADING
- PHP: rfc: https://wiki.php.net/rfc
- What's new in PHP 8: https://stitcher.io/blog/new-in-php-8
더 읽을거리
https://www.inflearn.com/course/php7-reboot