Skip to content

Instantly share code, notes, and snippets.

@clue
Last active November 14, 2021 13:49
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 clue/1dc153db2ce5ecd0ecc76f428a07813e to your computer and use it in GitHub Desktop.
Save clue/1dc153db2ce5ecd0ecc76f428a07813e to your computer and use it in GitHub Desktop.
Generator-based coroutines returning Generator vs wrapping in Promises using coroutine helper in Framework X
AcmeDemo
├── src/
│   ├── Book.php
│   ├── BookRepository.php
│   └── BookLookupController.php
├── vendor/
├── app.php
├── composer.json
└── composer.lock

Coroutines (all in)

# src/Book.php
<?php

namespace Acme\Todo;

class Book
{
    public $title;

    public function __construct(string $title)
    {
        $this->title = $title;
    }
}
# src/BookRepository.php
<?php

namespace Acme\Todo;

use React\Mysql\ConnectionInterface
use React\Promise\PromiseInterface;

class BookRepository
{
    private $db;

    public function __construct(ConnectionInterface $db)
    {
        $this->db = $db;
    }

    /** @return \Generator<mixed,PromiseInterface,mixed,?Book> */
    public function findBook(string $isbn): \Generator
    {
        $result = yield $this->db->query(
            'SELECT title FROM book WHERE isbn = ?',
            [$isbn]
        );

        if (count($result->resultRows) === 0) {
            return null;
        }

        return new Book($result->resultRows[0]['title']);
    }
}
# src/BookLookupController.php
<?php

namespace Acme\Todo;

use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use React\Http\Message\Response;
use React\Promise\PromiseInterface;

class BookLookupController
{
    private $repository;

    public function __construct(BookRepository $repository)
    {
        $this->repository = $repository;
    }

    /** @return \Generator<mixed,PromiseInterface,mixed,ResponseInterface> */
    public function __invoke(ServerRequestInterface $request): \Generator
    {
        $isbn = $request->getAttribute('isbn');
        $book = yield from $this->repository->findBook($isbn);

        if ($book === null) {
            return new Response(
                404,
                [],
                "Book not found\n"
            );
        }

        $data = $book->title;
        return new Response(
            200,
            [],
            $data
        );
    }
}

Coroutines (hybrid)

# src/Book.php
<?php

namespace Acme\Todo;

class Book
{
    public $title;

    public function __construct(string $title)
    {
        $this->title = $title;
    }
}
# src/BookRepository.php
<?php

namespace Acme\Todo;

use React\Mysql\ConnectionInterface;
use React\Promise\PromiseInterface;
use function FrameworkX\coroutine;

class BookRepository
{
    private $db;

    public function __construct(ConnectionInterface $db)
    {
        $this->db = $db;
    }

    /** @return PromiseInterface<?Book> */
    public function findBook(string $isbn): PromiseInterface
    {
        return coroutine(function () use ($isbn) {
            $result = yield $this->db->query(
                'SELECT title FROM book WHERE isbn = ?',
                [$isbn]
            );
    
            if (count($result->resultRows) === 0) {
                return null;
            }
    
            return new Book($result->resultRows[0]['title']);
        });
    }
}
# src/BookLookupController.php
<?php

namespace Acme\Todo;

use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use React\Http\Message\Response;
use React\Promise\PromiseInterface;
use function FrameworkX\coroutine;

class BookLookupController
{
    private $repository;

    public function __construct(BookRepository $repository)
    {
        $this->repository = $repository;
    }

    /** @return PromiseInterface<ResponseInterface> */
    public function __invoke(ServerRequestInterface $request): PromiseInterface
    {
        return coroutine(function () use ($request) {
            $isbn = $request->getAttribute('isbn');
            $book = yield $this->repository->findBook($isbn);
    
            if ($book === null) {
                return new Response(
                    404,
                    [],
                    "Book not found\n"
                );
            }
    
            $data = $book->title;
            return new Response(
                200,
                [],
                $data
            );
        });
    }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment