PHP 생성기 사용법에 대해 이야기합시다

不言
풀어 주다: 2023-04-03 08:20:02
원래의
1712명이 탐색했습니다.

PHP는 버전 5.5에서 "Generator" 기능을 도입했지만 이 기능은 사람들의 관심을 끌지 못했습니다. PHP 5.4.x에서 PHP 5.5.x로의 정식 마이그레이션에서는 간단한 방법으로 반복자(Iterator)를 구현할 수 있다고 소개되어 있습니다. 하지만 그 외에도 어떤 시나리오에서 생성기를 사용할 수 있나요?

Yield 키워드를 통해 Generator 구현이 완료됩니다. 생성기는 반복기 인터페이스를 구현하는 클래스를 통해 반복기 구현의 복잡성이나 추가 오버헤드가 거의 없이 반복기를 구현하는 간단한 방법을 제공합니다.

문서는 이 간단한 반복자를 보여주는 간단한 예를 제공합니다. 아래 코드를 살펴보십시오.

function xrange($start, $limit, $step = 1) {
    for ($i = $start; $i <= $limit; $i += $step) {
        yield $i;
    }
}
로그인 후 복사

반복자를 지원하지 않는 배열과 비교해 보겠습니다.

foreach xrange($start, $limit, $step = 1) {
    $elements = [];
    
    for ($i = $start; $i <= $limit; $i += $step) {
        $elements[] = $i;
    }

    return $elements;
}
로그인 후 복사

두 버전의 함수 모두 foreach 모든 ​​요소에 대한 반복을 지원합니다.

foreach (xrange(1, 100) as $i) {
    print $i . PHP_EOL;
}
로그인 후 복사

더 짧은 함수 정의 외에도 무엇을 얻을 수 있을까요? yield 정확히 어떤 일을 하셨나요? return 문 없이도 첫 번째 함수를 정의할 때 데이터가 반환될 수 있는 이유는 무엇입니까?

반환 값부터 시작해 보겠습니다. 제너레이터는 PHP에서 매우 특별한 기능입니다. 함수에 yield이 포함되어 있으면 이 함수는 더 이상 일반 함수가 아니며 항상 "생성기" 인스턴스를 반환합니다. 생성기는 Iterator 인터페이스를 구현하므로 foreach 순회를 수행할 수 있습니다.

다음으로 Iterator 인터페이스의 메서드를 사용하여 이전 foreach 루프를 다시 작성합니다. 3v4l.org에서 결과를 볼 수 있습니다.

$generator = xrange(1, 100);

while($generator->valid()) {
    print $generator->current() . PHP_EOL;

    $generator->next();
}
로그인 후 복사

생성기가 더 발전된 기술이라는 것을 확실히 알 수 있습니다. 이제 생성기 내부에서 처리되는 방식을 더 잘 이해하기 위해 새로운 생성기 예제를 작성해 보겠습니다.

function foobar() {
    print 'foobar - start' . PHP_EOL;

    for ($i = 0; $i < 5; $i++) {
        print &#39;foobar - yielding...&#39; . PHP_EOL;
        yield $i;
        print &#39;foobar - continued...&#39; . PHP_EOL;
    }

    print &#39;foobar - end&#39; . PHP_EOL;
}

$generator = foobar();

print &#39;Generator created&#39; . PHP_EOL;

while ($generator->valid()) {
    print "Getting current value from the generator..." . PHP_EOL;

    print $generator->current() . PHP_EOL;

    $generator->next();
}
로그인 후 복사
Generator created
foobar - start
foobar - yielding...
Getting current value from the generator...
1
foobar - continued
foobar - yielding...
Getting current value from the generator...
2
foobar - continued
foobar - yielding...
Getting current value from the generator...
3
foobar - continued
foobar - yielding...
Getting current value from the generator...
4
foobar - continued
foobar - yielding...
Getting current value from the generator...
5
foobar - continued
foobar - end
로그인 후 복사

응? Generatorcreated가 먼저 인쇄되는 이유는 무엇입니까? 이는 발전기가 사용될 때까지 아무 작업도 수행하지 않기 때문입니다. 위의 예에서 생성기 실행을 시작하는 코드는 $generator->valid()**입니다. 생성기가 첫 번째 **yield**까지 실행되고 제어 흐름을 호출자 **$generator->valid()에게 반환하는 것을 볼 수 있습니다. $generator->next() 호출되면 생성기 실행이 재개되고 다음 yield 실행이 다시 중지되는 식으로 더 이상 생성기가 없을 때까지 계속됩니다. # 🎜🎜 #yield 지금까지. 이제 yield에서 일시 중지 및 재개를 수행할 수 있는 터미널 기능이 있습니다. 이 기능을 사용하면 클라이언트에 필요한 연기 기능을 작성할 수 있습니다.

GitHub API에서 모든 사용자를 읽는 함수를 만들 수 있습니다. 페이지 매김이 지원되지만 필요한 경우에만 이러한 세부 정보를 숨기고 데이터의 다음 페이지를 가져올 수 있습니다.

yield을 사용하면 현재 페이지에서 각 사용자 데이터를 얻을 수 있습니다. 현재 페이지의 모든 사용자를 얻을 때까지 다음 페이지의 데이터를 얻을 수 있습니다.

class GitHubClient {
    function getUsers(): Iterator {
        $uri = '/users';

        do {
            $response = $this->get($uri);
            foreach ($response->items as $user) {
                yield $user;
            }

            $uri = $response->nextUri;
        } while($uri !== null);
    }
}
로그인 후 복사
클라이언트는 언제든지 모든 사용자를 반복하거나 반복을 중지할 수 있습니다.

생성자를 반복자로 사용하는 것은 정말 지루합니다

예, 당신 말이 맞습니다. 위에서 제공한 모든 설명은 PHP 문서를 통해 누구든지 얻을 수 있습니다. 그러나 반복자로서 이러한 사용은 강력한 기능의 절반도 사용하지 않습니다. 생성기는

Iterator 인터페이스의 일부가 아닌 send()throw() 함수도 제공합니다. 앞서 생성기 실행을 일시 중지하고 재개하는 기능에 대해 이야기했습니다. 생성기를 복원해야 하는 경우 Generator::next() 메서드를 사용할 수 있을 뿐만 아니라 Generator::send() 및 #을 사용할 수도 있습니다. 🎜🎜#Generator ::throw() 메서드.

Generator::send()

을 사용하면 yield의 반환 값을 지정할 수 있으며 Generator::throw( )# 🎜🎜# yield에 예외가 발생하도록 허용합니다. 이러한 방법을 통해 생성기에서 데이터를 얻을 수 있을 뿐만 아니라 새 데이터를 생성기로 보낼 수도 있습니다. 코루틴을 사용한 협력적 멀티태스킹에서 가져온 Logger

로그 예제를 살펴보겠습니다(이 문서를 읽는 것이 좋습니다). 여기서는

function logger($filename) {
    $fileHandle = fopen($filename, 'a');

    while (true) {
        fwrite($fileHandle, yield . "\n");
    }
}

$logger = logger(__DIR__ . '/log');
$logger->send('Foo');
$logger->send('Bar');
로그인 후 복사
yield이 표현으로 사용되었습니다. 데이터를 보내면 데이터는

yield에서 반환된 다음 fwrite()에 매개 변수로 전달됩니다. 솔직히 이 예제는 실제 프로젝트에서는 쓸모가 없습니다. Generator::send()

의 사용 원리를 보여주기 위해 사용되지만 단순히 데이터를 보낼 수 있다는 것만으로는 큰 효과가 없습니다. 일반적인 기능을 지원하는 클래스가 있었다면 달랐을 것이다.

使用生成器的乐趣来自于通过 yield 创建数据,然后由「生成器执行程序(generator runner)」依据这个数据来处理业务,然后再继续执行生成器。这就是「协程(coroutines)」和「状态流解析器(stateful streaming parsers)」实例。在讲解协程和状态流解析器之前,我们快速浏览一下如何在生成器中返回数据,我们还没有将接触这方面的知识。从 PHP 5.5 开始我们可以在生成器内部使用 return; 语句,但是不能返回任何值。执行 return; 语句的唯一目的是结束生成器执行。

不过从 PHP 7.0 起支持返回值。这个功能在用于迭代时可能有些奇怪,但是在其他使用场景如协程时将非常有用,例如,当我们在执行一个生成器时我们可以依据返回值处理,而无需直接对生成器进行操作。下一节我们将讲解 return 语句在协程中的使用。

异步生成器

Amp 是一款 PHP 异步编程的框架。支持异步协程功能,本质上是等待处理结果的占位符。「生成器执行程序」为 Coroutine类。它会订阅异步生成器(yielded promise),当有执行结果可用时则继续生成器处理。如果处理失败,则会抛出异常给生成器。你可以到 amphp/amp 版本库查看实现细节。在 Amp 中的 Coroutine 本身就是一个 Promise。如果这个协程抛出未经捕获的异常,这个协程就执行失败了。如果解析成功,那么就返回一个值。这个值看起来和普通函数的返回值并无二致,只不过它处于异步执行环境中。这就是需要生成器需要有返回值的意义,这也是为何我们将这个特性加入到 PHP 7.0 中的原因,我们会将最后执行的yield 值作为返回值,但这不是一个好的解决方案。

Amp 可以像编写阻塞代码一样编写非阻塞代码,同时允许在同一进程中执行其它非阻塞事件。一个使用场景是,同时对一个或多个第三方 API 并行的创建多个 HTTP 请求,但不限于此。得益于事件循环,可以同时处理多个 I/O 处理,而不仅仅是只能处理多个 HTTP请求这类操作。

Loop::run(function() {
    $uris = [
        "https://google.com/",
        "https://github.com/",
        "https://stackoverflow.com/",
    ];

    $client = new Amp\Artax\DefaultClient;
    $promises = [];

    foreach ($uris as $uri) {
        $promises[$uri] = $client->request($uri);
    }

    $responses = yield $promises;

    foreach ($responses as $uri => $response) {
        print $uri . " - " . $response->getStatus() . PHP_EOL;
    }
});
로그인 후 복사

但是,拥有异步功能的协程并非只能够在 yield 右侧出现变量,还可以在它的左侧。这就是我们前面提到的解析器。

$parse = new Parser((function(){
    while (true) {
        $line = yield "\r\n";

        if (trim($line) === "") {
            continue;
        }

        print "New item: {$line}" . PHP_EOL;
    }
})());

for ($i = 0; $i < 100; $i++) {
    $parser->push("bar\r");
    $parser->push("\nfoo");
}
로그인 후 복사

解析器会缓存所有输入直到接收的是 rn。这类生成器解析器并不能简化简单协议处理(如换行分隔符协议),但是对于复杂的解析器,如在服务器解析 HTTP 请求的 Aerys。

相关推荐:

如何利用PHP实现开发中基于layUI的三级联动效果的代码

关于IIS下PHP快三平台源码的架设环境的配置过程

위 내용은 PHP 생성기 사용법에 대해 이야기합시다의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

관련 라벨:
원천:php.cn
본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
인기 튜토리얼
더>
최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿