Passed
Push — master ( 4ac306...d2aa89 )
by Mihail
05:30
created

CallableStream::eof()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
c 1
b 0
f 0
nc 1
nop 0
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 1
rs 10
1
<?php
2
3
/*
4
 * This file is part of the Koded package.
5
 *
6
 * (c) Mihail Binev <[email protected]>
7
 *
8
 * Please view the LICENSE distributed with this source code
9
 * for the full copyright and license information.
10
 *
11
 */
12
13
namespace Koded\Http;
14
15
use Exception;
16
use Generator;
17
use Psr\Http\Message\StreamInterface;
18
use ReflectionFunction;
19
use RuntimeException;
20
21
22
class CallableStream implements StreamInterface
23
{
24
    /** @var callable */
25
    private $callable;
26
27
    /** @var int Current position of the pointer in the buffer */
28
    private $position = 0;
29
30
    /** @var bool If callable is Generator instance */
31
    private $isGenerator = false;
32
33 15
    public function __construct(callable $callable)
34
    {
35 15
        $this->callable    = $callable;
36 15
        $this->isGenerator = (new ReflectionFunction($this->callable))->isGenerator();
37 15
    }
38
39 15
    public function __destruct()
40
    {
41 15
        $this->close();
42 15
    }
43
44 4
    public function __toString(): string
45
    {
46
        try {
47 4
            return $this->getContents();
48 1
        } catch (RuntimeException $e) {
49 1
            return '';
50
        }
51
    }
52
53 15
    public function close(): void
54
    {
55 15
        $this->detach();
56 15
    }
57
58 15
    public function detach()
59
    {
60 15
        $this->callable = null;
61 15
        $this->position = 0;
62 15
    }
63
64 1
    public function getSize(): ?int
65
    {
66 1
        return null;
67
    }
68
69 3
    public function tell(): int
70
    {
71 3
        return $this->position;
72
    }
73
74 1
    public function eof(): bool
75
    {
76 1
        return null === $this->callable;
77
    }
78
79 1
    public function seek($offset, $whence = SEEK_SET): void
80
    {
81 1
        throw new RuntimeException('Cannot seek in CallableStream');
82
    }
83
84 1
    public function rewind(): void
85
    {
86 1
        throw new RuntimeException('Cannot rewind the CallableStream');
87
    }
88
89 1
    public function write($string): int
90
    {
91 1
        throw new RuntimeException('Cannot write to CallableStream');
92
    }
93
94 7
    public function read($length): string
95
    {
96 7
        $content = '';
97
98 7
        if (null === $this->callable) {
99 3
            return $content;
100
        }
101
102
        try {
103 7
            foreach ($this->reader($length) as $chunk) {
104 5
                $content        .= $chunk;
105 5
                $this->position += mb_strlen($chunk);
106
            }
107 2
        } catch (Exception $e) {
108 2
            throw new RuntimeException($e->getMessage(), $e->getCode(), $e);
109 5
        } finally {
110 7
            $this->callable = null;
111
        }
112
113 5
        return $content;
114
    }
115
116 6
    public function getContents(): string
117
    {
118 6
        return $this->read(65536); // 64KB
119
    }
120
121 1
    public function getMetadata($key = null)
122
    {
123 1
        return $key ? null : [];
124
    }
125
126 1
    public function isSeekable(): bool
127
    {
128 1
        return false;
129
    }
130
131 1
    public function isWritable(): bool
132
    {
133 1
        return false;
134
    }
135
136 1
    public function isReadable(): bool
137
    {
138 1
        return true;
139
    }
140
141
    /**
142
     * @param int $length
143
     *
144
     * @return Generator
145
     * @throws RuntimeException
146
     */
147 7
    private function reader(int $length): Generator
148
    {
149 7
        if ($this->isGenerator) {
150 2
            yield from ($this->callable)();
151 5
        } elseif ($resource = fopen('php://temp', 'r+')) {
152 5
            if (false === @fwrite($resource, ($this->callable)())) {
153 2
                throw new RuntimeException('Cannot write to stream');
154
            }
155
156 3
            fseek($resource, 0);
157 3
            while (false === feof($resource)) {
158 3
                yield fread($resource, $length);
159
            }
160 3
            fclose($resource);
161
        }
162 5
    }
163
}
164