소스 수준에서 Composer 패키지 재정의

나는 우리 모두가 어떤 라이브러리 클래스를 확장해야 하는 상황에 처해 있다고 생각하지만 정상적인 방법으로 수행하는 것을 방해하는 무언가가 있었습니다. 이에 대한 일반적인 예는 개인 메서드를 재정의하거나 개인 변수에 액세스하거나 최종 클래스를 처리하는 것입니다. 덜 분명한 또 다른 경우는 확장하려는 클래스가 제어할 수 없는 코드에 대한 고정 배선 종속성인 경우입니다. 이 경우 모든 것이 공개되고 재정의 가능하더라도 하위 클래스를 주입할 수 없습니다.

시나리오



Laravel 프레임워크에서 cleanBindings 클래스( source )의 함수Builder를 재정의하고 싶다는 글을 썼습니다. 클래스 자체를 확장하는 것은 함수가 공용이고 클래스가 최종으로 표시되지 않기 때문에 문제가 되지 않습니다. 여기서 진짜 문제는 Builder가 프레임워크 코드의 여러 수준에 있는 하드 코딩된 종속성이라는 것입니다.

이제 면책 조항으로 이 작업을 수행할 수 있는 다른 방법이 있습니다. 그리고 일반적으로 프로덕션 코드에 내 솔루션을 사용하는 것은 권장하지 않습니다. 그런 식으로 메커니즘을 살펴 보겠습니다.

재정의 대신 덮어쓰기



PHP는 스크립팅 언어이므로 Composer로 설치하는 종속성은 결국 디스크에 있는 PHP 소스 파일 묶음에 불과합니다. 이론적으로 클래스를 확장하여 라이브러리 함수를 재정의하는 대신 공급업체 디렉토리에서 해당 소스 파일을 열고 코드를 직접 편집할 수 있습니다.

물론 여기에는 많은 문제가 따릅니다. 우선 변경 사항은 패키지를 업데이트하거나 다시 설치할 때까지만 유지됩니다. 둘째, 팀의 다른 누구도 변경 사항을 받지 못합니다. 또한 변경으로 인해 해당 기능에 의존하는 사람이 중단되지 않도록 해야 합니다. 여기에는 설치한 다른 패키지도 포함될 수 있습니다. 그러나 후자를 보장할 수 있다면 소스 코드 변경을 자동화하는 문제일 뿐입니다.

Arralyn에서 Pexels 님의 사진

더러운 짓을 하고



패키지를 설치할 때 소스 코드 변경 사항이 적용되고 업데이트로 덮어쓰지 않도록 해야 합니다. 편리하게도 컴포저가 post-autoload-dump 이벤트 형식으로 고리를 제공합니다. 설명서에는 다음과 같이 명시되어 있습니다.

post-autoload-dump: occurs after the autoloader has been dumped, either during install/update, or via the dump-autoload command.

https://getcomposer.org/doc/articles/scripts.md#event-names



작성기 스크립트documentation에 따르면 후크 콜백을 자동 로드 가능한 클래스의 정적 함수로 정의할 수 있습니다. 다음과 같은 프로젝트 구조를 사용하여 composer.json 파일에서 후크를 정의할 수 있습니다.

project
|-- scripts
| +-- ComposerScripts.php
|-- src
|-- vendor
|-- composer.json
+-- composer.lock


{
    "autoload": {
        "psr-4": {
            "App\\": "src/",
            "Scripts\\": "scripts/"
        }
    },
    "scripts": {
        "post-autoload-dump": [
            "Scripts\\ComposerScripts::postAutoloadDump"
        ]
    }
}



ComposerScripts 클래스와 작동하는지 빠르게 확인합시다.

<?php

namespace Scripts;

class ComposerScripts
{
    public static function postAutoloadDump($event)
    {
        echo 'I am gonna use this'.PHP_EOL;
    }
}


$ composer dump-autoload
Generating autoload files
Scripts\ComposerScripts::postAutoloadDump
I am gonna use this
Generated autoload files


머리 이식



Shuxuan Cao에서 Pexels 님의 사진

다음으로 소스 파일에 있는 cleanBindings의 함수 본문을 우리 자신의 것으로 바꾸고 싶습니다. 하지만 다음에 Laravel 개발자가 파일 업스트림을 업데이트할 때 깨지기 쉬운 방식으로 어떻게 해야 할까요?

환상적인nikic/PHP-Parser 프로젝트를 사용하여 PHP 코드를 AST로 구문 분석하고 함수 본문을 바꾼 다음 다시 PHP 코드로 변환할 수 있습니다. 많은 작업처럼 들리지만 파서가 우리를 위해 모든 무거운 작업을 수행합니다.

use PhpParser\Node;
use PhpParser\NodeFinder;
use PhpParser\ParserFactory;

function replaceCleanBindingsBody(string $srcPath)
{
    $code = file_get_contents($srcPath);

    // Parse the source code.
    $parser = (new ParserFactory())->create(ParserFactory::PREFER_PHP7);
    $stmts = $parser->parse($code);

    // Find the node of the cleanBindings function in the AST.
    $nodeFinder = new NodeFinder();
    $builderClass = $nodeFinder->findFirstInstanceOf($stmts, Node\Stmt\Class_::class);
    $cleanBindingsFunction = $nodeFinder->findFirst($builderClass->stmts, function(Node $node) {
        return $node instanceof Node\Stmt\ClassMethod
            &amp;&amp; $node->name->toString() === 'cleanBindings';
    });

    $newCleanBindingsCode = <<<'PHP'
<?php
function donor() {
    // Up to you
    return [];
}
PHP
    ;

    // Transplant the body of the donor to our patient.
    $donorFunction = $parser->parse($newCleanBindingsCode)[0];
    $cleanBindingsFunction->stmts = $donorFunction->stmts;

    // Dump PHP code.
    $prettyPrinter = new \PhpParser\PrettyPrinter\Standard();
    $newCode = $prettyPrinter->prettyPrintFile($stmts);

    file_put_contents($srcPath, $newCode);
}



무엇이 남았나요?



코딩 측면에서 PHP-Parser를 사용하기 위해서는 ComposerScripts 클래스에 컴포저 오토로더 파일이 필요합니다. 이 게시물에서 논의된 내용으로 전체sample project를 업로드했습니다.

이 권한을 책임감 있게 사용하십시오.

TO THE MAXIMUM EXTEND PERMITTED BY APPLICABLE LAW, THE AUTHOR OF THIS BLOG POST SHALL NOT BE LIABLE FOR ANY DAMAGE TO CODE BASES, OR PRODUCTION SYSTEMS, OR THE TIME LOST DUE TO HOUR-LONG DEBUGGING SESSIONS RESULTING FROM APPLYING THE TECHNIQUES DESCRIBED IN THIS BLOG POST. THE MATERIALS IN THIS BLOG POST COMPRISE THE AUTHOR'S VIEWS AND DO NOT CONSTITUTE PROFESSIONAL ADVICE, you know.


게시물 Overriding Composer packages at the source levelhbgl에 처음 나타났습니다.

좋은 웹페이지 즐겨찾기