Passed
Branch master (b0d099)
by Markus
03:49
created

Package::__construct()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 14
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 3

Importance

Changes 7
Bugs 0 Features 0
Metric Value
cc 3
eloc 10
nc 3
nop 1
dl 0
loc 14
rs 9.4285
c 7
b 0
f 0
ccs 11
cts 11
cp 1
crap 3
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 11
            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 1
            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
        try {
218 12
            $this->extractIfMissing('PACKAGE_ICON.PNG', $this->filepathNoExt . '_thumb_72.png');
219 12
        } catch (\Exception $e) {
220
            // Check if icon is in metadata
221 9
            $this->collectMetadata();
222 9
            if (isset($this->metadata['package_icon'])) {
223 9
                file_put_contents($this->filepathNoExt . '_thumb_72.png', base64_decode($this->metadata['package_icon']));
224 9
            }
225
        }
226 12
        $thumbnails = array();
227 12
        foreach (array('72', '120') as $size) {
228 12
            $thumb_name = $this->filepathNoExt . '_thumb_' . $size . '.png';
229
            // Use $size px thumbnail, if available
230 12
            if (file_exists($thumb_name)) {
231 12
                $thumbnails[] = $pathPrefix . $thumb_name;
232 12
            } else {
233 12
                $thumbnails[] = $pathPrefix . dirname($thumb_name) . '/default_package_icon_' . $size . '.png';
234
            }
235 12
        }
236 12
        return $thumbnails;
237
    }
238
239
    /**
240
     * Returns a list of screenshots for the specified package.
241
     *
242
     * @param string $pathPrefix Prefix to put before file path
243
     * @return array List of screenshots
244
     */
245 12
    public function getSnapshots($pathPrefix = '')
246
    {
247 12
        $snapshots = array();
248
        // Add screenshots, if available
249 12
        foreach (glob($this->filepathNoExt . '*_screen_*.png') as $snapshot) {
250 7
            $snapshots[] = $pathPrefix . $snapshot;
251 12
        }
252 12
        return $snapshots;
253
    }
254
255
    /**
256
     * Checks compatibility to the given $arch-itecture.
257
     *
258
     * @param string $arch Architecture to check against (or "noarch")
259
     * @return bool TRUE if compatible, otherwise FALSE.
260
     */
261 1
    public function isCompatibleToArch($arch)
262
    {
263
        // Make sure we have metadata available
264 1
        $this->collectMetadata();
265 1
        return (in_array($arch, $this->metadata['arch']) || in_array('noarch', $this->metadata['arch']));
266
    }
267
268
    /**
269
     * Checks compatibility to the given firmware $version.
270
     *
271
     * @param string $version Target firmware version.
272
     * @return bool TRUE if compatible, otherwise FALSE.
273
     */
274 1
    public function isCompatibleToFirmware($version)
275
    {
276 1
        $this->collectMetadata();
277 1
        return version_compare($this->metadata['firmware'], $version, '<=');
278
    }
279
280
    /**
281
     * Checks if this package is a beta version or not.
282
     *
283
     * @return bool TRUE if this is a beta version, FALSE otherwise.
284
     */
285 1
    public function isBeta()
286
    {
287 1
        $this->collectMetadata();
288 1
        return (isset($this->metadata['beta']) && $this->metadata['beta'] == true);
289
    }
290
}
291