속성은 마침내 PHP 8에 있습니다! 수년 간의 토론, 기능 요청 및 Doctrine Annotations와 같은 사용자 랜드 구현이 끝나면 PHP 8에 대한 속성 제안이 최종 승인됩니다!
이 게시물은 속성, 엣지 케이스 및 히스토리에 대한 자세한 안내서와 기존 주석을 속성으로 업그레이드하는 데 대한 실제 안내서입니다.
https://php.watch/articles/php-attributes
속성은 무엇인가
속성은 PHP 클래스, 함수, 클로저, 클래스 속성, 클래스 메서드, 상수 및 익명 클래스에 추가 된 작은 메타 데이터 요소입니다.
PHP DocBlock 주석은 아마도 가장 친숙한 예일 것입니다.
/**
* @param string $message
*/
function foo(string $message) {}
이 주석은 @param "주석"으로 다소 구조화되어 있습니다. 이러한 작은 비트는 실행되지 않지만 PHP는 "Reflection API"라는 API를 제공하여 이러한 주석을 편리하게 검색합니다.
이 접근 방식은 오타가 쉽고 코드에서 다른 곳에서 주석을 가져올 때까지 눈에 띄지 않기 때문에 취하기가 쉽지 않습니다.
PHP 8에 추가 된 새로운 접근 방식은 속성입니다. 속성은 이러한 작은 정보를 선언하고 가져 오는 보다 실용적인 접근 방식을 제공합니다.
Drupal, Symfony 및 Doctrine과 같은 프레임 워크는 주석을 사용하여 특정 클래스에 대한 보조 정보를 체계적으로 제공합니다.
class AboutPage extends AbstractController {
/**
* @Route("/about")
*/
public function page() {}
}
DocBlock 주석을 피기 백하면 이 주석은 AboutPage 클래스에 대한 유용한 정보를 제공합니다. 프레임 워크에서 "/about"경로를 AboutPage :: page 메서드로 라우팅 하기 위해 라우터 항목으로 전환 될 수 있습니다.
PHP 8의 속성은 이것보다 한발 앞서서 주석에 대한 체계적이고 엔진 검증 된 접근 방식을 제공합니다.
class AboutPage extends AbstractController {
<<Route('/about')>>
public function page() {}
}
속성은 XML 스키마 또는 JSON 스키마 형식으로 별도의 정의를 작성하는 대신이 메타 데이터를 구성하기 쉽고 관리하기 쉬운 방법을 제공합니다.
다른 언어로 된 속성
많은 언어는 PHP 속성과 비슷한 기능을 가지고 있습니다.
PHP의 기존 Doctrine-esque가 널리 사용되지만 PHP 8의 속성은 << 및 >> 구문을 사용하는데, 다른 언어에서는 사용되지 않지만 그럼에도 불구하고 동일한 의미 기능을 제공합니다.
속성 대 주석
속성과 주석은 동일한 기능을 제공합니다. "Annotations"라는 단어는 이미 PHP 라이브러리와 프레임 워크에서 널리 사용되므로 Attributes라는 이름은 Annotations와의 혼동을 최소화하는 데 도움이 됩니다.
이전 시도
이 기능을 PHP로 가져 오려는 이전의 두 가지 시도가 있었습니다.
첫 번째는 약 8 년 전에 "주석(annotations)"이라는 제안이 있습니다.
2016 년에 Dmitry Stogov가 첫 번째 속성 RFC를 제안했습니다.
이 시도들 중 어느 것도 유익하지 않았습니다. 첫 번째 속성 RFC는 실제로 PHP 8과 동일한 구문을 제안했지만 PHP 8로 축소 한 두 번째 RFC는 조금 더 정교했으며 Benjamin Eberlei는 사소한 세부 사항을 다루고 건전한 토론을 하기 위해 놀라운 노력을 기울였습니다. 구문과 기능에 동의하는 커뮤니티.
속성 구문 및 기능
속성 구문은 단순히 두 개의 작거나 큰 부호로 만든 중괄호입니다.
<<Attribute>>
구문을 선택할 때 좋은 토론과 자전거 흘림이 있었습니다. 제안 된 몇 가지 대안 패턴은 다음과 같습니다.
디자인 목표
PHP 8 속성을 통해 정보에 편리하게 액세스 할 수 있습니다. 구문과 구현은 사용자가 이미 알고 있는 내용에 구문을 매우 친숙하게 만드는 것입니다.
이 모든 기능은 이 기사의 나머지 부분에서 정교한 예제와 함께 설명됩니다.
네임 스페이스를 사용하고 클래스 이름과 연결하면 속성을 보다 쉽게 재사용 하고 구성 할 수 있습니다. 속성을 폴링 할 때 Reflection API가 편리한 필터 기능을 제공하는 인터페이스를 확장 및 / 또는 구현할 수 있습니다.
속성은 클래스 이름으로 해석 될 수 있습니다
필수는 아니지만 PHP 8은 속성 이름을 클래스 이름으로 해석하는 기능을 제공합니다. use 문을 사용하여 코드를 정리할 수 있습니다. 클래스 이름 확인의 표준 규칙이 준수됩니다.
속성 이름을 클래스 이름과 일치 시키는 것은 선택 사항입니다.
속성은 매개 변수를 가질 수 있습니다
각 속성은 0 개 이상의 매개 변수를 가질 수 있습니다. 속성의 인스턴스화 된 객체를 얻으려고 시도하면 속성 클래스 생성자로 전달됩니다.
매개 변수는 간단한 스칼라 유형, 배열 또는 수학 표현식, PHP 상수, 클래스 상수 (마법 상수 포함)와 같은 간단한 표현식 일 수 있습니다. 클래스 상수로 사용할 수 있는 모든 표현식을 Attribute 매개 변수로 사용할 수 있습니다.
둘 이상의 속성이 허용됨
속성을 받는 각 항목은 각각 고유 한 << >> 괄호 안에 0 개 또는 많은 속성을 가질 수 있습니다.
각 속성은 공백 (새 줄 또는 공백)으로 구분할 수 있습니다.
DocBlock 주석 전후
DocBlock 주석 전후에 속성이 나타날 수 있습니다. 코드 스타일에 대한 표준 권장 사항은 없지만 향후 PSR 코드 스타일 권장 사항에서 해결 될 것입니다.
속성 예
광범위한 선언에 속성을 추가 할 수 있습니다.
함수
<<Attribute('foo')>>
function example(){}
클래스
<<Attribute('foo')>>
class Example {}
함수 / 메소드 인수
function example(<<Attribute('foo')>> string $foo) {}
클래스 속성
class Foo {
<<Attribute('foo')>>
private string $foo;
}
클래스 상수
class Foo {
<<Attribute('foo')>>
private const FOO = 'foo';
}
클로저
$fn = <<Attribute('foo')>> fn() => 1 > 2;
$fn = <<Attribute('foo')>> function() {
return 1 > 2;
}
익명 클래스
$instance = new <<Attribute('foo')>> class {};
DocBlocks로
DocBlock 주석 전후에 속성을 배치 할 수 있습니다.
<<AttributeBefore('foo')>>
<<AttributeBefore2('foo')>>
/**
* Foo
*/
<<AttributeAfter('foo')>>
function example() {}
코드 스타일
구문이 아직 새롭기 때문에 속성에 대해 동의 된 PSR 코드 스타일이 없습니다.
개인적 권장 사항은 다음과 같습니다.
완전한 예
use App\Annotations\FooAttribute;
use App\Annotations\ClassAttrib as FooClassAttrib;
use App\Annotations\FooParamAttrib;
use External\Attr\BarClassAttrib;
<<App\Annotations\FooAttribute>>
function foo_func(<<FooParamAttrib('Foo1')>> $foo) {}
<<FooAttribute('hello')>>
<<BarClassAttrib(42)>>
class Foo {
<<ConstAttr>>
<<FooAttribute(null)>>
private const FOO_CONST = 28;
private const BAR_CONST = 28;
<<PropAttr(Foo::BAR_CONST, 'string')>>
private string $foo;
<<SomeoneElse\FooMethodAttrib>>
public function getFoo(<<FooClassAttrib(28)>>): string{}
}
<< PhpAttribute >>가 있는 속성 객체
위에서 언급 한 바와 같이, 속성은 PHP 클래스 이름으로 해석 될 수 있습니다. 특별한 <<PhpAttribute>> 속성을 사용하여 클래스를 선언하여 클래스를 선언 할 수 있습니다.
<<PhpAttribute>>
class FooAttribute {
public function __construct(?string $param1 = null) {}
}
원하는 네임 스페이스에서 이 클래스를 선언 할 수 있습니다. 호출자가 속성 클래스의 인스턴스를 요청하면 속성의 매개 변수가 클래스 생성자로 전달됩니다.
위에서 선언 된 속성은 이제 속성 선언의 어느 곳에서나 사용할 수 있습니다.
<<FooAttribute('Hello World!')>>
function example() {}
속성에 대한 리플렉션 API
Reflection API를 사용하여 속성을 검색합니다. PHP 엔진은 속성이 포함 된 코드를 구문 분석 할 때 나중에 사용할 수 있도록 내부 구조에 저장됩니다. Opcache 지원이 포함되었습니다. Attribute의 인스턴스가 요청되지 않으면 코드를 실행하거나 속성의 생성자를 호출하지 않습니다 (아래 예 참조).
Reflection API를 사용하면 속성 이름 (클래스 이름이 확인 된)이 포함 된 문자열 및 선택적 인수로 속성을 검색 할 수 있습니다.
Reflection API는 클래스 이름을 확인하고 자동로드 하며 선택적 매개 변수를 클래스 생성자에 전달하여 Attribute 클래스의 인스턴스를 인스턴스화 할 수도 있습니다. 클래스를 인스턴스화 하지 않으면 호출자 레벨에서 발견 될 수 있는 \Error 예외가 발생합니다.
새로운 리플렉션 * :: getAttributes () 메소드
$reflector = new \ReflectionClass(Foo::class);
$reflector->getAttributes();
모든 Reflection * 클래스는 ReflectionAttribute 객체의 배열을 반환하는 새로운 메소드 getAttributes 메소드를 얻습니다. 이 새로운 방법의 개요는 다음과 유사합니다.
/**
* @param string $name Name of the class to filter the return list
* @param int $flags Flags to pass for the filtering process.
* @return array ReflectionAttribute[]
*/
public function getAttributes(?string $name = null, int $flags = 0): array {}
ReflectionAttribute 클래스 개요
final class ReflectionAttribute {
/**
* @return string The name of the attribute, with class names resolved.
*/
public function getName(): string {}
/**
* @return array Arguments passed to the attribute when it is declared.
*/
public function getArguments(): array {}
/**
* @return object An instantiated class object of the name, with arguments passed to the constructor.
*/
public function newInstance(): object {}
}
속성 필터링
Reflection * :: getAttributes()는 선택적으로 특정 속성 이름으로 속성의 반환 배열을 필터링 하는 데 사용할 수 있는 클래스 이름 문자열을 허용합니다.
$attrs = $reflector->getAttributes(FooAttribute::class);
$attrs 배열은 이제 ReflectionAttribute 객체 또는 FooAttribute 속성 이름만 됩니다.
두 번째 선택적 매개 변수는 정수를 허용하여 리턴 배열을 추가로 미세 조정합니다.
$attrs = $reflector->getAttributes(BaseAttribute::class, \ReflectionAttribute::IS_INSTANCEOF);
현재는 \ ReflectionAttribute :: IS_INSTANCEOF 만 사용할 수 있습니다.
\ReflectionAttribute :: IS_INSTANCEOF가 전달되면, 반환 배열은 제공된 이름 (즉, instanceOf $ name을 수행하는 모든 클래스)을 확장하거나 구현하는 동일한 클래스 이름 또는 클래스를 가진 Attribute를 포함합니다.
속성 객체 인스턴스 검색
ReflectionAttribute :: newInstance 메서드는 Attribute 객체 클래스 생성자에 전달 된 매개 변수와 함께 Attribute 클래스의 인스턴스를 반환합니다.
완전한 Reflection 예
use My\Attributes\ExampleAttribute;
<<ExampleAttribute('Hello world', 42)>>
class Foo {}
<<PhpAttribute>>
class ExampleAttribute {
private string $message;
private int $answer;
public function __construct(string $message, int $answer) {
$this->message = $message;
$this->answer = $answer;
}
}
$reflector = new \ReflectionClass(Foo::class);
$attrs = $reflector->getAttributes();
foreach ($attrs as $attribute) {
$attribute->getName(); // "My\Attributes\ExampleAttribute"
$attribute->getArguments(); // ["Hello world", 42]
$attribute->newInstance();
// object(ExampleAttribute)#1 (2) {
// ["message":"Foo":private]=> string(11) "Hello World"
// ["answer":"Foo":private]=> int(42)
// }
}
실용 사례
속성 기능은 클래스 이름과 직접 연관 될 수 있고 클래스 이름 확인 기능이 내장되어있어 정적 분석기와 IDE가 속성에 대한 지원을 쉽게 추가 할 수 있기 때문에 매우 강력합니다.
Java 또는 Rust에서 속성 / 주석을 사용하는 사용자에게는 구문이 확실하지 않을 것입니다. RFC의 구현 세부 사항에 대해 지불하는 작은 가격입니다. 전보다 어색한 구문이 더 낫습니다.
속성은 이전 버전과 호환되지 않습니다. 새로운 구문으로 인해 8.0 이전의 PHP 버전에서는 구문 분석 오류가 발생합니다.
Parse error: syntax error, unexpected '<<' (T_SL), expecting end of file in ... on line ...
교리 주석에서 속성으로 마이그레이션
프로젝트에서 PHP 8을 최소 버전으로 사용할 수있는 경우 Doctrine-esque Annotations를 일류 PHP 속성으로 업그레이드 할 수 있습니다.
- /** @ORM\Entity */
+ <<ORM\Entity>>
class Book {
- /**
- * @ORM\Id
- * @ORM\Column(type="string")
- * @ORM\GeneratedValue
- */
+ <<ORM\Id>>
+ <<ORM\Column("string")>>
+ <<ORM\GeneratedValue>>
private string $isbn;
}
미래의 속성
인터페이스로 이상적으로 "표시"되지 않은 많은 PHP 기능의 특성은 속성이 될 수 있습니다.
속성 제안에서 속성을 사용하여 JIT와 호환 가능하거나 호환되지 않는 선언을 표시하는 것에 대해 언급합니다.
또 다른 유스 케이스는 엔진 및 사용자 랜드 클래스 / 함수 또는 더 이상 사용되지 않는 것을 선언하는 데 사용할 수 있는 << Deprecated >> 속성입니다. 결국 @deprecated DocBlock 주석을 폐기 할 수 있습니다.
Drupal과 Symfony는 컨트롤러, 플러그인, 렌더 블록 등에 Doctrine Annotation을 사용합니다. 시간이 적절하면 모든 속성을 속성으로 업그레이드 할 수 있습니다.
등록된 댓글이 없습니다.