- 0
- 네모
- 조회 수 710
from. https://velog.io/@qroffle/PHP-%EC%86%8C%EC%8B%9D-Attribute
PHP8.0에 Attribute라는 새로운 친구가 나타났습니다.
PHP8이 출시된지 한참 지났는데, 이제와서... 라는 느낌이 들기는 합니다만!
이상하게 국내에는 이 친구를 소개하는 글이 없더라구요.
Attribute?
Attribute는 클래스, 익명 클래스, 클래스 변수, 클래스 메서드, 함수, 클로져, 상수에 추가할 수 있는 메타데이터 요소입니다.
기존의 DocBlock 주석이 제일 비슷한 용법이 될 수 있겠네요.
/**
* @param string $person
*/
function cute(string $person){}
이런 주석은 @param
이라는 일종의 암묵적인 룰으로 구성되어 있지요.
(엄밀히 따지면 암묵적이지는 않지만요.)
이런 만화책도 있었군요...?
아무튼, 저 코드는 실제로 실행되지는 않지만 PHP에서는 Reflection API 라는 것을 통해 주석을 검색할 수 있도록 하고 있습니다.
/**
* @param string $person
*/
function cute(string $person){}
$reflection = new ReflectionFunction('cute');
$docs = $reflection->getDocComment();
var_dump($docs);
이러한 기능을 아주 알뜰살뜰히 챙긴 라이브러리가 있지요.
이름하야 Doctrine! https://www.doctrine-project.org/
이러한 짭 Annotation 을 활용한 방식은 역시 관리하기 힘들고, 잘 보이지도 않고, IDE가 이쁘게 출력해 주지도 않습니다.
DocBlock 문법을 따르는 암묵적인 룰은 있지만, 말 그대로 암묵적인 룰이니까요.
그래서 PHP8에는 새로운 방식이 추가되었습니다.
Attribute는 이런 정보를 좀 더 펀쿨섹한 방법으로 구현하고 있습니다.
// DocBlock 을 사용하는 방식
class HolymolyController extends AbstractController
{
/**
* @Route('/holymoly')
*/
public function index(){}
}
// Attribute 를 사용하는 방식
class HolymolyController extends AbstractController
{
#[Route('/holymoly')]
public function index(){}
}
사용법
Attribute 적용
#[LoremAttr()]
class Ipsum
{
#[DolorAttr('Sit')]
private string $amet;
#[LoremAttr(), DolorAttr]
public function consectetur()
{
}
}
// Attribute 클래스 선언
#[Attribute]
class LoremAttr
{
public function __construct(){}
}
#[Attribute]
class DolorAttr
{
public function __construct(?string $param1 = null){}
}
생겨난지 얼마 되지 않은 친구라 그런지, PSR 코드 스타일이 아직은 존재하지 않습니다.
개인적으로는 아래의 스타일을 따라 사용하고 있습니다.
- Attribute 는 항상 선언부 바로 위에 (DocBlock 밑에)
#[
와]
사이에 공백을 남기지 않음- 하나의
#[]
블럭 안에 하나의 Attribute 만 선언 - 인자를 전달하지 않아도 되는 Attribute 라도 뒤에
()
를 추가
Attribute 선언
Attribute는 클래스로 선언됩니다. Attribute 클래스의 유효성은 Reflection API 를 통해 정보를 가져올때에만 검사되고, 선언 그 자체로 유효성을 검증받지는 않습니다.
클래스에는 표준 PHP 클래스인 \Attribute
를 Attribute 로 추가해 주어야 합니다.
(\Attribute
는 final
으로 선언되어 있습니다. 확장이 불가능해요.)
#[Attribute]
class LoremAttr
{
}
기본적으로 선언된 Attribute 는 Attribute 를 허용하는 모든 요소에서 사용할 수 있습니다.
그러니까, 클래스, 변수, 함수 등등에서요.
특정 요소에만 적용을 허용하고 싶다면, 아래 방법을 사용할 수 있습니다.
#[Attribute(Attribute::TARGET_CLASS)]
class LoremAttr
{
}
허용되는 인자는 아래와 같습니다.
Attribute::TARGET_ALL
(기본값)Attribute::TARGET_CLASS
(클래스)Attribute::TARGET_METHOD
(클래스 메서드)Attribute::TARGET_PROPERTY
(클래스 멤버변수)Attribute::TARGET_CLASS_CONSTANT
(클래스 상수)Attribute::TARGET_FUNCTION
(함수)Attribute::TARGET_PARAMETER
(인자)
Attribute 획득
Attribute 의 정보는 Reflection API 를 통해 얻어올 수 있습니다.
바로 getAttributes()
메서드를 사용해서요.
모든 Reflection*
클래스에는 getAttributes()
라는 메서드가 추가되었으며,
해당 메서드는 ReflectionAttribute
라는 객체 배열을 반환합니다.
$reflector = new ReflectionClass(Ipsum::class);
$attrs = $reflector->getAttributes();
혹, 특정 Attribute 클래스만을 획득하고 싶다면, getAttributes()
에 인자로 전달해주면 됩니다.
$attrs = $reflector->getAttributes(LoremAttr::class);
ReflectionAttribute
에는 다음과 같은 메서드가 존재합니다.
getName(): string
getArguments(): array
newInstance(): object
newInstance()
newInstance()
메서드는 Attribute 클래스의 인자를 반환합니다.
적용된 Attribute 에 인자가 있다면 해당 인자를 __construct()
로 전달해주는건 당연한 일이구요.
아래 코드와 같이 사용할 수 있습니다.
#[LoremAttr('CuteQroffle')]
class Ipsum {}
#[Attribute]
class LoremAttr
{
private string $message;
public function __construct(string $message)
{
$this->message = $message;
}
}
$reflector = new ReflectionClass(Ipsum::class);
$attrs = $reflector->getAttributes();
foreach($attrs as $attr)
{
var_dump($attr->newInstance());
}
SO EASY!
기타
Doctrine ORM 에서는 이미 Annotation 대신 Attribute 를 사용하여 ORM 을 구성할 수 있게 되었습니다.
https://www.doctrine-project.org/projects/doctrine-orm/en/current/reference/attributes-reference.html
Laravel 에서 Attribute 를 사용하여 라우팅할 수 있도록 하는 라이브러리도 있구요.
개인적으로 제일 마음에 드는 활용입니다. 퍼포먼스 문제가 있느냐 마느냐는 둘째치구요.
https://github.com/spatie/laravel-route-attributes
PHPStorm 에서는 프로젝트 언어를 PHP8 이상으로 설정할 경우, 몇가지 IDE용 Attribute를 추가적으로 제공합니다. 대표적으로 Deprecated 가 있지요.
다만, PHP8을 적용한 프로젝트들은 새로 작성하는 프로젝트가 대부분이라 제 입장에선 크게 와닿지 않네요.