Completed
Push — master ( 4578ad...a78113 )
by Damian
04:15 queued 43s
created

FilesystemQueue::throwItNotExists()   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 0
Metric Value
eloc 1
dl 0
loc 3
ccs 2
cts 2
cp 1
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 0
crap 1
1
<?php declare(strict_types=1);
2
3
namespace Initx\Driver;
4
5
use Initx\Envelope;
6
use Initx\Exception\IllegalStateException;
7
use Initx\Exception\NoSuchElementException;
8
use Initx\Queue;
9
use JMS\Serializer\SerializerInterface;
10
11
final class FilesystemQueue implements Queue
12
{
13
    use HasFallbackSerializer;
14
15
    /**
16
     * @var string
17
     */
18
    private $path;
19
20
    /**
21
     * @var SerializerInterface
22
     */
23
    private $serializer;
24
25 11
    public function __construct(string $path, SerializerInterface $serializer = null)
26
    {
27 11
        $this->serializer = $serializer;
28 11
        $this->path = $path;
29 11
        $this->fallbackSerializer();
30 11
    }
31
32 5
    public function add(Envelope $envelope): void
33
    {
34 5
        if (!$this->offer($envelope)) {
35
            $this->throwItIsNotWriteable();
36
        }
37 4
    }
38
39 7
    public function offer(Envelope $envelope): bool
40
    {
41 7
        $content = $this->serializer->serialize($envelope, 'json').PHP_EOL;
42
43 7
        $result = (bool)@file_put_contents(
44 7
            $this->path,
45 7
            $content,
46 7
            FILE_APPEND
47
        );
48
49 7
        if (!$result) {
50 2
            $this->throwItIsNotWriteable();
51
        }
52
53 5
        return true;
54
    }
55
56 3
    public function remove(): Envelope
57
    {
58 3
        $envelope = $this->poll();
59
60 2
        if (!$envelope) {
61 1
            throw new NoSuchElementException();
62
        }
63
64 1
        return $envelope;
65
    }
66
67 3
    public function poll(): ?Envelope
68
    {
69 3
        $firstLine = $this->removeFirstLine();
70
71 2
        if (!$firstLine) {
72 1
            return null;
73
        }
74
75 1
        return $this->serializer->deserialize($firstLine, Envelope::class, 'json');
76
    }
77
78 2
    public function element(): Envelope
79
    {
80 2
        $envelope = $this->peek();
81
82 2
        if (!$envelope) {
83 1
            throw new NoSuchElementException();
84
        }
85
86 1
        return $envelope;
87
    }
88
89 4
    public function peek(): ?Envelope
90
    {
91 4
        $firstLine = $this->readFirstLine();
92
93 4
        if (!$firstLine) {
94 2
            return null;
95
        }
96
97 2
        return $this->serializer->deserialize($firstLine, Envelope::class, 'json');
98
    }
99
100 3
    private function removeFirstLine(): ?string
101
    {
102 3
        if (!file_exists($this->path)) {
103 1
            $this->throwItNotExists();
104
        }
105 2
        $firstLine = null;
106 2
        if ($handle = fopen($this->path, 'cb+')) {
107 2
            if (!flock($handle, LOCK_EX)) {
108
                fclose($handle);
109
            }
110 2
            $offset = 0;
111 2
            $len = filesize($this->path);
112 2
            while (($line = fgets($handle, 4096)) !== false) {
113 1
                if (!$firstLine) {
114 1
                    $firstLine = $line;
115 1
                    $offset = strlen($firstLine);
116 1
                    continue;
117
                }
118 1
                $pos = ftell($handle);
119 1
                fseek($handle, $pos - strlen($line) - $offset);
120 1
                fwrite($handle, $line);
121 1
                fseek($handle, $pos);
122
            }
123 2
            fflush($handle);
124 2
            ftruncate($handle, $len - $offset);
125 2
            flock($handle, LOCK_UN);
126 2
            fclose($handle);
127
        }
128
129 2
        return $firstLine;
130
    }
131
132 4
    private function readFirstLine(): ?string
133
    {
134 4
        if (!file_exists($this->path)) {
135
            $this->throwItNotExists();
136
        }
137
138 4
        $firstLine = fgets(fopen($this->path, 'rb'));
0 ignored issues
show
Bug introduced by
It seems like fopen($this->path, 'rb') can also be of type false; however, parameter $handle of fgets() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

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

138
        $firstLine = fgets(/** @scrutinizer ignore-type */ fopen($this->path, 'rb'));
Loading history...
139
140 4
        return $firstLine ?: null;
141
    }
142
143 2
    private function throwItIsNotWriteable(): void
144
    {
145 2
        throw new IllegalStateException("Could not write to file: {$this->path}");
146
    }
147
148 1
    private function throwItNotExists(): void
149
    {
150 1
        throw new IllegalStateException("File $this->path not exists");
151
    }
152
}
153