Passed
Pull Request — master (#20)
by Nikolay
04:51
created

WorkerDownloader::progress()   A

Complexity

Conditions 5
Paths 7

Size

Total Lines 15
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 11
c 0
b 0
f 0
dl 0
loc 15
rs 9.6111
cc 5
nc 7
nop 3
1
<?php
2
/*
3
 * Copyright © MIKO LLC - All Rights Reserved
4
 * Unauthorized copying of this file, via any medium is strictly prohibited
5
 * Proprietary and confidential
6
 * Written by Alexey Portnov, 9 2020
7
 */
8
9
namespace MikoPBX\PBXCoreREST\Workers;
10
11
require_once 'Globals.php';
12
13
use MikoPBX\Core\Workers\WorkerBase;
14
use MikoPBX\Core\System\Util;
15
16
17
class WorkerDownloader extends WorkerBase
18
{
19
    private string $old_memory_limit;
20
    private int $progress = 0;
21
    private array $settings;
22
    private string $progress_file = '';
23
    private string $error_file = '';
24
    private int $file_size = 0;
25
26
    /**
27
     * WorkerDownloader entry point.
28
     *
29
     * @param $argv
30
     */
31
    public function start($argv): void
32
    {
33
        if (file_exists($argv[1])) {
34
            $this->settings = json_decode(file_get_contents($argv[1]), true);
35
        } else {
36
            Util::sysLogMsg(__CLASS__, 'Wrong download settings');
37
38
            return;
39
        }
40
        $this->old_memory_limit = ini_get('memory_limit');
41
        ini_set('memory_limit', '300M');
42
43
        $temp_dir            = dirname($this->settings['res_file']);
44
        $this->progress_file = $temp_dir . '/progress';
45
        $this->error_file    = $temp_dir . '/error';
46
47
        $result = $this->getFile();
48
        $result = $result && $this->checkFile();
49
        if ( ! $result) {
50
            Util::sysLogMsg(__CLASS__, 'Download error...');
51
        }
52
    }
53
54
    /**
55
     * Downloads file from remote resource by link
56
     */
57
    public function getFile(): bool
58
    {
59
        if (empty($this->settings)) {
60
            return false;
61
        }
62
        if (file_exists($this->settings['res_file'])) {
63
            unlink($this->settings['res_file']);
64
        }
65
        if (isset($this->settings['size'])){
66
            $this->file_size = $this->settings['size'];
67
        } else {
68
            $this->file_size = $this->remoteFileSize($this->settings['url']);
69
        }
70
71
        file_put_contents($this->progress_file, 0);
72
73
        $fp = fopen($this->settings['res_file'], 'w');
74
        $ch = curl_init();
75
        if ( ! is_resource($ch)) {
76
            return false;
77
        }
78
        curl_setopt($ch, CURLOPT_FILE, $fp);
79
        curl_setopt($ch, CURLOPT_URL, $this->settings['url']);
80
        curl_setopt($ch, CURLOPT_PROGRESSFUNCTION, [$this, 'progress']);
81
        curl_setopt($ch, CURLOPT_NOPROGRESS, false); // needed to make progress function work
82
        curl_setopt($ch, CURLOPT_HEADER, 0);
83
        curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
84
85
        curl_exec($ch);
86
        $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
87
        if ($http_code !== 200) {
88
            file_put_contents($this->error_file, "Curl return code $http_code", FILE_APPEND);
89
        }
90
        curl_close($ch);
91
92
        return $http_code === 200;
93
    }
94
95
    /**
96
     * Remote File Size Using cURL
97
     *
98
     * @param string $url
99
     *
100
     * @return int
101
     */
102
    private function remoteFileSize(string $url): int
103
    {
104
        $ch       = curl_init($url);
105
        $fileSize = 0;
106
        if ($ch !== false) {
107
            curl_setopt($ch, CURLOPT_NOBODY, 1);
108
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, 0);
109
            curl_setopt($ch, CURLOPT_HEADER, 0);
110
            curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
111
            curl_setopt($ch, CURLOPT_MAXREDIRS, 3);
112
            curl_exec($ch);
113
            $fileSize = curl_getinfo($ch, CURLINFO_CONTENT_LENGTH_DOWNLOAD);
114
            curl_close($ch);
115
        }
116
117
        return $fileSize;
118
    }
119
120
    /**
121
     * Checks file md5 sum and size
122
     */
123
    public function checkFile(): bool
124
    {
125
        if ( ! file_exists($this->settings['res_file'])) {
126
            file_put_contents($this->error_file, 'File did not upload', FILE_APPEND);
127
128
            return false;
129
        }
130
        if (md5_file($this->settings['res_file']) !== $this->settings['md5']) {
131
            unlink($this->settings['res_file']);
132
            file_put_contents($this->error_file, 'Error on comparing MD5 sum', FILE_APPEND);
133
134
            return false;
135
        }
136
        if ($this->file_size !== filesize($this->settings['res_file'])) {
137
            unlink($this->settings['res_file']);
138
            file_put_contents($this->error_file, 'Error on comparing file size', FILE_APPEND);
139
140
            return false;
141
        }
142
        file_put_contents($this->progress_file, 100);
143
144
        return true;
145
    }
146
147
    /**
148
     * Returns memory_limit to default value.
149
     */
150
    public function __destruct()
151
    {
152
        ini_set('memory_limit', $this->old_memory_limit);
153
154
    }
155
156
    /**
157
     * Calculates download progress and writes it on progress_file.
158
     *
159
     * @param $resource
160
     * @param $download_size
161
     * @param $downloaded
162
     */
163
    public function progress($resource, $download_size, $downloaded): void
164
    {
165
        if ($download_size === 0) {
166
            return;
167
        }
168
        if ($this->file_size < 0) {
169
            $new_progress = $downloaded / $download_size * 100;
170
        } else {
171
            $new_progress = $downloaded / $this->file_size * 100;
172
        }
173
        $delta = $new_progress - $this->progress;
174
        if ($delta > 1) {
175
            $this->progress = round($new_progress, 0);
176
            $this->progress = $this->progress < 99 ? $this->progress : 99;
177
            file_put_contents($this->progress_file, $this->progress);
178
        }
179
    }
180
181
}
182
183
// Start worker process
184
$workerClassname = WorkerDownloader::class;
185
if (isset($argv) && count($argv) > 1) {
186
    try {
187
        $worker = new $workerClassname();
188
        $worker->start($argv);
189
    } catch (\Error $e) {
190
        global $errorLogger;
191
        $errorLogger->captureException($e);
192
        Util::sysLogMsg("{$workerClassname}_EXCEPTION", $e->getMessage());
193
    }
194
}