Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
89.29% covered (warning)
89.29%
25 / 28
50.00% covered (danger)
50.00%
1 / 2
CRAP
0.00% covered (danger)
0.00%
0 / 1
Util
89.29% covered (warning)
89.29%
25 / 28
50.00% covered (danger)
50.00%
1 / 2
10.12
0.00% covered (danger)
0.00%
0 / 1
 slug
100.00% covered (success)
100.00%
10 / 10
100.00% covered (success)
100.00%
1 / 1
3
 isAnimatedGif
83.33% covered (warning)
83.33%
15 / 18
0.00% covered (danger)
0.00%
0 / 1
7.23
1<?php
2
3declare(strict_types=1);
4
5namespace Cosray\Assets;
6
7use Cosray\Exception\RuntimeException;
8use Normalizer;
9use Transliterator;
10
11class Util
12{
13    /**
14     * Conservative lowercase slug of an uploaded filename, safe as URL
15     * path segment and pool basename. May return an empty string or a
16     * bare extension for names without transliterable characters.
17     */
18    public static function slug(string $filename): string
19    {
20        $slug = Normalizer::normalize($filename, Normalizer::FORM_C) ?: $filename;
21        $latin = Transliterator::create('Any-Latin; Latin-ASCII')?->transliterate($slug);
22
23        if (is_string($latin)) {
24            $slug = $latin;
25        }
26
27        $slug = mb_strtolower($slug);
28        $slug = preg_replace('/\s+/u', '-', $slug) ?? '';
29        $slug = preg_replace('/[^a-z0-9._-]/', '', $slug) ?? '';
30        $slug = preg_replace('/-{2,}/', '-', $slug) ?? '';
31        $slug = preg_replace('/\.{2,}/', '.', $slug) ?? '';
32
33        // A leading dot survives so `Storage::key()` can keep the
34        // extension when it swaps in the uid as stem.
35        return rtrim(ltrim($slug, '-'), '-.');
36    }
37
38    public static function isAnimatedGif(string $fileName): bool
39    {
40        // Check if the file exists
41        if (!file_exists($fileName)) {
42            throw new RuntimeException('File does not exist: ' . $fileName);
43        }
44
45        $fileHandle = fopen($fileName, 'rb');
46
47        if ($fileHandle === false) {
48            throw new RuntimeException('File could not be opened: ' . $fileName);
49        }
50
51        try {
52            if (fread($fileHandle, 3) !== 'GIF') {
53                return false;
54            }
55
56            rewind($fileHandle);
57            $frameCount = 0;
58
59            while (!feof($fileHandle)) {
60                $chunk = fread($fileHandle, 1024 * 100);
61
62                if ($chunk === false) {
63                    return false;
64                }
65
66                $frameCount += substr_count($chunk, "\x00\x21\xF9\x04");
67
68                if ($frameCount > 1) {
69                    return true;
70                }
71            }
72
73            return false;
74        } finally {
75            fclose($fileHandle);
76        }
77    }
78}