PHP fnmatch() - Match Filename Pattern
Learn PHP fnmatch() function. Match a filename against a pattern using shell-style wildcards for precise pattern matching.
Introduction
The fnmatch() function in PHP is a powerful tool for filename pattern matching. It allows developers to compare a filename or string against a shell-style wildcard pattern, commonly used in filesystem operations like filtering files or validating filenames.
This function supports wildcard characters including *, ?, and character ranges [a-z], enabling flexible and efficient pattern matching for filenames or any string data that follows similar conventions.
Prerequisites
- Basic understanding of PHP syntax and functions.
- Familiarity with filesystem concepts like filenames and patterns.
- PHP environment configured (version 4.3.0+ as
fnmatch()was introduced in PHP 4.3.0). - Optional: Command line or web server setup to run and test PHP scripts.
Setup Steps
- Ensure PHP 4.3.0 or higher is installed (recommended PHP 7+).
- Create a new PHP file, e.g.,
fnmatch_demo.php. - Use the
fnmatch()function directlyβno additional libraries or packages are needed.
Understanding PHP fnmatch() Function
Function signature:
bool fnmatch(string $pattern, string $string [, int $flags = 0])
$pattern: The shell wildcard pattern to match against.$string: The string (usually filename) to test.$flags: Optional flags altering matching behavior.
Supported Wildcards:
*- matches any string (including empty string).?- matches any single character.[seq]- matches any character in seq.[!seq]or[^seq]- matches any character not in seq.
Available Flags
FNM_NOESCAPE- disables backslash escaping.FNM_PATHNAME- slash (/) must be matched explicitly.FNM_PERIOD- leading period must be matched explicitly.FNM_CASEFOLD- match case-insensitively (available on some systems).
Examples Explained
Example 1: Basic wildcard matching
<?php
$pattern = "*.txt";
$filename = "document.txt";
if (fnmatch($pattern, $filename)) {
echo "$filename matches pattern $pattern";
} else {
echo "$filename does NOT match pattern $pattern";
}
?>
Output: document.txt matches pattern *.txt
Explanation: The pattern *.txt matches any filename ending with ".txt".
Example 2: Using single character wildcard "?"
<?php
$pattern = "file?.php";
$filename1 = "file1.php";
$filename2 = "file12.php";
echo fnmatch($pattern, $filename1) ? "Match\n" : "No match\n"; // Match
echo fnmatch($pattern, $filename2) ? "Match\n" : "No match\n"; // No match
?>
Explanation: The ? wildcard matches exactly one character. Hence file1.php matches but file12.php does not.
Example 3: Matching specific character ranges
<?php
$pattern = "image[0-9].png";
$files = ["image1.png", "imagea.png", "image9.png"];
foreach ($files as $file) {
if (fnmatch($pattern, $file)) {
echo "$file matches\n";
} else {
echo "$file does NOT match\n";
}
}
?>
Output:
image1.png matches
imagea.png does NOT match
image9.png matches
Explanation: The pattern matches filenames where the character in brackets is a digit between 0 and 9.
Example 4: Case-insensitive matching with FNM_CASEFOLD
<?php
$pattern = "*.jpg";
$filename = "PHOTO.JPG";
if (fnmatch($pattern, $filename, FNM_CASEFOLD)) {
echo "Case-insensitive match successful";
} else {
echo "No match";
}
?>
Note: FNM_CASEFOLD availability depends on the platform.
Example 5: Using FNM_PATHNAME flag to treat slashes specially
<?php
$pattern = "data/*/file.txt";
$filename1 = "data/folder/file.txt";
$filename2 = "data/folder/subfolder/file.txt";
echo fnmatch($pattern, $filename1, FNM_PATHNAME) ? "Match\n" : "No match\n"; // Match
echo fnmatch($pattern, $filename2, FNM_PATHNAME) ? "Match\n" : "No match\n"; // No match
?>
Explanation: With FNM_PATHNAME enabled, * does not match directory separators (/), so only one folder depth matches.
Best Practices
- Always validate the pattern input, especially if coming from users, to avoid unexpected matching.
- Use
FNM_PATHNAMEwhen patterns should not match across directory boundaries. - Remember that
fnmatch()performs shell-style pattern matching, not regular expressions. Use regex functions for complex patterns. - Be cautious about platform differences, especially when using
FNM_CASEFOLDas case-insensitivity flag support is system dependent. - Use
FNM_NOESCAPEfor patterns where backslashes are literal characters.
Common Mistakes
- Confusing
fnmatch()with regular expressions (preg_match()). - Not considering directory separators when matching paths (forgetting to use
FNM_PATHNAME). - Overlooking case sensitivity, leading to unexpected match failures.
- Using patterns without escaping special characters when necessary.
- Expecting
fnmatch()to throw errors on bad patterns β it simply returns false if no match occurs.
Interview Questions
Junior Level
- Q1: What is the purpose of the PHP
fnmatch()function?
A: It matches a filename (string) against a shell-style wildcard pattern. - Q2: Name two wildcard characters supported by
fnmatch().
A:*(matches any string) and?(matches one character). - Q3: What does
fnmatch("*.txt", "file.txt")return?
A: True, because "file.txt" matches the "*.txt" pattern. - Q4: Which PHP versions support
fnmatch()?
A: PHP 4.3.0 and later. - Q5: Is
fnmatch()case-sensitive by default?
A: Yes, it is case-sensitive unlessFNM_CASEFOLDis used.
Mid Level
- Q1: How does
FNM_PATHNAMEflag affect matching?
A: It forces slashes (/) to be matched literally, so wildcards won't match across directory separators. - Q2: Can
fnmatch()be used for pattern matching outside filesystems?
A: Yes, it can match any string against shell-style wildcard patterns. - Q3: How does
fnmatch()differ from regular expressions?
A: It uses shell-style patterns, simpler and less powerful, while regex is more complex and expressive. - Q4: What does the flag
FNM_NOESCAPEdo?
A: It disables treating backslash (\) as an escape character. - Q5: Is the
FNM_CASEFOLDflag supported on all platforms?
A: No, support depends on the underlying OS.
Senior Level
- Q1: How would you reliably implement case-insensitive filename matching cross-platform using
fnmatch()givenFNM_CASEFOLDis not always available?
A: Convert both pattern and filename to lower case before matching to simulate case-insensitivity. - Q2: How does
fnmatch()behave when matching filenames with leading dots, and which flag controls this?
A: Leading dots are only matched explicitly ifFNM_PERIODis set; otherwise, wildcards don't match leading periods. - Q3: What are potential portability issues when using
fnmatch()in different hosting environments?
A: Differences in support for flags likeFNM_CASEFOLD, as well as filesystem case-sensitivity, can cause inconsistent results. - Q4: Describe an efficient way to filter files in a directory using
fnmatch()without scanning unnecessary files.
A: Usescandir()orDirectoryIteratorto list files, then applyfnmatch()on relevant entries. - Q5: How can escaping be handled in patterns passed to
fnmatch()when the pattern contains literal wildcard characters?
A: Escape wildcard characters with a backslash, and ensureFNM_NOESCAPEis not set so escapes are processed properly.
Frequently Asked Questions (FAQ)
- Q: Does
fnmatch()support recursive pattern matching? - A: No,
fnmatch()matches patterns within a single string and does not inherently support recursion through directories. - Q: Can
fnmatch()match against absolute file paths? - A: Yes, but when matching paths with slashes, use
FNM_PATHNAMEto ensure accurate matching of directory separators. - Q: How to match filenames beginning with a dot?
- Use the
FNM_PERIODflag; otherwise, wildcards wonβt match leading periods by default. - Q: Is
fnmatch()faster than using regular expressions? - Generally, yes, because shell patterns are simpler and less computationally intensive than regexes.
- Q: What should I do if
fnmatch()is not available on my platform? - Consider using alternative pattern matching techniques like regular expressions or third-party libraries.
Conclusion
PHP's fnmatch() function provides a straightforward and efficient way to match strings against shell-style wildcard patterns, making file filtering and validation tasks easier. Understanding how wildcards and flags influence matching behavior enables developers to implement robust filesystem operations and string validations with minimal overhead.
Always consider the context of your application and environment to handle platform-specific nuances and ensure consistent matching results. Mastering fnmatch() enhances your PHP filesystem toolbox and prepares you for real-world filename pattern matching challenges.