속성(Attribute) 개요

속성(Attribute) 개요

(PHP 8)

PHP 속성(attribute)은 클래스, 메서드, 함수, 매개변수, 프로퍼티, 상수에 붙일 수 있는 구조화된 기계 판독 가능한 메타데이터예요. 런타임에 Reflection API를 통해 내용을 확인할 수 있어서, 코드를 수정하지 않고도 동적인 동작을 만들 수 있죠. 속성은 코드에 메타데이터를 덧붙이는 선언적인 방법을 제공해요.

속성은 어떤 기능의 구현과 그 사용을 분리할 수 있게 해줘요. 인터페이스가 메서드를 강제해서 구조를 정의하는 반면, 속성은 메서드뿐 아니라 함수, 프로퍼티, 상수 등 여러 요소에 메타데이터를 제공해요. 인터페이스가 메서드 구현을 강제하는 것과 달리, 속성은 코드의 구조를 바꾸지 않고 메타데이터만 덧붙이는 방식이에요.

속성은 강제된 구조 대신 메타데이터를 제공해서, 선택적인(optional) 인터페이스 메서드를 보완하거나 대체할 수 있어요. 애플리케이션 안에서 하나의 동작을 나타내는 ActionHandler 인터페이스를 생각해 볼게요. 어떤 구현은 준비(setup) 단계가 필요하고 어떤 구현은 필요 없을 수 있어요. 이때 ActionHandler를 구현하는 모든 클래스가 setUp() 메서드를 정의하도록 강제하는 대신, 속성으로 준비 요구 사항을 표시하면 돼요. 이런 접근 방식은 유연성을 높여 주고, 필요할 때 속성을 여러 번 적용할 수도 있게 해줘요.

예제 #1 속성으로 인터페이스의 선택적 메서드 구현하기

<?php
interface ActionHandler
{
    public function execute();
}

#[Attribute]
class SetUp {}

class CopyFile implements ActionHandler
{
    public string $fileName;
    public string $targetDirectory;

    #[SetUp]
    public function fileExists()
    {
        if (!file_exists($this->fileName)) {
            throw new RuntimeException("File does not exist");
        }
    }

    #[SetUp]
    public function targetDirectoryExists()
    {
        if (!file_exists($this->targetDirectory)) {
            mkdir($this->targetDirectory);
        } elseif (!is_dir($this->targetDirectory)) {
            throw new RuntimeException("Target directory $this->targetDirectory is not a directory");
        }
    }

    public function execute()
    {
        copy($this->fileName, $this->targetDirectory . '/' . basename($this->fileName));
    }
}

function executeAction(ActionHandler $actionHandler)
{
    $reflection = new ReflectionObject($actionHandler);

    foreach ($reflection->getMethods() as $method) {
        $attributes = $method->getAttributes(SetUp::class);

        if (count($attributes) > 0) {
            $methodName = $method->getName();

            $actionHandler->$methodName();
        }
    }

    $actionHandler->execute();
}

$copyAction = new CopyFile();
$copyAction->fileName = "/tmp/foo.jpg";
$copyAction->targetDirectory = "/home/user";

executeAction($copyAction);

SetUp 속성이 붙은 메서드(fileExists(), targetDirectoryExists())는 실행 전에 따로 호출되고, 그다음 execute()가 실행돼요. 이 예제에서 속성은 ActionHandler를 구현하는 클래스가 반드시 가져야 하는 메서드를 강제하는 대신, 준비 단계가 필요한 곳만 표시해 주는 역할을 해요.