HelperFile.php 23 KB
Newer Older
1
2
3
4
5
6
7
8
<?php
/**
 * Created by PhpStorm.
 * User: crose
 * Date: 1/28/16
 * Time: 8:05 AM
 */

Marc Egger's avatar
Marc Egger committed
9
namespace IMATHUZH\Qfq\Core\Helper;
10

Marc Egger's avatar
Marc Egger committed
11

Marc Egger's avatar
Marc Egger committed
12
13
use IMATHUZH\Qfq\Core\Exception\Thrower;

14
15
16
17
/**
 * Class HelperFile
 * @package qfq
 */
18
19
20
21
22
23
class HelperFile {

    /**
     * Iterate over array $files. Delete only named files which are stored in '/tmp/' . DOWNLOAD_FILE_PREFIX.
     *
     * @param array $files
Marc Egger's avatar
Marc Egger committed
24
25
     * @throws \CodeException
     * @throws \UserFormException
26
27
28
29
30
     */
    public static function cleanTempFiles(array $files) {

        foreach ($files as $file) {
            if (self::isQfqTemp($file)) {
31
                self::unlink($file);
32
33
34
35
            }

            $dir = dirname($file);
            if (self::isQfqTemp($dir)) {
36
                self::rmdir($dir);
37
38
39
40
            }
        }
    }

41
    /**
42
     * Returns a uniq (use for temporary) filename, prefixed with QFQ TMP_FILE_PREFIX
43
44
45
     *
     * @return bool|string
     */
46
    public static function tempnam() {
47
48
        return tempnam(sys_get_temp_dir(), TMP_FILE_PREFIX);
    }
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65

    /**
     * Check against standard QFQ Temp location. If it is a Qfq Temp location, return true, else false.
     *
     * @param string $name Absolute filename.
     *
     * @return bool
     */
    public static function isQfqTemp($name) {
        $prefix = sys_get_temp_dir() . '/' . TMP_FILE_PREFIX;
        $len = strlen($prefix);

        return (substr($name, 0, $len) == $prefix);
    }

    /**
     * Creates a temporary directory.
66
67
     * Be aware: '/tmp' is under systemd/apache2 (Ubuntu 18...) remapped to something like: '/tmp/systemd-private-...-apache2.service-.../tmp'
     *
Carsten  Rose's avatar
Carsten Rose committed
68
     * @return bool|string
Marc Egger's avatar
Marc Egger committed
69
70
     * @throws \CodeException
     * @throws \UserFormException
71
72
73
     */
    public static function mktempdir() {
        $name = tempnam(sys_get_temp_dir(), TMP_FILE_PREFIX);
74
        self::unlink($name);
75
76
77
78
        mkdir($name);

        return $name;
    }
79
80
81
82
83

    /**
     * Return the mimetype of $pathFilename
     *
     * @param string $pathFilename
84
     * @param bool $flagIgnoreError
85
     * @return string
Marc Egger's avatar
Marc Egger committed
86
     * @throws \UserFormException
87
     */
88
    public static function getMimeType($pathFilename, $flagIgnoreError = false) {
89
90
91
92

        // E.g.: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet; charset=binary'
        $fileMimeType = exec('file --brief --mime ' . $pathFilename, $output, $return_var);
        if ($return_var != 0) {
93
94
95
96
97

            if ($flagIgnoreError) {
                return '';
            }

Marc Egger's avatar
Marc Egger committed
98
            throw new \UserFormException('Error get mime type of upload.', ERROR_UPLOAD_GET_MIME_TYPE);
99
100
101
102
103
104
105
        }

        return $fileMimeType;
    }

    /**
     * Returns an array with filestat information to $pathFileName
106
107
     * - mimeType
     * - fileSize
108
     *
109
     * @param $pathFileNameRelToApp
110
     * @return array
Marc Egger's avatar
Marc Egger committed
111
     * @throws \UserFormException
112
     */
113
    public static function getFileStat($pathFileNameRelToApp) {
114
        $vars = [VAR_FILE_MIME_TYPE => '-', VAR_FILE_SIZE => '-'];
115

116
        if (empty($pathFileNameRelToApp)) {
117
            return $vars;
118
119
        }

Marc Egger's avatar
Marc Egger committed
120
        $pathFileNameRelToCwd = Path::cwdToApp($pathFileNameRelToApp);
121

Marc Egger's avatar
Marc Egger committed
122
        if (!file_exists($pathFileNameRelToCwd)) {
123
            return $vars;
124
125
        }

Marc Egger's avatar
Marc Egger committed
126
127
        $vars[VAR_FILE_MIME_TYPE] = self::getMimeType($pathFileNameRelToCwd);
        $vars[VAR_FILE_SIZE] = filesize($pathFileNameRelToCwd);
128
129
130

        if ($vars[VAR_FILE_SIZE] === false) {
            $vars[VAR_FILE_SIZE] = '-';
131
        }
132
133
134
135
136
137
138
139
140
141
142
143
144

        return $vars;
    }

    /**
     * Split $pathFileName into it's components and fill an array, with array keys like used in STORE_VAR.
     *
     * @param string $pathFileName
     * @return array
     */
    public static function pathInfo($pathFileName) {
        $vars = array();

145
        $pathParts = pathinfo($pathFileName);
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
        $vars[VAR_FILENAME] = $pathFileName;

        if (isset($pathParts['basename'])) {
            $vars[VAR_FILENAME_ONLY] = $pathParts['basename'];
        }

        if (isset($pathParts['filename'])) {
            $vars[VAR_FILENAME_BASE] = $pathParts['filename'];
        }

        if (isset($pathParts['extension'])) {
            $vars[VAR_FILENAME_EXT] = $pathParts['extension'];
        }

        return $vars;
    }

163
164
165
166
167
168
169
170
171
172
173
174
175
    /**
     * Checks the file filetype against the allowed mimeType definition. Return true as soon as one match is found.
     * Types recognized:
     *   * 'mime type' as delivered by `file` which matches a definition on
     *   http://www.iana.org/assignments/media-types/media-types.xhtml
     *   * Joker based: audio/*, video/*, image/*
     *   * Filename extension based: .pdf,.doc,..
     *
     * @param string $pathFileName
     * @param string $origFileName
     * @param string $mimeTypeListAccept
     *
     * @return bool
Marc Egger's avatar
Marc Egger committed
176
     * @throws \UserFormException
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
     */
    public static function checkFileType($pathFileName, $origFileName, $mimeTypeListAccept) {

        // E.g.: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet; charset=binary'
        $fileMimeType = self::getMimeType($pathFileName);

        // Strip optional '; charset=binary'
        $arr = explode(';', $fileMimeType, 2);
        $fileMimeType = $arr[0];

        // Split between 'Media Type' and 'Media Subtype'
        $fileMimeTypeSplitted = explode('/', $arr[0], 2);

        $path_parts = pathinfo($origFileName); // to extract the filename extension of the uploaded file.

        // Process all listed mimeTypes (incl. filename extension and joker)
        // $accept e.g.: 'image/*,application/pdf,.pdf'
        $arr = explode(',', $mimeTypeListAccept); // Split multiple defined mimetypes/extensions in single chunks.
        foreach ($arr as $listElementMimeType) {
            $listElementMimeType = trim(strtolower($listElementMimeType));
            if ($listElementMimeType == '') {
                continue; // will be skipped
            } elseif ($listElementMimeType[0] == '.') { // Check for definition 'filename extension'
                if ('.' . strtolower($path_parts['extension']) == $listElementMimeType) {
                    return true;
                }
            } else {
                // Check for Joker, e.g.: 'image/*'
                $splitted = explode('/', $listElementMimeType, 2);

                if ($splitted[1] == '*') {
                    if ($splitted[0] == $fileMimeTypeSplitted[0]) {
                        return true;
                    }
                } elseif ($fileMimeType == $listElementMimeType) {
                    return true;
                }
            }
        }

        return false;
    }
219
220
221

    /**
     * @param $pathFileName
222
     * @param int|bool $mode
Marc Egger's avatar
Marc Egger committed
223
     * @throws \UserFormException
224
     */
225
    public static function chmod($pathFileName, $mode = false) {
226

227
        if ($mode !== false) {
228
            if (false === @chmod($pathFileName, $mode)) {
Marc Egger's avatar
Marc Egger committed
229
                throw new \UserFormException(
230
                    json_encode([ERROR_MESSAGE_TO_USER => 'Failed: chmod', ERROR_MESSAGE_TO_DEVELOPER => self::errorGetLastAsString()]),
231
                    ERROR_IO_CHMOD);
232
233
234
            }
        }
    }
235
236
237
238

    /**
     * @return string
     */
239
    public static function errorGetLastAsString() {
240
241

        if (NULL === ($errors = error_get_last())) {
Carsten  Rose's avatar
Carsten Rose committed
242
            return 'error_get_last(): no error';
243
244
245
246
247
        }

        return $errors['type'] . ' - ' . $errors['message'];

    }
248
249
250
251
252
253

    /**
     * PHP System function: chdir() with QFQ exception
     *
     * @param $cwd
     * @return string
Marc Egger's avatar
Marc Egger committed
254
     * @throws \UserFormException
255
256
257
     */
    public static function chdir($cwd) {

258
        if (false === @chdir($cwd)) {
259
            $msg = self::errorGetLastAsString() . " - chdir($cwd)";
Marc Egger's avatar
Marc Egger committed
260
            throw new \UserFormException(json_encode([ERROR_MESSAGE_TO_USER => 'chdir failed', ERROR_MESSAGE_TO_DEVELOPER => $msg]), ERROR_IO_CHDIR);
261
262
263
264
265
266
267
268
        }

        return true;
    }

    /**
     * PHP System function: unlink() with QFQ exception
     *
269
270
     * @param $filename
     * @param string $logFilename
271
     * @return string
Marc Egger's avatar
Marc Egger committed
272
273
     * @throws \CodeException
     * @throws \UserFormException
274
     */
275
    public static function unlink($filename, $logFilename = '') {
276

277
278
279
280
        if ($logFilename != '') {
            Logger::logMessageWithPrefix("Unlink: $filename", $logFilename);
        }

281
        if (false === @unlink($filename)) {
282
            $msg = self::errorGetLastAsString() . " - unlink($filename)";
Marc Egger's avatar
Marc Egger committed
283
            throw new \UserFormException(json_encode([ERROR_MESSAGE_TO_USER => 'unlink failed', ERROR_MESSAGE_TO_DEVELOPER => $msg]), ERROR_IO_UNLINK);
284
285
286
287
288
289
290
291
292
293
        }

        return true;
    }

    /**
     * PHP System function: rmdir() with QFQ exception
     *
     * @param $tempDir
     * @return string
Marc Egger's avatar
Marc Egger committed
294
     * @throws \UserFormException
295
296
297
     */
    public static function rmdir($tempDir) {

298
        if (false === @rmdir($tempDir)) {
299
            $msg = self::errorGetLastAsString() . " - rmdir($tempDir)";
Marc Egger's avatar
Marc Egger committed
300
            throw new \UserFormException(json_encode([ERROR_MESSAGE_TO_USER => 'rmdir failed', ERROR_MESSAGE_TO_DEVELOPER => $msg]), ERROR_IO_RMDIR);
301
302
303
304
305
306
307
308
309
310
311
        }

        return true;
    }

    /**
     * PHP System function: rename() with QFQ exception
     *
     * @param $oldname
     * @param $newname
     * @return string
Marc Egger's avatar
Marc Egger committed
312
     * @throws \UserFormException
313
314
315
     */
    public static function rename($oldname, $newname) {

316
        if (false === @rename($oldname, $newname)) {
317
            $msg = self::errorGetLastAsString() . " - rename($oldname ,$newname)";
Marc Egger's avatar
Marc Egger committed
318
            throw new \UserFormException(json_encode([ERROR_MESSAGE_TO_USER => 'unlink failed', ERROR_MESSAGE_TO_DEVELOPER => $msg]), ERROR_IO_RENAME);
319
320
321
322
323
324
325
326
327
328
329
        }

        return true;
    }

    /**
     * PHP System function: copy() with QFQ exception
     *
     * @param $source
     * @param $dest
     * @return string
Marc Egger's avatar
Marc Egger committed
330
     * @throws \UserFormException
331
332
333
     */
    public static function copy($source, $dest) {

Carsten  Rose's avatar
Carsten Rose committed
334
335
336
337
338
        self::mkDirParent($dest);
        if (!is_file($dest)) {
            touch($dest);
        }

339
        if (false === @copy($source, $dest)) {
Carsten  Rose's avatar
Carsten Rose committed
340
341

            if (!is_readable($source)) {
Marc Egger's avatar
Marc Egger committed
342
                throw new \UserFormException(json_encode([ERROR_MESSAGE_TO_USER => 'copy failed', ERROR_MESSAGE_TO_DEVELOPER => "Can't read file '$source'"]), ERROR_IO_READ_FILE);
Carsten  Rose's avatar
Carsten Rose committed
343
344
345
            }

            if (!is_writeable($dest)) {
Marc Egger's avatar
Marc Egger committed
346
                throw new \UserFormException(json_encode([ERROR_MESSAGE_TO_USER => 'copy failed', ERROR_MESSAGE_TO_DEVELOPER => "Can't write to file '$dest'"]), ERROR_IO_WRITE_FILE);
Carsten  Rose's avatar
Carsten Rose committed
347
348
349
350
            }

            $msg = self::errorGetLastAsString(); // Often, there is no specific error string.
            $msg .= " - copy($source, $dest)";
Marc Egger's avatar
Marc Egger committed
351
            throw new \UserFormException(json_encode([ERROR_MESSAGE_TO_USER => 'copy failed', ERROR_MESSAGE_TO_DEVELOPER => $msg]), ERROR_IO_COPY);
352
353
354
355
        }

        return true;
    }
Carsten  Rose's avatar
Carsten Rose committed
356
357
358
359
360
361
362

    /**
     * Creates all necessary directories in $pathFileName, but not the last part, the filename. A filename has to be
     * specified.
     *
     * @param string $pathFileName Path with Filename
     * @param bool|int $chmodDir false if not explicit set
Marc Egger's avatar
Marc Egger committed
363
     * @throws \UserFormException
Carsten  Rose's avatar
Carsten Rose committed
364
365
366
     */
    public static function mkDirParent($pathFileName, $chmodDir = false) {
        $path = "";
367
368
369
370
371
372
373
        $cwd = '';

        // Leading '/' will be removed - chdir to / to still use correct path
        if ($pathFileName[0] == '/') {
            $cwd = getcwd();
            self::chdir('/');
        }
Carsten  Rose's avatar
Carsten Rose committed
374
375
376
377

        // Teile "Directory/File.Extension" auf
        $pathParts = pathinfo($pathFileName);

378

Carsten  Rose's avatar
Carsten Rose committed
379
380
381
382
383
384
        // Zerlege Pfad in einzelne Directories
        $arr = explode("/", $pathParts["dirname"]);

        // Durchlaufe die einzelnen Dirs und überprüfe ob sie angelegt sind.
        // Wenn nicht, lege sie an.
        foreach ($arr as $part) {
Carsten  Rose's avatar
Carsten Rose committed
385

Carsten  Rose's avatar
Carsten Rose committed
386
            if ($part == '') {// Happens with '/...'
Carsten  Rose's avatar
Carsten Rose committed
387
388
389
                continue;
            }

Carsten  Rose's avatar
Carsten Rose committed
390
391
392
393
394
            $path .= $part;

            // Check ob der Pfad ein Link ist
            if ("link" == @filetype($path)) {
                if ("0" == ($path1 = readlink($path))) {
Marc Egger's avatar
Marc Egger committed
395
                    throw new \UserFormException("Can't create '$pathFileName': '$path' contains an invalid link.", ERROR_IO_INVALID_LINK);
Carsten  Rose's avatar
Carsten Rose committed
396
397
398
399
                }
            } else {
                if (file_exists($path)) {
                    if ("dir" != filetype($path)) {
Marc Egger's avatar
Marc Egger committed
400
                        throw new \UserFormException("Can't create '$pathFileName': There is already a file with the same name as '$path'", ERROR_IO_DIR_EXIST_AS_FILE);
Carsten  Rose's avatar
Carsten Rose committed
401
402
403
404
405
406
407
408
                    }
                } else {
                    mkdir($path);
                    HelperFile::chmod($path, $chmodDir);
                }
            }
            $path .= "/";
        }
409
410
411
412

        if ($cwd != '') {
            self::chdir($cwd);
        }
Carsten  Rose's avatar
Carsten Rose committed
413
414
    }

Carsten  Rose's avatar
Carsten Rose committed
415
416
417
418
419
420
    /**
     * Determine highlight name, based on the given $highlight or provided filename  (the extension will be used)
     *
     * @param $highlight
     * @param $fileName
     * @return string
Marc Egger's avatar
Marc Egger committed
421
     * @throws \UserFormException
Carsten  Rose's avatar
Carsten Rose committed
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
     */
    public static function getFileTypeHighlight($highlight, $fileName) {

        $extToFileType = [
            'js' => 'javascript.json'
            , 'javascript' => 'javascript.json'
            , 'qfq' => 'highlight.qfq.json'
            , 'php' => 'highlight.php.json'
            , 'py' => 'highlight.py.json'
            , 'python' => 'highlight.py.json'
            , 'matlab' => 'highlight.m.json'
            , 'm' => 'highlight.m.json'
        ];

        switch ($highlight) {
            case FE_HIGHLIGHT_AUTO:
                $arr = explode('.', $fileName);
                $ext = strtolower(end($arr));
                $highlight = (isset($extToFileType[$ext])) ? $extToFileType[$ext] : '';
                break;
            case FE_HIGHLIGHT_JAVASCRIPT:
            case FE_HIGHLIGHT_QFQ:
            case FE_HIGHLIGHT_PYTHON:
            case FE_HIGHLIGHT_MATLAB:
                $ext = $highlight;
                break;
            case FE_HIGHLIGHT_OFF:
            case '':
                $ext = '';
                break;
            default:
Marc Egger's avatar
Marc Egger committed
453
                throw new \UserFormException("Unknown highlight type: " . $highlight, ERROR_UNKNOWN_MODE);
Carsten  Rose's avatar
Carsten Rose committed
454
455
456
457
                break;
        }

        if (isset($extToFileType[$ext])) {
458
            return Path::appToExt(Path::EXT_TO_HIGHLIGHT_JSON_DIR) . '/' . $extToFileType[$ext];
Carsten  Rose's avatar
Carsten Rose committed
459
460
461
462
463
        }

        return '';
    }

464
465
466
467
    /**
     * Get array of split file names. Remove '.', '..', QFQ_TEMP_SOURCE.
     * @param $dir
     * @return array
Marc Egger's avatar
Marc Egger committed
468
     * @throws \UserFormException
469
470
471
472
473
     */
    public static function getSplitFileNames($dir) {

        // Array of created file names.
        if (false === ($files = scandir($dir))) {
Marc Egger's avatar
Marc Egger committed
474
            throw new \UserFormException(
Marc Egger's avatar
Marc Egger committed
475
                json_encode([ERROR_MESSAGE_TO_USER => 'Splitted files not found', ERROR_MESSAGE_TO_DEVELOPER => "[cwd=$dir] scandir(.)" . HelperFile::errorGetLastAsString()]),
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
                ERROR_PDF2JPEG);
        }

        $new = Array();
        foreach ($files as $key => $value) {

            if ($value == '.' || $value == '..' || $value == QFQ_TEMP_SOURCE) {
                continue;
            }

            // IM 'convert' use a strange auto numbering: if there is only one page: split.jpg, if there are multiple pages, first: split-0.jpg, ....
            if ($value == 'split.jpg') {
                $value = 'split-0.jpg';
                self::rename($dir . '/' . $files[$key], $dir . '/' . $value);
            }

            $new[] = $value;
        }

        return $new;
    }
Carsten  Rose's avatar
Carsten Rose committed
497

498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
    /**
     * Joins $pre and $post. If $post is absolute, returns only $post. If $pre ends without '/', a '/' is injected.
     *
     * @param $pre
     * @param $post
     *
     * @return string
     */
    public static function joinPathFilename($pre, $post) {
        $separator = '';

        if ($pre == '' || $post == '') {
            return $pre . $post;
        }

        if ($post[0] == '/') {
            return $post;
        }

        if ((substr($pre, -1) != '/')) {
            $separator = '/';
        }

        return $pre . $separator . $post;
    }
523
524
525
526
527
528
529
530
531
532

    /**
     * Checks if the given file name only contains alphanumeric characters and ".", "-", "_"
     * @param string $fileName
     * @return bool
     */
    public static function isValidFileName(string $fileName): bool
    {
        return preg_match('/^([-\.\w]+)$/', $fileName) > 0;
    }
533
534
535
536
537
538
539

    /**
     * Creates the given path if it does not exist.
     *
     * @param $path
     * @throws \UserFormException
     */
540
    public static function createPathRecursive($path) // : void
541
542
    {
        if (!is_dir($path)) {
Marc Egger's avatar
Marc Egger committed
543
544
545
546

            try {
                $success = mkdir($path, 0777, true);
            } catch (\Error | \Exception $e) {
547
                // DEBUG
Marc Egger's avatar
Marc Egger committed
548
                Thrower::userFormException('HEREEEE: ' . $path . ' >>> MESSAGE: ' . $e->getMessage());
549
                // END DEBUG
Marc Egger's avatar
Marc Egger committed
550
551
            }

552
553
554
555
556
557
558
559
            if ($success === false) {
                throw new \UserFormException(json_encode([
                    ERROR_MESSAGE_TO_USER => "Can't create file path.",
                    ERROR_MESSAGE_TO_DEVELOPER => "Can't create path: " . $path]),
                    ERROR_IO_WRITE_FILE);
            }
        }
    }
560
561
562

    /**
     * Wrapper for file_put_contents which throws exception on failure.
563
     * Clear the stat cache to make sure that stat(), file_exists(),... etc. return current data.
564
565
566
567
568
569
570
571
572
573
574
575
576
577
     *
     * @param $pathFileName
     * @param $content
     * @throws \UserFormException
     */
    public static function file_put_contents($pathFileName, $content) // : void
    {
        $success = file_put_contents($pathFileName, $content);
        if ($success === false) {
            throw new \UserFormException(json_encode([
                ERROR_MESSAGE_TO_USER => "Writing file failed.",
                ERROR_MESSAGE_TO_DEVELOPER => "Can't write to file '$pathFileName'"]),
                ERROR_IO_WRITE_FILE);
        }
578
        clearstatcache (true, $pathFileName);
579
    }
580

581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
    /**
     * Wrapper for file_get_contents which throws exception on failure.
     *
     * @param string $pathFileName
     * @return string
     * @throws \UserFormException
     */
    public static function file_get_contents(string $pathFileName): string
    {
        $fileContents = file_get_contents($pathFileName);
        if ($fileContents === false) {
            throw new \UserFormException(json_encode([
                ERROR_MESSAGE_TO_USER => 'File not readable.',
                ERROR_MESSAGE_TO_DEVELOPER => "Can't read file '$pathFileName'"]), ERROR_IO_READ_FILE);
        }
        return $fileContents;
    }

    /**
     * Wrapper fo json_decode which throws exception on failure.
     *
     * @param string $jsonString
     * @param bool $associativeArray
     * @return array
     * @throws \UserFormException
     */
    public static function json_decode(string $jsonString, bool $associativeArray = true)
    {
        $result = json_decode($jsonString, $associativeArray);
        if (is_null($result)) {
            throw new \UserFormException(json_encode([
                ERROR_MESSAGE_TO_USER => 'Json decode failed.',
                ERROR_MESSAGE_TO_DEVELOPER => "JSON: $jsonString"]), ERROR_IO_READ_FILE);
        }
        return $result;
    }

618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
    /**
     * Wrapper for is_writeable which throws exception on failure.
     * Throws exception if file does not exist!
     *
     * @param $path
     * @throws \UserFormException
     */
    public static function enforce_writable($path)
    {
        if (!is_writeable($path)) {
            throw new \UserFormException(json_encode([
                ERROR_MESSAGE_TO_USER => 'File/directory not found or not writable.',
                ERROR_MESSAGE_TO_DEVELOPER => "Can't write to file/directory '$path'"]), ERROR_IO_WRITE_FILE);
        }
    }

    /**
     * Like enforce_writable but does not throw exception if file does not exist and the containing directory is writable.
     *
     * @param $pathFileName
     * @throws \UserFormException
     */
    public static function enforce_writable_or_creatable($pathFileName)
    {
        if (file_exists($pathFileName)) {
            self::enforce_writable($pathFileName);
        } else {
            self::enforce_writable(dirname($pathFileName));
        }
    }
648
649
650

    /**
     * Wrapper for is_readable() but throws exception if file/directory exists but is not readable.
651
     * Returns false if file does not exist.
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
     *
     * @param $pathFileName
     * @return bool
     * @throws \UserFormException
     */
    public static function isReadableException($pathFileName): bool
    {
        if (!file_exists($pathFileName)) {
            return false;
        }
        if (!is_readable($pathFileName)) {
            throw new \UserFormException(json_encode([
                ERROR_MESSAGE_TO_USER => 'File/directory found but not readable.',
                ERROR_MESSAGE_TO_DEVELOPER => "Can't read existing file/directory '$pathFileName'"]), ERROR_IO_READ_FILE);
        }
        return true;
    }

    /**
     * Wrapper for include() but throws exception on failure.
     *
     * @param string $pathFileName
     * @return mixed
     * @throws \UserFormException
     */
    public static function include(string $pathFileName)
    {
        $result = include($pathFileName);
        if (empty($result) || $result === true) {
            throw new \UserFormException(json_encode([
                ERROR_MESSAGE_TO_USER => 'Error read file.',
                ERROR_MESSAGE_TO_DEVELOPER => "Can't include file '$pathFileName'"]), ERROR_IO_READ_FILE);
        }
        return $result;
    }
687

688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
    /**
     * Translates ZIP error codes to text.
     *
     * @param $errno
     * @return string
     */
    public static function zipFileErrMsg($errno) {

        // using constant name as a string to make this function PHP4 compatible
        $zipFileFunctionsErrors = array(
            'ZIPARCHIVE::ER_MULTIDISK' => 'Multi-disk zip archives not supported.',
            'ZIPARCHIVE::ER_RENAME' => 'Renaming temporary file failed.',
            'ZIPARCHIVE::ER_CLOSE' => 'Closing zip archive failed',
            'ZIPARCHIVE::ER_SEEK' => 'Seek error',
            'ZIPARCHIVE::ER_READ' => 'Read error',
            'ZIPARCHIVE::ER_WRITE' => 'Write error',
            'ZIPARCHIVE::ER_CRC' => 'CRC error',
            'ZIPARCHIVE::ER_ZIPCLOSED' => 'Containing zip archive was closed',
            'ZIPARCHIVE::ER_NOENT' => 'No such file.',
            'ZIPARCHIVE::ER_EXISTS' => 'File already exists',
            'ZIPARCHIVE::ER_OPEN' => 'Can\'t open file',
            'ZIPARCHIVE::ER_TMPOPEN' => 'Failure to create temporary file.',
            'ZIPARCHIVE::ER_ZLIB' => 'Zlib error',
            'ZIPARCHIVE::ER_MEMORY' => 'Memory allocation failure',
            'ZIPARCHIVE::ER_CHANGED' => 'Entry has been changed',
            'ZIPARCHIVE::ER_COMPNOTSUPP' => 'Compression method not supported.',
            'ZIPARCHIVE::ER_EOF' => 'Premature EOF',
            'ZIPARCHIVE::ER_INVAL' => 'Invalid argument',
            'ZIPARCHIVE::ER_NOZIP' => 'Not a zip archive',
            'ZIPARCHIVE::ER_INTERNAL' => 'Internal error',
            'ZIPARCHIVE::ER_INCONS' => 'Zip archive inconsistent',
            'ZIPARCHIVE::ER_REMOVE' => 'Can\'t remove file',
            'ZIPARCHIVE::ER_DELETED' => 'Entry has been deleted',
        );

        return $zipFileFunctionsErrors[$errno] ?? 'unknown';
    }
Carsten  Rose's avatar
Carsten Rose committed
725
726
}