PHP Case Insensitive Version of file_exists()

后端 未结 14 1340
忘掉有多难
忘掉有多难 2020-11-30 07:47

I\'m trying to think of the fastest way to implement a case insensitive file_exists function in PHP. Is my best bet to enumerate the file in the directory and do a strtolowe

相关标签:
14条回答
  • 2020-11-30 08:11

    Having found this page from a quick google I used Kirk's solution, however it's slow if you call it multiple times on the same directory, or on a directory that has many files in. This is due to it looping over all the files each time, so I optimised it a little:

    function fileExists($fileName) {
        static $dirList = [];
        if(file_exists($fileName)) {
            return true;
        }
        $directoryName = dirname($fileName);
        if (!isset($dirList[$directoryName])) {
            $fileArray = glob($directoryName . '/*', GLOB_NOSORT);
            $dirListEntry = [];
            foreach ($fileArray as $file) {
                $dirListEntry[strtolower($file)] = true;
            }
            $dirList[$directoryName] = $dirListEntry;
        }
        return isset($dirList[$directoryName][strtolower($fileName)]);
    }
    

    I dropped the flag to check for case insensitivity as I assume you'd just use file_exists if you didn't need this behaviour, so the flag seemed redundant. I also expect that if you're doing anything beyond a trivial script you'd want to turn this into a class to get more control over the directory list caching, e.g. resetting it, but that's beyond the scope of what I needed and it should be trivial to do if you need it.

    0 讨论(0)
  • 2020-11-30 08:11

    AbraCadaver's answer with +7 rating is incorrect, I do not have enough reputation to comment under it, so here is correct solution, based on his answer:

    $result = count(preg_grep('/\/'.preg_quote($filename)."$/i", glob("$path/*")));
    

    AbraCadaver's answer is incorrect, because it returns true if you test against file foo.jpg and files like anytext_foo.jpg exist.

    0 讨论(0)
  • 2020-11-30 08:13

    My tuned solution, OS independent, case-insensitive realpath() alternative, covering whole path, named realpathi():

    /**
     * Case-insensitive realpath()
     * @param string $path
     * @return string|false
     */
    function realpathi($path)
    {
        $me = __METHOD__;
    
        $path = rtrim(preg_replace('#[/\\\\]+#', DIRECTORY_SEPARATOR, $path), DIRECTORY_SEPARATOR);
        $realPath = realpath($path);
        if ($realPath !== false) {
            return $realPath;
        }
    
        $dir = dirname($path);
        if ($dir === $path) {
            return false;
        }
        $dir = $me($dir);
        if ($dir === false) {
            return false;
        }
    
        $search = strtolower(basename($path));
        $pattern = '';
        for ($pos = 0; $pos < strlen($search); $pos++) {
            $pattern .= sprintf('[%s%s]', $search[$pos], strtoupper($search[$pos]));
        }
        return current(glob($dir . DIRECTORY_SEPARATOR . $pattern));
    }
    

    search filename with glob [nN][aA][mM][eE] pattern seems to be the faster solution

    0 讨论(0)
  • 2020-11-30 08:17

    I ran into the same issue when we migrated from IIS to apache. Below is the piece I whipped up. It returns either the correct path as a string or false.

    function resolve_path($path)
    {
        $is_absolute_path = substr($path, 0, 1) == '/';
        $resolved_path = $is_absolute_path ? '/' : './';
        $path_parts = explode('/', strtolower($path));
    
        foreach ($path_parts as $part)
        {
            if (!empty($part))
            {
                $files = scandir($resolved_path);
    
                $match_found = FALSE;
    
                foreach ($files as $file)
                {
                    if (strtolower($file) == $part)
                    {
                        $match_found = TRUE;
    
                        $resolved_path .= $file . '/';
                    }
                }
    
                if (!$match_found)
                {
                    return FALSE;
                }
            }
        }
    
        if (!is_dir($resolved_path) && !is_file($resolved_path))
        {
            $resolved_path = substr($resolved_path, 0, strlen($resolved_path) - 1);
        }
    
        $resolved_path = $is_absolute_path ? $resolved_path : substr($resolved_path, 2, strlen($resolved_path));
    
        return $resolved_path;
    }
    
    $relative_path = substr($_SERVER['REQUEST_URI'], 1, strlen($_SERVER['REQUEST_URI']));
    $resolved_path = resolve_path($relative_path);
    
    if ($resolved_path)
    {
        header('Location: http://' . $_SERVER['SERVER_NAME'] . '/' . $resolved_path);
        die();
    }
    
    0 讨论(0)
  • 2020-11-30 08:17

    I have improved John Himmelman's function and come up with this:
    suppose that i have a catch system \iMVC\kernel\caching\fileCache

    function resolve_path($path)
    {
        # check if string is valid
        if(!strlen($path)) return FALSE;
        # a primary check
        if(file_exists($path)) return $path;
        # create a cache signiture
        $cache_sig = __METHOD__."@$path";
        # open the cache file
        $fc = new \iMVC\kernel\caching\fileCache(__CLASS__);
        # check cache file and validate it
        if($fc->isCached($cache_sig) && file_exists($fc->retrieve($cache_sig)))
        {
            # it was a HIT!
            return $fc->retrieve($cache_sig);
        }
        # if it is ab
        $is_absolute_path = ($path[0] == DIRECTORY_SEPARATOR);
        # depart the path
        $path_parts = array_filter(explode(DIRECTORY_SEPARATOR, strtolower($path)));
        # normalizing array's parts
        $path_parts = count($path_parts)? array_chunk($path_parts, count($path_parts)) : array();
        $path_parts = count($path_parts[0])?$path_parts[0]:array();
        # UNIX fs style
        $resolved_path = $is_absolute_path ? DIRECTORY_SEPARATOR : ".";
        # WINNT fs style
        if(string::Contains($path_parts[0], ":"))
        {
            $is_absolute_path = 1;
            $resolved_path = $is_absolute_path ? "" : ".".DIRECTORY_SEPARATOR;
        }
        # do a BFS in subdirz
        foreach ($path_parts as $part)
        {
            if (!empty($part))
            {
                $target_path = $resolved_path.DIRECTORY_SEPARATOR.$part;
                if(file_exists($target_path))
                {
                    $resolved_path = $target_path;
                    continue;
                }
                $files = scandir($resolved_path);
    
                $match_found = FALSE;
    
                foreach ($files as $file)
                {   
                    if (strtolower($file) == $part)
                    {
                        $match_found = TRUE;
                        $resolved_path = $resolved_path.DIRECTORY_SEPARATOR.$file;
                        break;
                    }
                }
                if (!$match_found)
                {
                    return FALSE;
                }
            }
        }
        # cache the result
        $fc->store($target_path, $resolved_path);
        # retrun the resolved path
        return $resolved_path;
    }
    
    0 讨论(0)
  • 2020-11-30 08:18

    For a pure PHP implementation, yes. There's an example in the comments for the file_exists function.

    The other option would be to run your script on a case insensitive file system.

    0 讨论(0)
提交回复
热议问题