Reflection API를 알게 된 방법

13845 단어 phpwebdev
재미를 위한 코딩은 때때로 예상치 못한 지역으로 당신을 이끌 수 있습니다. 이것이 이번 주에 나에게 일어난 일입니다. 나는 이전에 마이크로 프레임워크Framework-X에 대해 글을 썼고 이번 주에 마이크로 프레임워크로 또 다른 프로젝트를 구축하고 있었고 그것을 데이터베이스와 연결할 필요가 있었습니다. Framework-X는 비동기 및 비차단 실행을 지원합니다. 이러한 이유로 프로젝트는 가능한 동안 비차단 데이터베이스 액세스를 권장합니다. 따라서 Doctrine를 사용하는 것이 절대적으로 가능하지만 Framework-X가 승격하는 반응 방식이 아닙니다. 그래서 이것은 데이터베이스와 통신하는 보다 "실제적인"접근 방식을 탐구하기 위해 Doctrine의 알려진 근거를 떠나게 했습니다.

그래서 나는 어쨌든.

저로서, 저는 새로운 주제를 탐구하고 현재 능력으로 얼마나 갈 수 있는지 확인하는 것을 좋아합니다. 나는 내가 한 모든 것이 거의 독점적으로 "객체 지향"이었던 배경에서 왔지만 지금은 Doctrine (인기있는 PHP ORM ) 없이 남겨졌습니다. 비슷한 (그러나 훨씬 더 단순한) 내 자신의 기능.

나는 내 프로젝트에서 미니 ORM을 구축할 것이라고 생각하지 않았지만 그것이 내가 나 자신을 발견한 곳입니다. 나는 엔터티 클래스 또는 "일반 PHP 개체"(POPO)로 설명할 수 있는 항목이 있습니다. 이러한 POPO는 유형을 사용하여 고유한 속성을 설명합니다. 이러한 유형은 다른 클래스(다대일)의 배열로 반영되는 일대다 관계뿐만 아니라 다른 엔터티에 대한 관계를 설명할 수 있습니다. 현재 프로젝트에서 다대다 관계가 없습니다. 따라서 POPO는 다음과 같이 보일 수 있습니다.

<?php 

namespace App\Entity;

class Category
{
    /**
     * @var string $uuid - UUID version 4 variant 1
     */
    private string $uuid = '';

    private string $name = '';

    /**
     * @var Category[] $childCategories
     */
    private array $childCategories = [];

    private ?Category $parentCategory = null;

    public function setUuid(string $uuid = ''): self
    {
        $this->uuid = $uuid;
        return $this;
    }

    public function getUuid(): string
    {
        return $this->uuid;
    }

    public function setName(string $name): self
    {
        $this->name = $name;
        return $this;
    }

    public function getName(): string
    {
        return $this->name;
    }

    public function setChildCategories(array $childCategories): self
    {
        $this->childCategories = $childCategories;
        return $this;
    }

    public function getChildCategories(): array
    {
        return $this->childCategories;
    }

    public function addChildCategory(Category $childCategory): self
    {
        if (!$this->hasChildCategory($childCategory)){
            $this->childCategories[] = $childCategory;
        }
        return $this;
    }

    public function removeChildCategory(Category $childCategory): self
    {
        $this->childCategories = array_filter($this->childCategories, function ($category) use ($childCategory) {
            return $category->getUuid() !== $childCategory->getUuid();
        });

        return $this;
    }

    public function hasChildCategory(Category $childCategory): bool
    {
        return in_array($childCategory, $this->childCategories);
    }

    public function setParentCategory(Category $parentCategory): self
    {
        $parentCategory->addChildCategory($this);
        $this->parentCategory = $parentCategory;
        return $this;
    }

    public function getParentCategory(): ?Category
    {
        return $this->parentCategory;
    }
}


이와 같은 POPO를 사용하면 이러한 개체를 데이터베이스에 저장할 수 있을 뿐만 아니라 데이터베이스에서 결과를 가져와 개체로 되돌릴 수 있기를 원했습니다. 이것이 나를 위해 작동하려면 각 속성이 어떤 유형인지 알아야 했습니다. 배열의 경우 배열 내부에 있는 객체의 유형을 알아야 했습니다. 일반 속성에는 단순히 명시된 유형이 있습니다. 배열의 경우 @var 주석을 사용합니다.

    /**
     * @var Category[] $childCategories
     */
    private array $childCategories = [];


각 속성에 대해 getter와 setter도 있습니다. 이 모든 것을 알면 데이터베이스에 저장하려고 할 때 모든 관련 속성을 얻을 수 있습니다. 클래스에 대한 충분한 통찰력을 통해 각 속성을 해당 SQL 유형으로 바꿀 수 있습니다. 예를 들어 MySQL의 경우 문자열을 VARCHAR로, 참조를 (UUID를 사용하는 경우) VARCHAR로 변환할 수 있습니다. 열쇠.

그러나 관련 통찰력은 어디서 얻습니까?!

그것은 ReflectionClass 일 것입니다! 그것 없이는 각 클래스 파일을 한 줄씩 구문 분석해야 하지만 대신 getMethods , getProperties , getType , getDocComment 사용할 수 있습니다. 이 프로젝트에서 가장 재미있었던 것은 배열에 대한 주석 유형 힌트를 가져오는 것이었습니다. 그것들을 가져오기 위해 유틸리티 클래스에 메소드를 만들었습니다.


    // Inside a class called App\Utility\EntityReflection.php

    public static function getPropertyTypeFromComment(string $entityClass, string $property): ?string
    {
        $reflectionClass = new \ReflectionClass($entityClass);
        $reflectionProperty = $reflectionClass->getProperty($property);
        $propertyComment = $reflectionProperty->getDocComment();

        $refType = preg_grep('/([A-Z])\w+/', explode(" ", $propertyComment));

        if (count($refType) > 0) {
            $refType = array_pop($refType);
        } else {
            return null;
        }

        if ($refType === null) {
            return null;
        }

        if (strpos($refType, '[]') !== false) {
            return $refType;
        }

        return null;    
    }


이것을 $commentType = EntityReflection::getPropertyTypeFromComment(Category::class, 'categories'); 로 호출하면 Category[] 가 반환됩니다.

그리고 그것이 내가 Reflection API를 우연히 발견한 방법입니다. 이제 관계형 데이터베이스에서 개체를 성공적으로 저장하고 가져오는 데 사용합니다. 하지만 그건 다른 이야기입니다. 그래서 당신은 당신의 물건에 반사를 사용한 적이 있고, 어떻게 그것을 좋아했습니까? 내가 암시했을지도 모르지만, 나는 그것이 꽤 재미있고 마술적이며 매우 유용하다는 것을 알았습니다!

좋은 웹페이지 즐겨찾기