Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
32 / 32
100.00% covered (success)
100.00%
5 / 5
CRAP
100.00% covered (success)
100.00%
1 / 1
guesser
100.00% covered (success)
100.00%
32 / 32
100.00% covered (success)
100.00%
5 / 5
24
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 register_guessers
100.00% covered (success)
100.00%
11 / 11
100.00% covered (success)
100.00%
1 / 1
8
 sort_priority
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
3
 guess
100.00% covered (success)
100.00%
12 / 12
100.00% covered (success)
100.00%
1 / 1
6
 choose_mime_type
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
6
1<?php
2/**
3*
4* This file is part of the phpBB Forum Software package.
5*
6* @copyright (c) phpBB Limited <https://www.phpbb.com>
7* @license GNU General Public License, version 2 (GPL-2.0)
8*
9* For full copyright and license information, please see
10* the docs/CREDITS.txt file.
11*
12*/
13
14namespace phpbb\mimetype;
15
16class guesser
17{
18    /**
19    * @const Default priority for mimetype guessers
20    */
21    const PRIORITY_DEFAULT = 0;
22
23    /**
24    * @var array guessers
25    */
26    protected $guessers;
27
28    /**
29    * Construct a mimetype guesser object
30    *
31    * @param array $mimetype_guessers Mimetype guesser service collection
32    */
33    public function __construct($mimetype_guessers)
34    {
35        $this->register_guessers($mimetype_guessers);
36    }
37
38    /**
39    * Register MimeTypeGuessers and sort them by priority
40    *
41    * @param array $mimetype_guessers Mimetype guesser service collection
42    *
43    * @throws \LogicException If incorrect or not mimetype guessers have
44    *            been supplied to class
45    */
46    protected function register_guessers($mimetype_guessers)
47    {
48        foreach ($mimetype_guessers as $guesser)
49        {
50            $is_supported = (method_exists($guesser, 'is_supported')) ? 'is_supported' : '';
51            $is_supported = (method_exists($guesser, 'isSupported')) ? 'isSupported' : $is_supported;
52            $is_supported = (method_exists($guesser, 'isGuesserSupported')) ? 'isGuesserSupported' : $is_supported;
53
54            if (empty($is_supported))
55            {
56                throw new \LogicException('Incorrect mimetype guesser supplied.');
57            }
58
59            if ($guesser->$is_supported())
60            {
61                $this->guessers[] = $guesser;
62            }
63        }
64
65        if (empty($this->guessers))
66        {
67            throw new \LogicException('No mimetype guesser supplied.');
68        }
69
70        // Sort guessers by priority
71        usort($this->guessers, array($this, 'sort_priority'));
72    }
73
74    /**
75    * Sort the priority of supplied guessers
76    * This is a compare function for usort. A guesser with higher priority
77    * should be used first and vice versa. usort() orders the array values
78    * from low to high depending on what the comparison function returns
79    * to it. Return value should be smaller than 0 if value a is smaller
80    * than value b. This has been reversed in the comparison function in
81    * order to sort the guessers from high to low.
82    * Method has been set to public in order to allow proper testing.
83    *
84    * @param object $guesser_a Mimetype guesser a
85    * @param object $guesser_b Mimetype guesser b
86    *
87    * @return int     If both guessers have the same priority 0, bigger
88    *        than 0 if first guesser has lower priority, and lower
89    *        than 0 if first guesser has higher priority
90    */
91    public function sort_priority($guesser_a, $guesser_b)
92    {
93        $priority_a = (int) (method_exists($guesser_a, 'get_priority')) ? $guesser_a->get_priority() : self::PRIORITY_DEFAULT;
94        $priority_b = (int) (method_exists($guesser_b, 'get_priority')) ? $guesser_b->get_priority() : self::PRIORITY_DEFAULT;
95
96        return $priority_b - $priority_a;
97    }
98
99    /**
100    * Guess mimetype of supplied file
101    *
102    * @param string $file Path to file
103    * @param string $file_name The real file name
104    *
105    * @return string|false Guess for mimetype of file or false if file can't be opened
106    */
107    public function guess($file, $file_name = '')
108    {
109        if (!is_file($file))
110        {
111            return false;
112        }
113
114        if (!is_readable($file))
115        {
116            return false;
117        }
118
119        $mimetype = 'application/octet-stream';
120
121        $args = (array) func_get_args();
122        foreach ($this->guessers as $guesser)
123        {
124            $guess = (method_exists($guesser, 'guess')) ? 'guess' : '';
125            $guess = (method_exists($guesser, 'guessMimeType')) ? 'guessMimeType' : $guess;
126
127            $mimetype_guess = $guesser->$guess(...$args);
128
129            $mimetype = $this->choose_mime_type($mimetype, $mimetype_guess);
130        }
131        // Return any mimetype if we got a result or the fallback value
132        return $mimetype;
133    }
134
135    /**
136     * Choose the best mime type based on the current mime type and the guess
137     * If a guesser returns nulls or application/octet-stream, we will keep
138     * the current guess. Guesses with a slash inside them will be favored over
139     * already existing ones. However, any guess that will pass the first check
140     * will always overwrite the default application/octet-stream.
141     *
142     * @param    string    $mime_type    The current mime type
143     * @param    string|null|false    $guess        The current mime type guess
144     *
145     * @return string The best mime type based on current mime type and guess
146     */
147    public function choose_mime_type($mime_type, $guess)
148    {
149        if ($guess === false || $guess === null || $guess == 'application/octet-stream')
150        {
151            return $mime_type;
152        }
153
154        if ($mime_type == 'application/octet-stream' || strpos($guess, '/') !== false)
155        {
156            $mime_type = $guess;
157        }
158
159        return $mime_type;
160    }
161}