Completed
Pull Request — master (#120)
by Miguel
07:49
created

FileProvider::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 8
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 8
ccs 0
cts 7
cp 0
rs 9.4285
cc 1
eloc 6
nc 1
nop 5
crap 2
1
<?php
2
namespace Uecode\Bundle\QPushBundle\Provider;
3
4
use Doctrine\Common\Cache\Cache;
5
use Monolog\Logger;
6
use Symfony\Component\Filesystem\Filesystem;
7
use Symfony\Component\Finder\Finder;
8
use Symfony\Component\Finder\SplFileInfo;
9
use Uecode\Bundle\QPushBundle\Event\MessageEvent;
10
use Uecode\Bundle\QPushBundle\Message\Message;
11
12
class FileProvider extends AbstractProvider
13
{
14
    protected $filePointerList = [];
15
    protected $queuePath;
16
17
    public function __construct($name, array $options, $client, Cache $cache, Logger $logger) {
18
        $this->name     = $name;
19
        /* md5 only contain numeric and A to F, so it is file system safe */
20
        $this->queuePath = $options['path'].DIRECTORY_SEPARATOR.str_replace('-', '', hash('md5', $name));
21
        $this->options  = $options;
22
        $this->cache    = $cache;
23
        $this->logger   = $logger;
24
    }
25
26
    public function getProvider()
27
    {
28
        return 'File';
29
    }
30
31
    public function create()
32
    {
33
        $fs = new Filesystem();
34
        if (!$fs->exists($this->queuePath)) {
35
            $fs->mkdir($this->queuePath);
36
            return $fs->exists($this->queuePath);
37
        }
38
        return true;
39
    }
40
41
    public function publish(array $message, array $options = [])
42
    {
43
        $fileName = microtime(false);
44
        $fileName = str_replace(' ', '', $fileName);
45
        $path = substr(hash('md5', $fileName), 0, 3);
46
47
        $fs = new Filesystem();
48
        if (!$fs->exists($this->queuePath.DIRECTORY_SEPARATOR.$path)) {
49
            $fs->mkdir($this->queuePath.DIRECTORY_SEPARATOR.$path);
50
        }
51
52
        $fs->dumpFile(
53
            $this->queuePath.DIRECTORY_SEPARATOR.$path.DIRECTORY_SEPARATOR.$fileName.'.json',
54
            json_encode($message)
55
        );
56
        return $fileName;
57
    }
58
59
    /**
60
     * @param array $options
61
     * @return Message[]
62
     */
63
    public function receive(array $options = [])
64
    {
65
        $finder = new Finder();
66
        $finder
67
            ->files()
68
            ->ignoreDotFiles(true)
69
            ->ignoreUnreadableDirs(true)
70
            ->ignoreVCS(true)
71
            ->name('*.json')
72
            ->in($this->queuePath)
73
        ;
74
        if ($this->options['message_delay'] > 0) {
75
            $finder->date(
76
                sprintf('< %d seconds ago', $this->options['message_delay'])
77
            );
78
        }
79
        $finder
80
            ->date(
81
                sprintf('> %d seconds ago', $this->options['message_expiration'])
82
            )
83
        ;
84
        $messages = [];
85
        /** @var SplFileInfo $file */
86
        foreach ($finder as $file) {
87
            $filePointer = fopen($file->getRealPath(), 'r+');
88
            $id = substr($file->getFilename(), 0, -5);
89
            if (!isset($this->filePointerList[$id]) && flock($filePointer, LOCK_EX | LOCK_NB)) {
90
                $this->filePointerList[$id] = $filePointer;
91
                $messages[] = new Message($id, json_decode($file->getContents(), true), []);
92
            } else {
93
                fclose($filePointer);
94
            }
95
            if (count($messages) === (int) $this->options['messages_to_receive']) {
96
                break;
97
            }
98
        }
99
        return $messages;
100
    }
101
102
    public function delete($id)
103
    {
104
        $success = false;
105
        if (isset($this->filePointerList[$id])) {
106
            $fileName = $id;
107
            $path = substr(hash('md5', (string)$fileName), 0, 3);
108
            $fs = new Filesystem();
109
            $fs->remove(
110
                $this->queuePath . DIRECTORY_SEPARATOR . $path . DIRECTORY_SEPARATOR . $fileName . '.json'
111
            );
112
            fclose($this->filePointerList[$id]);
113
            unset($this->filePointerList[$id]);
114
            $success = true;
115
        }
116
        if (rand(1,10) === 5) {
117
            $this->cleanUp();
118
        }
119
        return $success;
120
    }
121
122
    public function cleanUp()
123
    {
124
        $finder = new Finder();
125
        $finder
126
            ->files()
127
            ->in($this->queuePath)
128
            ->ignoreDotFiles(true)
129
            ->ignoreUnreadableDirs(true)
130
            ->ignoreVCS(true)
131
            ->depth('< 2')
132
            ->name('*.json')
133
        ;
134
        $finder->date(
135
            sprintf('< %d seconds ago', $this->options['message_expiration'])
136
        );
137
        /** @var SplFileInfo $file */
138
        foreach ($finder as $file) {
139
            @unlink($file->getRealPath());
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
140
        }
141
    }
142
143
    public function destroy()
144
    {
145
        $fs = new Filesystem();
146
        $fs->remove($this->queuePath);
147
        $this->filePointerList = [];
148
        return !is_dir($this->queuePath);
149
    }
150
151
    /**
152
     * Removes the message from queue after all other listeners have fired
153
     *
154
     * If an earlier listener has erred or stopped propagation, this method
155
     * will not fire and the Queued Message should become visible in queue again.
156
     *
157
     * Stops Event Propagation after removing the Message
158
     *
159
     * @param MessageEvent $event The SQS Message Event
160
     * @return bool|void
161
     */
162
    public function onMessageReceived(MessageEvent $event)
163
    {
164
        $id = $event->getMessage()->getId();
165
        $this->delete($id);
166
        $event->stopPropagation();
167
    }
168
}
169