Completed
Push — master ( 5bf22f...cf1632 )
by Kirill
06:39
created

JsonStream::log()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 11
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 11
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 7
nc 2
nop 2
1
<?php declare(strict_types=1);
2
/**
3
 * This file is part of GitterApi package.
4
 *
5
 * For the full copyright and license information, please view the LICENSE
6
 * file that was distributed with this source code.
7
 */
8
namespace Gitter\Support;
9
10
use Psr\Http\Message\StreamInterface;
11
12
/**
13
 * Class JsonStream
14
 * @package Gitter\Support
15
 */
16
class JsonStream
17
{
18
    /**
19
     * @var int
20
     */
21
    const BUFFER_MAX_SIZE = 2 ** 14;
22
23
    /**
24
     * @var string
25
     */
26
    private $bufferSize = 0;
27
28
    /**
29
     * @var string
30
     */
31
    private $endsWith = ']';
32
33
    /**
34
     * @var string
35
     */
36
    private $buffer = '';
37
38
    /**
39
     * @var int
40
     */
41
    private $chunkSize = 1;
42
43
    /**
44
     * @var Loggable
45
     */
46
    private $logger;
47
48
    /**
49
     * JsonStream constructor.
50
     * @param int $bufferSize
51
     */
52
    public function __construct(int $bufferSize = self::BUFFER_MAX_SIZE)
53
    {
54
        $this->bufferSize = $bufferSize;
55
    }
56
57
    /**
58
     * @param Loggable $logger
59
     */
60
    public function setLogger(Loggable $logger)
61
    {
62
        $this->logger = $logger;
63
    }
64
65
    /**
66
     * @throws \OutOfBoundsException
67
     */
68
    private function checkSize()
69
    {
70
        if ($this->bufferSize > 0 && strlen($this->buffer) > $this->bufferSize) {
71
            throw new \OutOfBoundsException(
72
                sprintf('Memory leak detected . Buffer size out of %s bytes', $this->bufferSize)
73
            );
74
        }
75
    }
76
77
    /**
78
     * @param string $data
79
     * @param \Closure|null $callback
80
     * @return mixed|null
81
     */
82
    public function push(string $data, \Closure $callback = null)
83
    {
84
        // Buffer are empty and input starts with "[" or "{"
85
        $canBeBuffered = $this->buffer === '' && in_array($data[0], ['[', '{'], true);
86
87
        // Data can be starts buffering
88
        if ($canBeBuffered) {
89
            $this->endsWith = $data[0] === '[' ? ']' : '}';
90
        }
91
92
        // Add chunks for non empty buffer
93
        if ($canBeBuffered || $this->buffer !== '') {
94
            $this->buffer .= $data;
95
96
            // Try to compile
97
            $trimmed = rtrim($data);
98
            if ($trimmed[strlen($trimmed) - 1] === $this->endsWith) {
99
                $object = json_decode($this->buffer, true);
100
101
                if (json_last_error() === JSON_ERROR_NONE) {
102
                    $this->buffer = '';
103
104
                    if ($callback !== null) {
105
                        $callback($object);
106
                    }
107
108
                    return $object;
109
                }
110
            }
111
        }
112
113
        return null;
114
    }
115
116
    /**
117
     * @param StreamInterface $stream
118
     * @return \Generator
119
     * @throws \OutOfBoundsException
120
     * @throws \RuntimeException
121
     */
122
    public function stream(StreamInterface $stream): \Generator
123
    {
124
        while (!$stream->eof()) {
125
            $data = $stream->read($this->chunkSize);
126
127
            $output = $this->push($data);
128
            if ($output !== null) {
129
                yield $output;
130
            }
131
132
            $this->checkSize();
133
        }
134
    }
135
}
136