Passed
Push — master ( 6150c4...fe4cdf )
by Markus
02:34
created

Package::getThumbnails()   B

Complexity

Conditions 5
Paths 7

Size

Total Lines 35
Code Lines 22

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 23
CRAP Score 5

Importance

Changes 7
Bugs 0 Features 0
Metric Value
cc 5
eloc 22
c 7
b 0
f 0
nc 7
nop 1
dl 0
loc 35
ccs 23
cts 23
cp 1
crap 5
rs 8.439
1
<?php
2
3
namespace SSpkS\Package;
4
5
/**
6
 * SPK Package class
7
 *
8
 * @property string $spk Path to SPK file
9
 * @property string $spk_url URL to SPK file
10
 * @property string $displayname Pretty printed name of package (falls back to $package if not present)
11
 * @property string $package Package name
12
 * @property string $version Package version
13
 * @property string $description Package description
14
 * @property string $maintainer Package maintainer
15
 * @property string $maintainer_url URL of maintainer's web page
16
 * @property string $distributor Package distributor
17
 * @property string $distributor_url URL of distributor's web page
18
 * @property array $arch List of supported architectures, or 'noarch'
19
 * @property array $thumbnail List of thumbnail files
20
 * @property array $thumbnail_url List of thumbnail URLs
21
 * @property array $snapshot List of screenshot files
22
 * @property array $snapshot_url List of screenshot URLs
23
 * @property bool $beta TRUE if this is a beta package.
24
 * @property string $firmware Minimum firmware needed on device.
25
 * @property string $install_dep_services Dependencies required by this package.
26
 * @property bool $silent_install Allow silent install
27
 * @property bool $silent_uninstall Allow silent uninstall
28
 * @property bool $silent_upgrade Allow silent upgrade
29
 */
30
class Package
31
{
32
    private $filepath;
33
    private $filepathNoExt;
34
    private $filename;
35
    private $filenameNoExt;
36
    private $metafile;
37
    private $metadata;
38
39
    /**
40
     * @param string $filename Filename of SPK file
41
     */
42 17
    public function __construct($filename)
43
    {
44 17
        if (!preg_match('/\.spk$/', $filename)) {
45 1
            throw new \Exception('File ' . $filename . ' doesn\'t have .spk extension!');
46
        }
47 16
        if (!file_exists($filename)) {
48 1
            throw new \Exception('File ' . $filename . ' not found!');
49
        }
50 15
        $this->filepath      = $filename;
51 15
        $this->filepathNoExt = substr($filename, 0, -4);
52 15
        $this->filename      = basename($filename);
53 15
        $this->filenameNoExt = basename($filename, '.spk');
54 15
        $this->metafile      = $this->filepathNoExt . '.nfo';
55 15
    }
56
57
    /**
58
     * Getter magic method.
59
     *
60
     * @param string $name Name of requested value.
61
     * @return mixed Requested value.
62
     */
63 10
    public function __get($name)
64
    {
65 10
        $this->collectMetadata();
66 10
        return $this->metadata[$name];
67
    }
68
69
    /**
70
     * Setter magic method.
71
     *
72
     * @param string $name Name of variable to set.
73
     * @param mixed $value Value to set.
74
     */
75 5
    public function __set($name, $value)
76
    {
77 5
        $this->collectMetadata();
78 5
        $this->metadata[$name] = $value;
79 5
    }
80
81
    /**
82
     * Isset feature magic method.
83
     *
84
     * @param string $name Name of requested value.
85
     * @return bool TRUE if value exists, FALSE otherwise.
86
     */
87 6
    public function __isset($name)
88
    {
89 6
        $this->collectMetadata();
90 6
        return isset($this->metadata[$name]);
91
    }
92
93
    /**
94
     * Unset feature magic method.
95
     *
96
     * @param string $name Name of value to unset.
97
     */
98 1
    public function __unset($name)
99
    {
100 1
        $this->collectMetadata();
101 1
        unset($this->metadata[$name]);
102 1
    }
103
104
    /**
105
     * Parses boolean value ('yes', '1', 'true') into
106
     * boolean type.
107
     *
108
     * @param mixed $value Input value
109
     * @return bool Boolean interpretation of $value.
110
     */
111 8
    public function parseBool($value)
112
    {
113 8
        return in_array($value, array('true', 'yes', '1', 1));
114
    }
115
116
    /**
117
     * Checks if given property $prop exists and converts it
118
     * into a boolean value.
119
     *
120
     * @param string $prop Property to convert
121
     */
122 12
    private function fixBoolIfExist($prop)
123
    {
124 12
        if (isset($this->metadata[$prop])) {
125 8
            $this->metadata[$prop] = $this->parseBool($this->metadata[$prop]);
126 8
        }
127 12
    }
128
129
    /**
130
     * Gathers metadata from package. Extracts INFO file if neccessary.
131
     */
132 12
    private function collectMetadata()
133
    {
134 12
        if (!is_null($this->metadata)) {
135
            // metadata already collected
136 12
            return;
137
        }
138 12
        $this->extractIfMissing('INFO', $this->metafile);
139 12
        $this->metadata = parse_ini_file($this->metafile);
140 12
        if (!isset($this->metadata['displayname'])) {
141 12
            $this->metadata['displayname'] = $this->metadata['package'];
142 12
        }
143 12
        $this->metadata['spk'] = $this->filepath;
144
145
        // Convert architecture(s) to array, as multiple architectures can be specified
146 12
        $this->metadata['arch'] = explode(' ', $this->metadata['arch']);
147
148 12
        $this->fixBoolIfExist('silent_install');
149 12
        $this->fixBoolIfExist('silent_uninstall');
150 12
        $this->fixBoolIfExist('silent_upgrade');
151
152 12
        if (isset($this->metadata['beta']) && in_array($this->metadata['beta'], array('true', '1', 'beta'))) {
153 4
            $this->metadata['beta'] = true;
154 4
        } else {
155 12
            $this->metadata['beta'] = false;
156
        }
157
158 12
        $this->metadata['thumbnail'] = $this->getThumbnails();
159 12
        $this->metadata['snapshot']  = $this->getSnapshots();
160 12
    }
161
162
    /**
163
     * Returns metadata for this package.
164
     *
165
     * @return array Metadata.
166
     */
167 3
    public function getMetadata()
168
    {
169 3
        $this->collectMetadata();
170 3
        return $this->metadata;
171
    }
172
173
    /**
174
     * Extracts $inPkgName from package to $targetFile, if it doesn't
175
     * already exist. Needs the phar.so extension and allow_url_fopen.
176
     *
177
     * @param string $inPkgName Filename in package
178
     * @param string $targetFile Path to destination
179
     * @throws \Exception if the file couldn't get extracted.
180
     * @return bool TRUE if successful or no action needed.
181
     */
182 13
    public function extractIfMissing($inPkgName, $targetFile)
183
    {
184 13
        if (file_exists($targetFile)) {
185
            // Everything in working order
186 8
            return true;
187
        }
188
        // Try to extract file
189 13
        $tmp_dir = sys_get_temp_dir();
190 13
        $free_tmp = disk_free_space($tmp_dir);
191 13
        if ($free_tmp < 2048) {
192
            throw new \Exception('TMP folder only has ' . $free_tmp . ' Bytes space available. Disk full!');
193
        }
194 13
        $free = disk_free_space(dirname($targetFile));
195 13
        if ($free < 2048) {
196
            throw new \Exception('Package folder only has ' . $free . ' Bytes space available. Disk full!');
197
        }
198
        try {
199 13
            $p = new \PharData($this->filepath, \Phar::CURRENT_AS_FILEINFO | \Phar::KEY_AS_FILENAME);
200 13
        } catch (\UnexpectedValueException $e) {
201
            rename($this->filepath, $this->filepath . '.invalid');
202
            throw new \Exception('Package ' . $this->filepath . ' not readable! Will be ignored in the future. Please try again!');
203
        }
204 13
        $p->extractTo($tmp_dir, $inPkgName);
205 13
        rename($tmp_dir . DIRECTORY_SEPARATOR . $inPkgName, $targetFile);
206 13
        return true;
207
    }
208
209
    /**
210
     * Returns a list of thumbnails for the specified package.
211
     *
212
     * @param string $pathPrefix Prefix to put before file path
213
     * @return array List of thumbnail urls
214
     */
215 12
    public function getThumbnails($pathPrefix = '')
216
    {
217
        $thumbnailSources = array(
218
            '72' => array(
219 12
                'file' => 'PACKAGE_ICON.PNG',
220 12
                'info' => 'package_icon',
221 12
            ),
222
            '120' => array(
223 12
                'file' => 'PACKAGE_ICON_256.PNG',
224 12
                'info' => 'package_icon_256',
225 12
            ),
226 12
        );
227 12
        $thumbnails = array();
228 12
        foreach ($thumbnailSources as $size => $sourceList) {
229 12
            $thumbName = $this->filepathNoExt . '_thumb_' . $size . '.png';
230
            // Try to find file in package, otherwise check if defined in INFO
231
            try {
232 12
                $this->extractIfMissing($sourceList['file'], $thumbName);
233 12
            } catch (\Exception $e) {
234
                // Check if icon is in metadata
235 12
                $this->collectMetadata();
236 12
                if (isset($this->metadata[$sourceList['info']])) {
237 9
                    file_put_contents($thumbName, base64_decode($this->metadata[$sourceList['info']]));
238 9
                }
239
            }
240
241
            // Use $size px thumbnail, if available
242 12
            if (file_exists($thumbName)) {
243 12
                $thumbnails[] = $pathPrefix . $thumbName;
244 12
            } else {
245 12
                $thumbnails[] = $pathPrefix . dirname($thumbName) . '/default_package_icon_' . $size . '.png';
246
            }
247 12
        }
248 12
        return $thumbnails;
249
    }
250
251
    /**
252
     * Returns a list of screenshots for the specified package.
253
     *
254
     * @param string $pathPrefix Prefix to put before file path
255
     * @return array List of screenshots
256
     */
257 12
    public function getSnapshots($pathPrefix = '')
258
    {
259
        /* Let's first try to extract screenshots from package (SSpkS feature) */
260 12
        $i = 1;
261 12
        while (true) {
262
            try {
263 12
                $this->extractIfMissing('screen_' . $i . '.png', $this->filepathNoExt . '_screen_' . $i . '.png');
264 7
                $i++;
265 12
            } catch (\Exception $e) {
266 12
                break;
267
            }
268 7
        }
269 12
        $snapshots = array();
270
        // Add screenshots, if available
271 12
        foreach (glob($this->filepathNoExt . '*_screen_*.png') as $snapshot) {
272 7
            $snapshots[] = $pathPrefix . $snapshot;
273 12
        }
274 12
        return $snapshots;
275
    }
276
277
    /**
278
     * Checks compatibility to the given $arch-itecture.
279
     *
280
     * @param string $arch Architecture to check against (or "noarch")
281
     * @return bool TRUE if compatible, otherwise FALSE.
282
     */
283 1
    public function isCompatibleToArch($arch)
284
    {
285
        // Make sure we have metadata available
286 1
        $this->collectMetadata();
287 1
        return (in_array($arch, $this->metadata['arch']) || in_array('noarch', $this->metadata['arch']));
288
    }
289
290
    /**
291
     * Checks compatibility to the given firmware $version.
292
     *
293
     * @param string $version Target firmware version.
294
     * @return bool TRUE if compatible, otherwise FALSE.
295
     */
296 1
    public function isCompatibleToFirmware($version)
297
    {
298 1
        $this->collectMetadata();
299 1
        return version_compare($this->metadata['firmware'], $version, '<=');
300
    }
301
302
    /**
303
     * Checks if this package is a beta version or not.
304
     *
305
     * @return bool TRUE if this is a beta version, FALSE otherwise.
306
     */
307 1
    public function isBeta()
308
    {
309 1
        $this->collectMetadata();
310 1
        return (isset($this->metadata['beta']) && $this->metadata['beta'] == true);
311
    }
312
}
313