Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
56.52% covered (warning)
56.52%
13 / 23
0.00% covered (danger)
0.00%
0 / 3
CRAP
0.00% covered (danger)
0.00%
0 / 1
FileExists
56.52% covered (warning)
56.52%
13 / 23
0.00% covered (danger)
0.00%
0 / 3
13.26
0.00% covered (danger)
0.00%
0 / 1
 warningHandler
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 fileExists
84.62% covered (warning)
84.62%
11 / 13
0.00% covered (danger)
0.00%
0 / 1
3.03
 fileExistsTryHarder
25.00% covered (danger)
25.00%
2 / 8
0.00% covered (danger)
0.00%
0 / 1
10.75
1<?php
2
3namespace FileUtil;
4
5use FileUtil\FileExistsUsingExec;
6
7/**
8 * A fileExist function free of deception
9 *
10 * @package    FileUtil
11 * @author     Bjørn Rosell <it@rosell.dk>
12 */
13class FileExists
14{
15
16    private static $lastWarning;
17
18    /**
19     * A warning handler that registers that a warning has occured and suppresses it.
20     *
21     * The function is a callback used with "set_error_handler".
22     * It is declared public because it needs to be accessible from the point where the warning is triggered.
23     *
24     * @param  integer  $errno
25     * @param  string   $errstr
26     * @param  string   $errfile
27     * @param  integer  $errline
28     *
29     * @return void
30     */
31    public static function warningHandler($errno, $errstr, $errfile, $errline)
32    {
33        self::$lastWarning = [$errstr, $errno];
34
35        // Suppress the warning by returning void
36        return;
37    }
38
39    /**
40     * A well behaved replacement for file_exist that throws upon failure rather than emitting a warning.
41     *
42     * @throws \Exception Throws an exception in case file_exists emits a warning
43     * @return boolean  True if file exists. False if it doesn't.
44     */
45    public static function fileExists($path)
46    {
47        // There is a challenges here:
48        // We want to suppress warnings, but at the same time we want to know that it happened.
49        // We achieve this by registering an error handler
50        set_error_handler(
51            array('FileUtil\FileExists', "warningHandler"),
52            E_WARNING | E_USER_WARNING | E_NOTICE | E_USER_NOTICE
53        );
54        self::$lastWarning = null;
55        $found = @file_exists($path);
56
57        // restore previous error handler immediately
58        restore_error_handler();
59
60        // If file_exists returns true, we can rely on there being a file there
61        if ($found) {
62            return true;
63        }
64
65        // file_exists returned false.
66        // this result is only trustworthy if no warning was emitted.
67        // @phpstan-ignore if.alwaysTrue
68        if (is_null(self::$lastWarning)) {
69            return false;
70        }
71
72        list($errstr, $errno) = self::$lastWarning; // @phpstan-ignore deadCode.unreachable
73        throw new \Exception($errstr, $errno);
74    }
75
76    /**
77     * A fileExist doing the best it can.
78     *
79     * @throws \Exception  If it cannot be determined if the file exists
80     * @return boolean  True if file exists. False if it doesn't.
81     */
82    public static function fileExistsTryHarder($path)
83    {
84        try {
85            $result = self::fileExists($path);
86        } catch (\Exception $e) {
87            try {
88                $result = FileExistsUsingExec::fileExists($path);
89            } catch (\Exception $e) {
90                throw new \Exception('Cannot determine if file exists or not');
91            } catch (\Throwable $e) {
92                throw new \Exception('Cannot determine if file exists or not');
93            }
94        }
95        return $result;
96    }
97}