Completed
Push — master ( c79300...9eb332 )
by Arne
02:48
created

FileReader::setLogger()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 1
1
<?php
2
3
namespace Storeman;
4
5
use Clue\StreamFilter;
6
use Psr\Log\LoggerAwareInterface;
7
use Psr\Log\LoggerInterface;
8
use Psr\Log\NullLogger;
9
use Storeman\Config\Configuration;
10
use Storeman\Hash\AggregateHashAlgorithm;
11
use Storeman\Hash\Algorithm\HashAlgorithmInterface;
12
use Storeman\Index\IndexObject;
13
14
/**
15
 * Provides read streams for index objects.
16
 */
17
class FileReader implements LoggerAwareInterface
18
{
19
    /**
20
     * @var Configuration
21
     */
22
    protected $configuration;
23
24
    /**
25
     * @var HashAlgorithmInterface[]
26
     */
27
    protected $hashFunctions;
28
29
    /**
30
     * @var LoggerInterface
31
     */
32
    protected $logger;
33
34
    public function __construct(Configuration $configuration, array $hashFunctions)
35
    {
36
        $this->configuration = $configuration;
37
        $this->hashFunctions = $hashFunctions;
38
        $this->logger = new NullLogger();
39
    }
40
41
    /**
42
     * {@inheritdoc}
43
     */
44
    public function setLogger(LoggerInterface $logger): void
45
    {
46
        $this->logger = $logger;
47
    }
48
49
50
    /**
51
     * Returns a readable stream for the given file index object.
52
     *
53
     * @param IndexObject $indexObject
54
     * @return resource
55
     */
56
    public function getReadStream(IndexObject $indexObject)
57
    {
58
        assert($indexObject->isFile());
59
60
        $absolutePath = "{$this->configuration->getPath()}/{$indexObject->getRelativePath()}";
61
62
        $this->logger->debug("Setting up read-stream for {$absolutePath}");
63
64
        $stream = fopen($absolutePath, 'rb');
65
66
        if ($stream === false)
67
        {
68
            throw new Exception("fopen() failed for '{$absolutePath}'");
69
        }
70
71
        $this->setupStreamHashing($stream, $indexObject);
72
73
        return $stream;
74
    }
75
76
    /**
77
     * Sets up transparent file hashing.
78
     *
79
     * @param resource $stream
80
     * @param IndexObject $indexObject
81
     */
82
    protected function setupStreamHashing($stream, IndexObject $indexObject)
83
    {
84
        $knownHashes = iterator_to_array($indexObject->getHashes());
85
        $configuredHashes = $this->configuration->getFileChecksums();
86
87
        if ($missingHashes = array_diff_key($configuredHashes, $knownHashes))
88
        {
89
            $this->logger->debug("Setting up stream hashing for missing hashes: " . implode(',', $missingHashes));
90
91
            $aggregateHashAlgorithm = new AggregateHashAlgorithm(array_intersect_key($this->hashFunctions, array_flip($missingHashes)));
92
            $aggregateHashAlgorithm->initialize();
93
94
            StreamFilter\prepend($stream, function(string $chunk = null) use ($aggregateHashAlgorithm, $indexObject) {
95
96
                // eof
97
                if ($chunk === null)
98
                {
99
                    $hashes = $aggregateHashAlgorithm->finalize();
100
101
                    foreach ($hashes as $algorithm => $hash)
102
                    {
103
                        $indexObject->getHashes()->addHash($algorithm, $hash);
104
                    }
105
                }
106
107
                // digest chunk
108
                else
109
                {
110
                    $aggregateHashAlgorithm->digest($chunk);
111
                }
112
113
                return $chunk;
114
            });
115
        }
116
    }
117
}
118