Completed
Push — master ( df9952...f86661 )
by Hannes
04:17 queued 02:11
created

FlysystemEngine::getIterator()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 6
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 3
nc 2
nop 0
1
<?php
2
3
declare(strict_types = 1);
4
5
namespace hanneskod\yaysondb\Engine;
6
7
use hanneskod\yaysondb\Exception\SourceModifiedException;
8
use League\Flysystem\FilesystemInterface;
9
10
/**
11
 * Engine based on a flysystem
12
 */
13
class FlysystemEngine implements EngineInterface
14
{
15
    /**
16
     * @var string Name of source file
17
     */
18
    private $fname;
19
20
    /**
21
     * @var FilesystemInterface Filesystem where source is found
22
     */
23
    private $fs;
24
25
    /**
26
     * @var DecoderInterface Decoder used to encode and deconde content
27
     */
28
    private $decoder;
29
30
    /**
31
     * @var array Loaded documents
32
     */
33
    private $docs;
34
35
    /**
36
     * @var string Hash of source at last reset
37
     */
38
    private $hash;
39
40
    /**
41
     * @var bool Flag if there are un-commited changes
42
     */
43
    private $inTransaction;
44
45
    public function __construct(string $fname, FilesystemInterface $fs, DecoderInterface $decoder = null)
46
    {
47
        $this->fname = $fname;
48
        $this->fs = $fs;
49
        $this->decoder = $decoder ?: $this->guessDecoder();
50
        $this->reset();
51
    }
52
53
    public function getId(): string
54
    {
55
        return $this->fname;
56
    }
57
58
    public function reset()
59
    {
60
        $this->inTransaction = false;
61
        $raw = $this->fs->read($this->fname);
62
        $this->hash = md5($raw);
63
        $this->docs = $this->decoder->decode($raw);
0 ignored issues
show
Security Bug introduced by
It seems like $raw defined by $this->fs->read($this->fname) on line 61 can also be of type false; however, hanneskod\yaysondb\Engin...oderInterface::decode() does only seem to accept string, did you maybe forget to handle an error condition?

This check looks for type mismatches where the missing type is false. This is usually indicative of an error condtion.

Consider the follow example

<?php

function getDate($date)
{
    if ($date !== null) {
        return new DateTime($date);
    }

    return false;
}

This function either returns a new DateTime object or false, if there was an error. This is a typical pattern in PHP programming to show that an error has occurred without raising an exception. The calling code should check for this returned false before passing on the value to another function or method that may not be able to handle a false.

Loading history...
64
    }
65
66
    public function getIterator(): \Generator
67
    {
68
        foreach ($this->docs as $id => $doc) {
69
            yield $id => $doc;
70
        }
71
    }
72
73
    public function has(string $id): bool
74
    {
75
        return isset($this->docs[$id]);
76
    }
77
78
    public function read(string $id): array
79
    {
80
        if ($this->has($id)) {
81
            return (array)$this->docs[$id];
82
        }
83
84
        return [];
85
    }
86
87
    public function write(string $id, array $doc): string
88
    {
89
        $this->inTransaction = true;
90
91
        if ('' !== $id) {
92
            $this->docs[$id] = $doc;
93
            return $id;
94
        }
95
96
        $this->docs[] = $doc;
97
        end($this->docs);
98
99
        return (string)key($this->docs);
100
    }
101
102
    public function delete(string $id): bool
103
    {
104
        if ($this->has($id)) {
105
            $this->inTransaction = true;
106
            unset($this->docs[$id]);
107
108
            return true;
109
        }
110
111
        return false;
112
    }
113
114
    public function clear()
115
    {
116
        if ($this->docs) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->docs of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
117
            $this->inTransaction = true;
118
            $this->docs = [];
119
        }
120
    }
121
122
    public function inTransaction(): bool
123
    {
124
        return $this->inTransaction;
125
    }
126
127
    /**
128
     * @throws SourceModifiedException If source is out of date
129
     */
130
    public function commit()
131
    {
132
        if (md5($this->fs->read($this->fname)) != $this->hash) {
133
            throw new SourceModifiedException('Unable to commit changes: source data has changed');
134
        }
135
136
        $raw = $this->decoder->encode($this->docs);
137
        $this->fs->update($this->fname, $raw);
138
        $this->hash = md5($raw);
139
        $this->inTransaction = false;
140
    }
141
142
    /**
143
     * Create a decoder based of source file mime-type
144
     */
145
    private function guessDecoder(): DecoderInterface
146
    {
147
        switch ($this->fs->getMimetype($this->fname)) {
148
            case 'application/x-httpd-php':
149
                return new PhpDecoder;
150
            case 'text/plain':
151
            case 'application/json':
152
            default:
153
                return new JsonDecoder;
154
        }
155
    }
156
}
157