Passed
Pull Request — master (#59)
by Alexander
22:15 queued 08:16
created

StreamedController::pageIndex()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 26
Code Lines 36

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 36
dl 0
loc 26
rs 9.344
c 0
b 0
f 0
cc 4
nc 3
nop 0
1
<?php
2
3
namespace App\LazyRendering\Http;
4
5
use App\Blog\Entity\Post;
6
use App\Blog\Post\PostRepository;
7
use App\Blog\Widget\PostCard;
8
use App\LazyRendering\View\MainLayout;
9
use Cycle\ORM\ORMInterface;
10
use Psr\Http\Message\ResponseInterface;
11
use RuntimeException;
12
use Yiisoft\Html\Html;
13
14
class StreamedController extends BaseController
15
{
16
    public const ROUTE_NAME = 'lazy-render';
17
18
    protected $pageLayout = MainLayout::class;
19
20
    public function pageIndex(): iterable
21
    {
22
        foreach (get_class_methods($this) as $method) {
23
            $isPage = strpos($method, 'page') === 0;
24
            if (!$isPage || $method === __FUNCTION__) {
25
                continue;
26
            }
27
            $page = substr($method, 4);
28
            $pageName = ltrim(preg_replace('/([A-Z][a-z])/u', ' $1', $page));
29
30
            yield '<li>'
31
                . "{$pageName} :: "
32
                . Html::a('Lazy', $this->urlGenerator->generate(static::ROUTE_NAME, ['page' => $page]))
33
                . ' :: '
34
                . Html::a(
35
                    'Classic Mode',
36
                    $this->urlGenerator->generate(static::ROUTE_NAME, ['page' => $page, 'forceBuffering' => 1])
37
                )
38
                . ' :: '
39
                . Html::a(
40
                    'Combined Mode',
41
                    $this->urlGenerator->generate(static::ROUTE_NAME, ['page' => $page, 'forceBuffering' => 2])
42
                )
43
                . '</li>';
44
        } ?>
45
        <div class="mt-5">
46
            <p>
47
                <b>Lazy Mode</b> - deferred rendering pages. Page renders as it emits.
48
            </p>
49
            <p>
50
                <b>Classic Mode</b> (imitation) - page is rendered and buffered before the response object is sent back
51
                via pipeline.
52
            </p>
53
            <p>
54
                <b>Combined Mode</b> - deferred rendering pages. First yielded value will be emitted immediately,
55
                the rest of the contents will be buffered and emitted in one piece
56
            </p>
57
            <p>
58
                <a href="https://github.com/yiisoft/yii-demo/pull/59">About this on GitHub</a>
59
            </p>
60
        </div>
61
        <?php
62
    }
63
64
    public function pageAllPosts(ORMInterface $orm, int $interval = 0): iterable
65
    {
66
        yield '<h1>Streamed output</h1>';
67
        $startTime = microtime(true);
68
69
        /** @var PostRepository $postRepo */
70
        $postRepo = $orm->getRepository(Post::class);
71
        $pages = $postRepo->findAllPreloaded();
72
        yield '<h2>Total posts: ' . $pages->count() . '</h2>';
73
74
        $afterCountTime = microtime(true);
75
        yield '<h5>Time to a count query: ' . (int)(1_000_000 * ($afterCountTime - $startTime)) . 'μs</h5>';
0 ignored issues
show
Bug introduced by
The constant App\LazyRendering\Http\1_000_000 was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
76
77
        $card = PostCard::widget();
78
        $previousPostTime = microtime(true);
79
80
        # type $stream->read() instead of $stream->getIterator()
81
        # to disable lazy loading and load all posts in buffer:
82
        /** @var Post $post */
83
        foreach ($pages->getIterator() as $post) {
84
            $currentPostTime = microtime(true);
85
            yield (string)$card->post($post)
0 ignored issues
show
Bug introduced by
The method post() does not exist on Yiisoft\Widget\Widget. It seems like you code against a sub-type of Yiisoft\Widget\Widget such as App\Blog\Widget\PostCard. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

85
            yield (string)$card->/** @scrutinizer ignore-call */ post($post)
Loading history...
86
                . '<h5>Getting item time: ' . (int)(1_000_000 * ($currentPostTime - $previousPostTime)) . 'μs</h5>';
87
            usleep($interval);
88
            $previousPostTime = microtime(true);
89
        }
90
    }
91
92
    public function pageAllPostsWithInterval(ORMInterface $orm): iterable
93
    {
94
        # disable time limit
95
        set_time_limit(0);
96
97
        return $this->pageAllPosts($orm, 150_000);
0 ignored issues
show
Bug introduced by
The constant App\LazyRendering\Http\150_000 was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
98
    }
99
100
    public function pageFewPostsAndError(ORMInterface $orm): iterable
101
    {
102
        yield '<h1>Streamed out with error after 3 posts</h1>';
103
        /** @var PostRepository $postRepo */
104
        $postRepo = $orm->getRepository(Post::class);
105
106
        $pages = $postRepo->findAllPreloaded()->withLimit(3);
107
        yield '<h2>Total posts: ' . $pages->count() . '</h2>';
108
109
        $card = PostCard::widget();
110
        /** @var Post $post */
111
        foreach ($pages->read() as $post) {
112
            yield (string)$card->post($post);
113
        }
114
115
        throw new RuntimeException('Just error');
116
    }
117
118
    public function pagePageWithEchoAndOutputBufferingBetweenYields(): iterable
119
    {
120
        yield '<h1>Page With Echo And Output Buffering Between Yields</h1>';
121
122
        for ($i = 1, $j = 20; $i < $j; ++$i) {
123
            if ($i % 2 === 0) {
124
                yield "<div>{$i} - yielded</div>";
125
            } else {
126
                echo "<div>{$i} - <b>printed</b></div>";
127
            }
128
        }
129
    }
130
131
    public function pageDirectEchoWithoutBufferingBetweenYields(): ResponseInterface
132
    {
133
        $generator = static function () {
134
            echo '<h1>Direct Echo Without Buffering Between Yields</h1>';
135
136
            for ($i = 1, $j = 20; $i < $j; ++$i) {
137
                if ($i % 2 === 0) {
138
                    yield "<div>{$i} - yielded</div>";
139
                } else {
140
                    echo "<div>{$i} - <b>printed</b></div>";
141
                }
142
            }
143
        };
144
        $stream = new GeneratorStream($generator());
145
        return $this->responseFactory->createResponse()->withBody($stream);
146
    }
147
}
148