댓글 검색 목록

[php] PHP 8의 속성

페이지 정보

작성자 운영자 작성일 20-06-05 10:18 조회 998 댓글 0

속성은 마침내 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 속성과 비슷한 기능을 가지고 있습니다.


  • Java는 아마도 가장 인기 있는 것으로 아마도 @Route (name = "/ about")와 비슷한 구문을 가진 주석이 있습니다.
  • Rust는 # [route (name = "/ about")]와 비슷한 구문을 가진 속성을 가지고 있습니다.
  • Python 주석은 데코레이터라고 하며 비슷한 구문을 따릅니다 : @route ( "/ about").

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>>


구문을 선택할 때 좋은 토론과 자전거 흘림이 있었습니다. 제안 된 몇 가지 대안 패턴은 다음과 같습니다.


  • @@ Attribute
  • [[Attribute]]
  • @: Attribute (voted out 41 : 12 in favor of <<Attribute>>)


디자인 목표 


PHP 8 속성을 통해 정보에 편리하게 액세스 할 수 있습니다. 구문과 구현은 사용자가 이미 알고 있는 내용에 구문을 매우 친숙하게 만드는 것입니다.


  • 속성은 클래스 이름으로 해석 될 수 있습니다.
  • 속성은 네임 스페이스가 될 수 있습니다.
  • use 문을 사용하여 속성 클래스 이름을 가져올 수 있습니다.
  • 속성은 0 개 이상의 매개 변수를 가질 수 있습니다.
  • 선언에는 둘 이상의 속성이 있을 수 있습니다.
  • Reflection API에서 속성 인스턴스를 검색 할 수 있습니다.

이 모든 기능은 이 기사의 나머지 부분에서 정교한 예제와 함께 설명됩니다.


네임 스페이스를 사용하고 클래스 이름과 연결하면 속성을 보다 쉽게 ​​재사용 하고 구성 할 수 있습니다. 속성을 폴링 할 때 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 코드 스타일이 없습니다.


개인적 권장 사항은 다음과 같습니다.

  • DocBlock 주석 뒤에는 항상 속성을 배치하십시오.
  • << 및 >> 중괄호 앞뒤에 공백을 두지 마십시오.
  • 함수 호출에 대해 동일한 스타일을 따르십시오. 매개 변수 바로 뒤에 쉼표를 넣고 공백 ( "first", "second")을 남겨 두십시오.

완전한 예 


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을 사용합니다. 시간이 적절하면 모든 속성을 속성으로 업그레이드 할 수 있습니다.



댓글목록 0

등록된 댓글이 없습니다.

웹학교 로고

온라인 코딩학교

코리아뉴스 2001 - , All right reserved.