Skip to content

Instantly share code, notes, and snippets.

@wan2land
Last active June 6, 2017 16:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save wan2land/9e3d09960fb99e6a428f29d2c4dc1864 to your computer and use it in GitHub Desktop.
Save wan2land/9e3d09960fb99e6a428f29d2c4dc1864 to your computer and use it in GitHub Desktop.

Annotation (feat. Reflection)

무엇인가?

  1. 메타프로그래밍 할 때 필요한 그거.    - 자세한 설명은 검색을.. 어디에 활용하면 좋을지는 함께 고민해보아요.
  2. 자바에 있는 그거.

Q. PHP가 그걸 지원해요?

A. 언어자체에서는 지원하지 않지만 뭐,, 어떻게든 그게 됩니다.

https://packagist.org/packages/doctrine/annotations

설치하기

composer require doctrine/annotations

사용준비

오토로드에 등록해야합니다.

use Doctrine\Common\Annotations\AnnotationRegistry;

$autoload = require __DIR__ . '/vendor/autoload.php';
AnnotationRegistry::registerLoader([$autoload, 'loadClass']);

그리고 어노테이션을 추가합니다. Target 은 총 3가지입니다.

  1. CLASS
  2. PROPERTY
  3. METHOD

각각 다음과 같이 정의할 수 있습니다.

use Doctrine\Common\Annotations\Annotation\Required;
use Doctrine\Common\Annotations\Annotation\Target;

/**
 * @Annotation
 * @Target({"CLASS"})
 */
class ClassAnnotated
{
    /**
     * @Required
     */
    public $name;
}

/**
 * @Annotation
 * @Target({"PROPERTY"})
 */
class PropertyAnnotated
{
    /**
     * @Required
     */
    public $name;
}

/**
 * @Annotation
 * @Target({"METHOD"})
 */
class MethodAnnotated
{
    /**
     * @Required
     */
    public $name;
}

어노테이션을 사용하는 클래스.

/**
 * @ClassAnnotated("this is user class maybe")
 */
class User
{
    /**
     * @PropertyAnnotated("this is username property maybe")
     */
    protected $username;

    /**
     * @MethodAnnotated("this is constructor maybe")
     */
    public function __construct($username)
    {
        $this->username = $username;
    }
}

Annotation 불러오는 로직.

use Doctrine\Common\Annotations\AnnotationReader;

$reader = new AnnotationReader();

$classRefl = new ReflectionClass(User::class);
$propertyRefl = $classRefl->getProperty("username");
$methodRefl = $classRefl->getMethod("__construct");

print_r($reader->getClassAnnotations($classRefl));
print_r($reader->getPropertyAnnotations($propertyRefl));
print_r($reader->getMethodAnnotations($methodRefl));

(소스실습)

IDE 지원

  1. 개발자가 가져야할 최고의 덕목은 게으름!!
  2. 일일이 외우지 말고 IDE를 활용합시다.
  3. 하지만, PHPStorm에서만.. 되요.

ide-support

(자동완성실습)

느리지 않을까요?

일반적인 프로그램밍 언어는 다음 과정을 거치게 됩니다.

  1. 토큰분석
  2. 문법분석
  3. AST 생성 -> PHP로 이야기하면 이부분이 Opcache로. (아마도)

PHP를 생각해봅시다.

파일 바꾸고 첫번째 리퀘스트는 오래걸리지만 그 다음 부터는 캐시때문에 빠르게 처리합니다. 즉, 문법 분석 비용이 가장 큽니다. 캐시만 사용해도 성능을 충분히 끌어올릴 수 있습니다.

추가 패키지를 설치합니다.

composer require doctrine/cache

기존 소스를 다음으로 교체.

doctrine에는 엄청나게 많은 캐시를 제공해줍니다.

use Doctrine\Common\Annotations\AnnotationReader;
use Doctrine\Common\Annotations\CachedReader;

$reader = new CachedReader(new AnnotationReader(), new Doctrine\Common\Cache\FilesystemCache(__DIR__));

Reflection의 속도 자체는 괜찮은가?

  1. PHP에서 클래스를 생성할 때, PHP 내부에서는 zend_class_entry라는 구조체가 선언이 됩니다.
  1. Reflection 객체 생성 = 이미 생성되어있는 zend_class_entry 구조체 연결 객체 생성
  2. 즉, 일반 객체 생성비용과 거의 비슷합니다.

어려우니까 그냥 진짜 그런지 확인하고 갑시다.

class Dummy {}
class HelloWorld
{
    protected $name;
    public function __construct($name)
    {
        $this->name = $name;
    }
    public function getName()
    {
        return $this->name;
    }
}

$benchmark->add("reflection", function () {
    $reflection = new ReflectionClass(Dummy::class);
    $reflection->getName();
});

$benchmark->add("basic", function () {
    $reflection = new HelloWorld(Dummy::class);
    $reflection->getName();
});

$benchmark->run();

결과는..?

결론

  1. Annotation 재밌으니 사용해봅시다.
  2. symfony 강추추, doctrine 강추추.
  3. Reflection 안느려요.

좋은 소스들

기타

PHP RFC에 거의 매년 Annotation 관련 논의가 올라옵니다. 물론 이것도 반영 안되겠지만.. PHP8정도 되면 혹시 적용되지 않을까요? :-)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment