wp_unique_filename

The timeline below displays how wordpress function wp_unique_filename has changed across different WordPress versions. If a version is not listed, refer to the next available version below.

WordPress Version: 6.4

/**
 * Gets a filename that is sanitized and unique for the given directory.
 *
 * If the filename is not unique, then a number will be added to the filename
 * before the extension, and will continue adding numbers until the filename
 * is unique.
 *
 * The callback function allows the caller to use their own method to create
 * unique file names. If defined, the callback should take three arguments:
 * - directory, base filename, and extension - and return a unique filename.
 *
 * @since 2.5.0
 *
 * @param string   $dir                      Directory.
 * @param string   $filename                 File name.
 * @param callable $unique_filename_callback Callback. Default null.
 * @return string New filename, if given wasn't unique.
 */
function wp_unique_filename($dir, $filename, $unique_filename_callback = null)
{
    // Sanitize the file name before we begin processing.
    $filename = sanitize_file_name($filename);
    $ext2 = null;
    // Initialize vars used in the wp_unique_filename filter.
    $number = '';
    $alt_filenames = array();
    // Separate the filename into a name and extension.
    $ext = pathinfo($filename, PATHINFO_EXTENSION);
    $name = pathinfo($filename, PATHINFO_BASENAME);
    if ($ext) {
        $ext = '.' . $ext;
    }
    // Edge case: if file is named '.ext', treat as an empty name.
    if ($name === $ext) {
        $name = '';
    }
    /*
     * Increment the file number until we have a unique file to save in $dir.
     * Use callback if supplied.
     */
    if ($unique_filename_callback && is_callable($unique_filename_callback)) {
        $filename = call_user_func($unique_filename_callback, $dir, $name, $ext);
    } else {
        $fname = pathinfo($filename, PATHINFO_FILENAME);
        // Always append a number to file names that can potentially match image sub-size file names.
        if ($fname && preg_match('/-(?:\d+x\d+|scaled|rotated)$/', $fname)) {
            $number = 1;
            // At this point the file name may not be unique. This is tested below and the $number is incremented.
            $filename = str_replace("{$fname}{$ext}", "{$fname}-{$number}{$ext}", $filename);
        }
        /*
         * Get the mime type. Uploaded files were already checked with wp_check_filetype_and_ext()
         * in _wp_handle_upload(). Using wp_check_filetype() would be sufficient here.
         */
        $file_type = wp_check_filetype($filename);
        $mime_type = $file_type['type'];
        $is_image = !empty($mime_type) && str_starts_with($mime_type, 'image/');
        $upload_dir = wp_get_upload_dir();
        $lc_filename = null;
        $lc_ext = strtolower($ext);
        $_dir = trailingslashit($dir);
        /*
         * If the extension is uppercase add an alternate file name with lowercase extension.
         * Both need to be tested for uniqueness as the extension will be changed to lowercase
         * for better compatibility with different filesystems. Fixes an inconsistency in WP < 2.9
         * where uppercase extensions were allowed but image sub-sizes were created with
         * lowercase extensions.
         */
        if ($ext && $lc_ext !== $ext) {
            $lc_filename = preg_replace('|' . preg_quote($ext) . '$|', $lc_ext, $filename);
        }
        /*
         * Increment the number added to the file name if there are any files in $dir
         * whose names match one of the possible name variations.
         */
        while (file_exists($_dir . $filename) || $lc_filename && file_exists($_dir . $lc_filename)) {
            $new_number = (int) $number + 1;
            if ($lc_filename) {
                $lc_filename = str_replace(array("-{$number}{$lc_ext}", "{$number}{$lc_ext}"), "-{$new_number}{$lc_ext}", $lc_filename);
            }
            if ('' === "{$number}{$ext}") {
                $filename = "{$filename}-{$new_number}";
            } else {
                $filename = str_replace(array("-{$number}{$ext}", "{$number}{$ext}"), "-{$new_number}{$ext}", $filename);
            }
            $number = $new_number;
        }
        // Change the extension to lowercase if needed.
        if ($lc_filename) {
            $filename = $lc_filename;
        }
        /*
         * Prevent collisions with existing file names that contain dimension-like strings
         * (whether they are subsizes or originals uploaded prior to #42437).
         */
        $files = array();
        $count = 10000;
        // The (resized) image files would have name and extension, and will be in the uploads dir.
        if ($name && $ext && @is_dir($dir) && str_contains($dir, $upload_dir['basedir'])) {
            /**
             * Filters the file list used for calculating a unique filename for a newly added file.
             *
             * Returning an array from the filter will effectively short-circuit retrieval
             * from the filesystem and return the passed value instead.
             *
             * @since 5.5.0
             *
             * @param array|null $files    The list of files to use for filename comparisons.
             *                             Default null (to retrieve the list from the filesystem).
             * @param string     $dir      The directory for the new file.
             * @param string     $filename The proposed filename for the new file.
             */
            $files = apply_filters('pre_wp_unique_filename_file_list', null, $dir, $filename);
            if (null === $files) {
                // List of all files and directories contained in $dir.
                $files = @scandir($dir);
            }
            if (!empty($files)) {
                // Remove "dot" dirs.
                $files = array_diff($files, array('.', '..'));
            }
            if (!empty($files)) {
                $count = count($files);
                /*
                 * Ensure this never goes into infinite loop as it uses pathinfo() and regex in the check,
                 * but string replacement for the changes.
                 */
                $i = 0;
                while ($i <= $count && _wp_check_existing_file_names($filename, $files)) {
                    $new_number = (int) $number + 1;
                    // If $ext is uppercase it was replaced with the lowercase version after the previous loop.
                    $filename = str_replace(array("-{$number}{$lc_ext}", "{$number}{$lc_ext}"), "-{$new_number}{$lc_ext}", $filename);
                    $number = $new_number;
                    ++$i;
                }
            }
        }
        /*
         * Check if an image will be converted after uploading or some existing image sub-size file names may conflict
         * when regenerated. If yes, ensure the new file name will be unique and will produce unique sub-sizes.
         */
        if ($is_image) {
            /** This filter is documented in wp-includes/class-wp-image-editor.php */
            $output_formats = apply_filters('image_editor_output_format', array(), $_dir . $filename, $mime_type);
            $alt_types = array();
            if (!empty($output_formats[$mime_type])) {
                // The image will be converted to this format/mime type.
                $alt_mime_type = $output_formats[$mime_type];
                // Other types of images whose names may conflict if their sub-sizes are regenerated.
                $alt_types = array_keys(array_intersect($output_formats, array($mime_type, $alt_mime_type)));
                $alt_types[] = $alt_mime_type;
            } elseif (!empty($output_formats)) {
                $alt_types = array_keys(array_intersect($output_formats, array($mime_type)));
            }
            // Remove duplicates and the original mime type. It will be added later if needed.
            $alt_types = array_unique(array_diff($alt_types, array($mime_type)));
            foreach ($alt_types as $alt_type) {
                $alt_ext = wp_get_default_extension_for_mime_type($alt_type);
                if (!$alt_ext) {
                    continue;
                }
                $alt_ext = ".{$alt_ext}";
                $alt_filename = preg_replace('|' . preg_quote($lc_ext) . '$|', $alt_ext, $filename);
                $alt_filenames[$alt_ext] = $alt_filename;
            }
            if (!empty($alt_filenames)) {
                /*
                 * Add the original filename. It needs to be checked again
                 * together with the alternate filenames when $number is incremented.
                 */
                $alt_filenames[$lc_ext] = $filename;
                // Ensure no infinite loop.
                $i = 0;
                while ($i <= $count && _wp_check_alternate_file_names($alt_filenames, $_dir, $files)) {
                    $new_number = (int) $number + 1;
                    foreach ($alt_filenames as $alt_ext => $alt_filename) {
                        $alt_filenames[$alt_ext] = str_replace(array("-{$number}{$alt_ext}", "{$number}{$alt_ext}"), "-{$new_number}{$alt_ext}", $alt_filename);
                    }
                    /*
                     * Also update the $number in (the output) $filename.
                     * If the extension was uppercase it was already replaced with the lowercase version.
                     */
                    $filename = str_replace(array("-{$number}{$lc_ext}", "{$number}{$lc_ext}"), "-{$new_number}{$lc_ext}", $filename);
                    $number = $new_number;
                    ++$i;
                }
            }
        }
    }
    /**
     * Filters the result when generating a unique file name.
     *
     * @since 4.5.0
     * @since 5.8.1 The `$alt_filenames` and `$number` parameters were added.
     *
     * @param string        $filename                 Unique file name.
     * @param string        $ext                      File extension. Example: ".png".
     * @param string        $dir                      Directory path.
     * @param callable|null $unique_filename_callback Callback function that generates the unique file name.
     * @param string[]      $alt_filenames            Array of alternate file names that were checked for collisions.
     * @param int|string    $number                   The highest number that was used to make the file name unique
     *                                                or an empty string if unused.
     */
    return apply_filters('wp_unique_filename', $filename, $ext, $dir, $unique_filename_callback, $alt_filenames, $number);
}

WordPress Version: 6.3

/**
 * Gets a filename that is sanitized and unique for the given directory.
 *
 * If the filename is not unique, then a number will be added to the filename
 * before the extension, and will continue adding numbers until the filename
 * is unique.
 *
 * The callback function allows the caller to use their own method to create
 * unique file names. If defined, the callback should take three arguments:
 * - directory, base filename, and extension - and return a unique filename.
 *
 * @since 2.5.0
 *
 * @param string   $dir                      Directory.
 * @param string   $filename                 File name.
 * @param callable $unique_filename_callback Callback. Default null.
 * @return string New filename, if given wasn't unique.
 */
function wp_unique_filename($dir, $filename, $unique_filename_callback = null)
{
    // Sanitize the file name before we begin processing.
    $filename = sanitize_file_name($filename);
    $ext2 = null;
    // Initialize vars used in the wp_unique_filename filter.
    $number = '';
    $alt_filenames = array();
    // Separate the filename into a name and extension.
    $ext = pathinfo($filename, PATHINFO_EXTENSION);
    $name = pathinfo($filename, PATHINFO_BASENAME);
    if ($ext) {
        $ext = '.' . $ext;
    }
    // Edge case: if file is named '.ext', treat as an empty name.
    if ($name === $ext) {
        $name = '';
    }
    /*
     * Increment the file number until we have a unique file to save in $dir.
     * Use callback if supplied.
     */
    if ($unique_filename_callback && is_callable($unique_filename_callback)) {
        $filename = call_user_func($unique_filename_callback, $dir, $name, $ext);
    } else {
        $fname = pathinfo($filename, PATHINFO_FILENAME);
        // Always append a number to file names that can potentially match image sub-size file names.
        if ($fname && preg_match('/-(?:\d+x\d+|scaled|rotated)$/', $fname)) {
            $number = 1;
            // At this point the file name may not be unique. This is tested below and the $number is incremented.
            $filename = str_replace("{$fname}{$ext}", "{$fname}-{$number}{$ext}", $filename);
        }
        /*
         * Get the mime type. Uploaded files were already checked with wp_check_filetype_and_ext()
         * in _wp_handle_upload(). Using wp_check_filetype() would be sufficient here.
         */
        $file_type = wp_check_filetype($filename);
        $mime_type = $file_type['type'];
        $is_image = !empty($mime_type) && str_starts_with($mime_type, 'image/');
        $upload_dir = wp_get_upload_dir();
        $lc_filename = null;
        $lc_ext = strtolower($ext);
        $_dir = trailingslashit($dir);
        /*
         * If the extension is uppercase add an alternate file name with lowercase extension.
         * Both need to be tested for uniqueness as the extension will be changed to lowercase
         * for better compatibility with different filesystems. Fixes an inconsistency in WP < 2.9
         * where uppercase extensions were allowed but image sub-sizes were created with
         * lowercase extensions.
         */
        if ($ext && $lc_ext !== $ext) {
            $lc_filename = preg_replace('|' . preg_quote($ext) . '$|', $lc_ext, $filename);
        }
        /*
         * Increment the number added to the file name if there are any files in $dir
         * whose names match one of the possible name variations.
         */
        while (file_exists($_dir . $filename) || $lc_filename && file_exists($_dir . $lc_filename)) {
            $new_number = (int) $number + 1;
            if ($lc_filename) {
                $lc_filename = str_replace(array("-{$number}{$lc_ext}", "{$number}{$lc_ext}"), "-{$new_number}{$lc_ext}", $lc_filename);
            }
            if ('' === "{$number}{$ext}") {
                $filename = "{$filename}-{$new_number}";
            } else {
                $filename = str_replace(array("-{$number}{$ext}", "{$number}{$ext}"), "-{$new_number}{$ext}", $filename);
            }
            $number = $new_number;
        }
        // Change the extension to lowercase if needed.
        if ($lc_filename) {
            $filename = $lc_filename;
        }
        /*
         * Prevent collisions with existing file names that contain dimension-like strings
         * (whether they are subsizes or originals uploaded prior to #42437).
         */
        $files = array();
        $count = 10000;
        // The (resized) image files would have name and extension, and will be in the uploads dir.
        if ($name && $ext && @is_dir($dir) && str_contains($dir, $upload_dir['basedir'])) {
            /**
             * Filters the file list used for calculating a unique filename for a newly added file.
             *
             * Returning an array from the filter will effectively short-circuit retrieval
             * from the filesystem and return the passed value instead.
             *
             * @since 5.5.0
             *
             * @param array|null $files    The list of files to use for filename comparisons.
             *                             Default null (to retrieve the list from the filesystem).
             * @param string     $dir      The directory for the new file.
             * @param string     $filename The proposed filename for the new file.
             */
            $files = apply_filters('pre_wp_unique_filename_file_list', null, $dir, $filename);
            if (null === $files) {
                // List of all files and directories contained in $dir.
                $files = @scandir($dir);
            }
            if (!empty($files)) {
                // Remove "dot" dirs.
                $files = array_diff($files, array('.', '..'));
            }
            if (!empty($files)) {
                $count = count($files);
                /*
                 * Ensure this never goes into infinite loop as it uses pathinfo() and regex in the check,
                 * but string replacement for the changes.
                 */
                $i = 0;
                while ($i <= $count && _wp_check_existing_file_names($filename, $files)) {
                    $new_number = (int) $number + 1;
                    // If $ext is uppercase it was replaced with the lowercase version after the previous loop.
                    $filename = str_replace(array("-{$number}{$lc_ext}", "{$number}{$lc_ext}"), "-{$new_number}{$lc_ext}", $filename);
                    $number = $new_number;
                    $i++;
                }
            }
        }
        /*
         * Check if an image will be converted after uploading or some existing image sub-size file names may conflict
         * when regenerated. If yes, ensure the new file name will be unique and will produce unique sub-sizes.
         */
        if ($is_image) {
            /** This filter is documented in wp-includes/class-wp-image-editor.php */
            $output_formats = apply_filters('image_editor_output_format', array(), $_dir . $filename, $mime_type);
            $alt_types = array();
            if (!empty($output_formats[$mime_type])) {
                // The image will be converted to this format/mime type.
                $alt_mime_type = $output_formats[$mime_type];
                // Other types of images whose names may conflict if their sub-sizes are regenerated.
                $alt_types = array_keys(array_intersect($output_formats, array($mime_type, $alt_mime_type)));
                $alt_types[] = $alt_mime_type;
            } elseif (!empty($output_formats)) {
                $alt_types = array_keys(array_intersect($output_formats, array($mime_type)));
            }
            // Remove duplicates and the original mime type. It will be added later if needed.
            $alt_types = array_unique(array_diff($alt_types, array($mime_type)));
            foreach ($alt_types as $alt_type) {
                $alt_ext = wp_get_default_extension_for_mime_type($alt_type);
                if (!$alt_ext) {
                    continue;
                }
                $alt_ext = ".{$alt_ext}";
                $alt_filename = preg_replace('|' . preg_quote($lc_ext) . '$|', $alt_ext, $filename);
                $alt_filenames[$alt_ext] = $alt_filename;
            }
            if (!empty($alt_filenames)) {
                /*
                 * Add the original filename. It needs to be checked again
                 * together with the alternate filenames when $number is incremented.
                 */
                $alt_filenames[$lc_ext] = $filename;
                // Ensure no infinite loop.
                $i = 0;
                while ($i <= $count && _wp_check_alternate_file_names($alt_filenames, $_dir, $files)) {
                    $new_number = (int) $number + 1;
                    foreach ($alt_filenames as $alt_ext => $alt_filename) {
                        $alt_filenames[$alt_ext] = str_replace(array("-{$number}{$alt_ext}", "{$number}{$alt_ext}"), "-{$new_number}{$alt_ext}", $alt_filename);
                    }
                    /*
                     * Also update the $number in (the output) $filename.
                     * If the extension was uppercase it was already replaced with the lowercase version.
                     */
                    $filename = str_replace(array("-{$number}{$lc_ext}", "{$number}{$lc_ext}"), "-{$new_number}{$lc_ext}", $filename);
                    $number = $new_number;
                    $i++;
                }
            }
        }
    }
    /**
     * Filters the result when generating a unique file name.
     *
     * @since 4.5.0
     * @since 5.8.1 The `$alt_filenames` and `$number` parameters were added.
     *
     * @param string        $filename                 Unique file name.
     * @param string        $ext                      File extension. Example: ".png".
     * @param string        $dir                      Directory path.
     * @param callable|null $unique_filename_callback Callback function that generates the unique file name.
     * @param string[]      $alt_filenames            Array of alternate file names that were checked for collisions.
     * @param int|string    $number                   The highest number that was used to make the file name unique
     *                                                or an empty string if unused.
     */
    return apply_filters('wp_unique_filename', $filename, $ext, $dir, $unique_filename_callback, $alt_filenames, $number);
}

WordPress Version: 6.1

/**
 * Gets a filename that is sanitized and unique for the given directory.
 *
 * If the filename is not unique, then a number will be added to the filename
 * before the extension, and will continue adding numbers until the filename
 * is unique.
 *
 * The callback function allows the caller to use their own method to create
 * unique file names. If defined, the callback should take three arguments:
 * - directory, base filename, and extension - and return a unique filename.
 *
 * @since 2.5.0
 *
 * @param string   $dir                      Directory.
 * @param string   $filename                 File name.
 * @param callable $unique_filename_callback Callback. Default null.
 * @return string New filename, if given wasn't unique.
 */
function wp_unique_filename($dir, $filename, $unique_filename_callback = null)
{
    // Sanitize the file name before we begin processing.
    $filename = sanitize_file_name($filename);
    $ext2 = null;
    // Initialize vars used in the wp_unique_filename filter.
    $number = '';
    $alt_filenames = array();
    // Separate the filename into a name and extension.
    $ext = pathinfo($filename, PATHINFO_EXTENSION);
    $name = pathinfo($filename, PATHINFO_BASENAME);
    if ($ext) {
        $ext = '.' . $ext;
    }
    // Edge case: if file is named '.ext', treat as an empty name.
    if ($name === $ext) {
        $name = '';
    }
    /*
     * Increment the file number until we have a unique file to save in $dir.
     * Use callback if supplied.
     */
    if ($unique_filename_callback && is_callable($unique_filename_callback)) {
        $filename = call_user_func($unique_filename_callback, $dir, $name, $ext);
    } else {
        $fname = pathinfo($filename, PATHINFO_FILENAME);
        // Always append a number to file names that can potentially match image sub-size file names.
        if ($fname && preg_match('/-(?:\d+x\d+|scaled|rotated)$/', $fname)) {
            $number = 1;
            // At this point the file name may not be unique. This is tested below and the $number is incremented.
            $filename = str_replace("{$fname}{$ext}", "{$fname}-{$number}{$ext}", $filename);
        }
        /*
         * Get the mime type. Uploaded files were already checked with wp_check_filetype_and_ext()
         * in _wp_handle_upload(). Using wp_check_filetype() would be sufficient here.
         */
        $file_type = wp_check_filetype($filename);
        $mime_type = $file_type['type'];
        $is_image = !empty($mime_type) && 0 === strpos($mime_type, 'image/');
        $upload_dir = wp_get_upload_dir();
        $lc_filename = null;
        $lc_ext = strtolower($ext);
        $_dir = trailingslashit($dir);
        /*
         * If the extension is uppercase add an alternate file name with lowercase extension.
         * Both need to be tested for uniqueness as the extension will be changed to lowercase
         * for better compatibility with different filesystems. Fixes an inconsistency in WP < 2.9
         * where uppercase extensions were allowed but image sub-sizes were created with
         * lowercase extensions.
         */
        if ($ext && $lc_ext !== $ext) {
            $lc_filename = preg_replace('|' . preg_quote($ext) . '$|', $lc_ext, $filename);
        }
        /*
         * Increment the number added to the file name if there are any files in $dir
         * whose names match one of the possible name variations.
         */
        while (file_exists($_dir . $filename) || $lc_filename && file_exists($_dir . $lc_filename)) {
            $new_number = (int) $number + 1;
            if ($lc_filename) {
                $lc_filename = str_replace(array("-{$number}{$lc_ext}", "{$number}{$lc_ext}"), "-{$new_number}{$lc_ext}", $lc_filename);
            }
            if ('' === "{$number}{$ext}") {
                $filename = "{$filename}-{$new_number}";
            } else {
                $filename = str_replace(array("-{$number}{$ext}", "{$number}{$ext}"), "-{$new_number}{$ext}", $filename);
            }
            $number = $new_number;
        }
        // Change the extension to lowercase if needed.
        if ($lc_filename) {
            $filename = $lc_filename;
        }
        /*
         * Prevent collisions with existing file names that contain dimension-like strings
         * (whether they are subsizes or originals uploaded prior to #42437).
         */
        $files = array();
        $count = 10000;
        // The (resized) image files would have name and extension, and will be in the uploads dir.
        if ($name && $ext && @is_dir($dir) && false !== strpos($dir, $upload_dir['basedir'])) {
            /**
             * Filters the file list used for calculating a unique filename for a newly added file.
             *
             * Returning an array from the filter will effectively short-circuit retrieval
             * from the filesystem and return the passed value instead.
             *
             * @since 5.5.0
             *
             * @param array|null $files    The list of files to use for filename comparisons.
             *                             Default null (to retrieve the list from the filesystem).
             * @param string     $dir      The directory for the new file.
             * @param string     $filename The proposed filename for the new file.
             */
            $files = apply_filters('pre_wp_unique_filename_file_list', null, $dir, $filename);
            if (null === $files) {
                // List of all files and directories contained in $dir.
                $files = @scandir($dir);
            }
            if (!empty($files)) {
                // Remove "dot" dirs.
                $files = array_diff($files, array('.', '..'));
            }
            if (!empty($files)) {
                $count = count($files);
                /*
                 * Ensure this never goes into infinite loop as it uses pathinfo() and regex in the check,
                 * but string replacement for the changes.
                 */
                $i = 0;
                while ($i <= $count && _wp_check_existing_file_names($filename, $files)) {
                    $new_number = (int) $number + 1;
                    // If $ext is uppercase it was replaced with the lowercase version after the previous loop.
                    $filename = str_replace(array("-{$number}{$lc_ext}", "{$number}{$lc_ext}"), "-{$new_number}{$lc_ext}", $filename);
                    $number = $new_number;
                    $i++;
                }
            }
        }
        /*
         * Check if an image will be converted after uploading or some existing image sub-size file names may conflict
         * when regenerated. If yes, ensure the new file name will be unique and will produce unique sub-sizes.
         */
        if ($is_image) {
            /** This filter is documented in wp-includes/class-wp-image-editor.php */
            $output_formats = apply_filters('image_editor_output_format', array(), $_dir . $filename, $mime_type);
            $alt_types = array();
            if (!empty($output_formats[$mime_type])) {
                // The image will be converted to this format/mime type.
                $alt_mime_type = $output_formats[$mime_type];
                // Other types of images whose names may conflict if their sub-sizes are regenerated.
                $alt_types = array_keys(array_intersect($output_formats, array($mime_type, $alt_mime_type)));
                $alt_types[] = $alt_mime_type;
            } elseif (!empty($output_formats)) {
                $alt_types = array_keys(array_intersect($output_formats, array($mime_type)));
            }
            // Remove duplicates and the original mime type. It will be added later if needed.
            $alt_types = array_unique(array_diff($alt_types, array($mime_type)));
            foreach ($alt_types as $alt_type) {
                $alt_ext = wp_get_default_extension_for_mime_type($alt_type);
                if (!$alt_ext) {
                    continue;
                }
                $alt_ext = ".{$alt_ext}";
                $alt_filename = preg_replace('|' . preg_quote($lc_ext) . '$|', $alt_ext, $filename);
                $alt_filenames[$alt_ext] = $alt_filename;
            }
            if (!empty($alt_filenames)) {
                /*
                 * Add the original filename. It needs to be checked again
                 * together with the alternate filenames when $number is incremented.
                 */
                $alt_filenames[$lc_ext] = $filename;
                // Ensure no infinite loop.
                $i = 0;
                while ($i <= $count && _wp_check_alternate_file_names($alt_filenames, $_dir, $files)) {
                    $new_number = (int) $number + 1;
                    foreach ($alt_filenames as $alt_ext => $alt_filename) {
                        $alt_filenames[$alt_ext] = str_replace(array("-{$number}{$alt_ext}", "{$number}{$alt_ext}"), "-{$new_number}{$alt_ext}", $alt_filename);
                    }
                    /*
                     * Also update the $number in (the output) $filename.
                     * If the extension was uppercase it was already replaced with the lowercase version.
                     */
                    $filename = str_replace(array("-{$number}{$lc_ext}", "{$number}{$lc_ext}"), "-{$new_number}{$lc_ext}", $filename);
                    $number = $new_number;
                    $i++;
                }
            }
        }
    }
    /**
     * Filters the result when generating a unique file name.
     *
     * @since 4.5.0
     * @since 5.8.1 The `$alt_filenames` and `$number` parameters were added.
     *
     * @param string        $filename                 Unique file name.
     * @param string        $ext                      File extension. Example: ".png".
     * @param string        $dir                      Directory path.
     * @param callable|null $unique_filename_callback Callback function that generates the unique file name.
     * @param string[]      $alt_filenames            Array of alternate file names that were checked for collisions.
     * @param int|string    $number                   The highest number that was used to make the file name unique
     *                                                or an empty string if unused.
     */
    return apply_filters('wp_unique_filename', $filename, $ext, $dir, $unique_filename_callback, $alt_filenames, $number);
}

WordPress Version: 5.9

/**
 * Get a filename that is sanitized and unique for the given directory.
 *
 * If the filename is not unique, then a number will be added to the filename
 * before the extension, and will continue adding numbers until the filename
 * is unique.
 *
 * The callback function allows the caller to use their own method to create
 * unique file names. If defined, the callback should take three arguments:
 * - directory, base filename, and extension - and return a unique filename.
 *
 * @since 2.5.0
 *
 * @param string   $dir                      Directory.
 * @param string   $filename                 File name.
 * @param callable $unique_filename_callback Callback. Default null.
 * @return string New filename, if given wasn't unique.
 */
function wp_unique_filename($dir, $filename, $unique_filename_callback = null)
{
    // Sanitize the file name before we begin processing.
    $filename = sanitize_file_name($filename);
    $ext2 = null;
    // Initialize vars used in the wp_unique_filename filter.
    $number = '';
    $alt_filenames = array();
    // Separate the filename into a name and extension.
    $ext = pathinfo($filename, PATHINFO_EXTENSION);
    $name = pathinfo($filename, PATHINFO_BASENAME);
    if ($ext) {
        $ext = '.' . $ext;
    }
    // Edge case: if file is named '.ext', treat as an empty name.
    if ($name === $ext) {
        $name = '';
    }
    /*
     * Increment the file number until we have a unique file to save in $dir.
     * Use callback if supplied.
     */
    if ($unique_filename_callback && is_callable($unique_filename_callback)) {
        $filename = call_user_func($unique_filename_callback, $dir, $name, $ext);
    } else {
        $fname = pathinfo($filename, PATHINFO_FILENAME);
        // Always append a number to file names that can potentially match image sub-size file names.
        if ($fname && preg_match('/-(?:\d+x\d+|scaled|rotated)$/', $fname)) {
            $number = 1;
            // At this point the file name may not be unique. This is tested below and the $number is incremented.
            $filename = str_replace("{$fname}{$ext}", "{$fname}-{$number}{$ext}", $filename);
        }
        /*
         * Get the mime type. Uploaded files were already checked with wp_check_filetype_and_ext()
         * in _wp_handle_upload(). Using wp_check_filetype() would be sufficient here.
         */
        $file_type = wp_check_filetype($filename);
        $mime_type = $file_type['type'];
        $is_image = !empty($mime_type) && 0 === strpos($mime_type, 'image/');
        $upload_dir = wp_get_upload_dir();
        $lc_filename = null;
        $lc_ext = strtolower($ext);
        $_dir = trailingslashit($dir);
        /*
         * If the extension is uppercase add an alternate file name with lowercase extension.
         * Both need to be tested for uniqueness as the extension will be changed to lowercase
         * for better compatibility with different filesystems. Fixes an inconsistency in WP < 2.9
         * where uppercase extensions were allowed but image sub-sizes were created with
         * lowercase extensions.
         */
        if ($ext && $lc_ext !== $ext) {
            $lc_filename = preg_replace('|' . preg_quote($ext) . '$|', $lc_ext, $filename);
        }
        /*
         * Increment the number added to the file name if there are any files in $dir
         * whose names match one of the possible name variations.
         */
        while (file_exists($_dir . $filename) || $lc_filename && file_exists($_dir . $lc_filename)) {
            $new_number = (int) $number + 1;
            if ($lc_filename) {
                $lc_filename = str_replace(array("-{$number}{$lc_ext}", "{$number}{$lc_ext}"), "-{$new_number}{$lc_ext}", $lc_filename);
            }
            if ('' === "{$number}{$ext}") {
                $filename = "{$filename}-{$new_number}";
            } else {
                $filename = str_replace(array("-{$number}{$ext}", "{$number}{$ext}"), "-{$new_number}{$ext}", $filename);
            }
            $number = $new_number;
        }
        // Change the extension to lowercase if needed.
        if ($lc_filename) {
            $filename = $lc_filename;
        }
        /*
         * Prevent collisions with existing file names that contain dimension-like strings
         * (whether they are subsizes or originals uploaded prior to #42437).
         */
        $files = array();
        $count = 10000;
        // The (resized) image files would have name and extension, and will be in the uploads dir.
        if ($name && $ext && @is_dir($dir) && false !== strpos($dir, $upload_dir['basedir'])) {
            /**
             * Filters the file list used for calculating a unique filename for a newly added file.
             *
             * Returning an array from the filter will effectively short-circuit retrieval
             * from the filesystem and return the passed value instead.
             *
             * @since 5.5.0
             *
             * @param array|null $files    The list of files to use for filename comparisons.
             *                             Default null (to retrieve the list from the filesystem).
             * @param string     $dir      The directory for the new file.
             * @param string     $filename The proposed filename for the new file.
             */
            $files = apply_filters('pre_wp_unique_filename_file_list', null, $dir, $filename);
            if (null === $files) {
                // List of all files and directories contained in $dir.
                $files = @scandir($dir);
            }
            if (!empty($files)) {
                // Remove "dot" dirs.
                $files = array_diff($files, array('.', '..'));
            }
            if (!empty($files)) {
                $count = count($files);
                /*
                 * Ensure this never goes into infinite loop as it uses pathinfo() and regex in the check,
                 * but string replacement for the changes.
                 */
                $i = 0;
                while ($i <= $count && _wp_check_existing_file_names($filename, $files)) {
                    $new_number = (int) $number + 1;
                    // If $ext is uppercase it was replaced with the lowercase version after the previous loop.
                    $filename = str_replace(array("-{$number}{$lc_ext}", "{$number}{$lc_ext}"), "-{$new_number}{$lc_ext}", $filename);
                    $number = $new_number;
                    $i++;
                }
            }
        }
        /*
         * Check if an image will be converted after uploading or some existing image sub-size file names may conflict
         * when regenerated. If yes, ensure the new file name will be unique and will produce unique sub-sizes.
         */
        if ($is_image) {
            /** This filter is documented in wp-includes/class-wp-image-editor.php */
            $output_formats = apply_filters('image_editor_output_format', array(), $_dir . $filename, $mime_type);
            $alt_types = array();
            if (!empty($output_formats[$mime_type])) {
                // The image will be converted to this format/mime type.
                $alt_mime_type = $output_formats[$mime_type];
                // Other types of images whose names may conflict if their sub-sizes are regenerated.
                $alt_types = array_keys(array_intersect($output_formats, array($mime_type, $alt_mime_type)));
                $alt_types[] = $alt_mime_type;
            } elseif (!empty($output_formats)) {
                $alt_types = array_keys(array_intersect($output_formats, array($mime_type)));
            }
            // Remove duplicates and the original mime type. It will be added later if needed.
            $alt_types = array_unique(array_diff($alt_types, array($mime_type)));
            foreach ($alt_types as $alt_type) {
                $alt_ext = wp_get_default_extension_for_mime_type($alt_type);
                if (!$alt_ext) {
                    continue;
                }
                $alt_ext = ".{$alt_ext}";
                $alt_filename = preg_replace('|' . preg_quote($lc_ext) . '$|', $alt_ext, $filename);
                $alt_filenames[$alt_ext] = $alt_filename;
            }
            if (!empty($alt_filenames)) {
                /*
                 * Add the original filename. It needs to be checked again
                 * together with the alternate filenames when $number is incremented.
                 */
                $alt_filenames[$lc_ext] = $filename;
                // Ensure no infinite loop.
                $i = 0;
                while ($i <= $count && _wp_check_alternate_file_names($alt_filenames, $_dir, $files)) {
                    $new_number = (int) $number + 1;
                    foreach ($alt_filenames as $alt_ext => $alt_filename) {
                        $alt_filenames[$alt_ext] = str_replace(array("-{$number}{$alt_ext}", "{$number}{$alt_ext}"), "-{$new_number}{$alt_ext}", $alt_filename);
                    }
                    /*
                     * Also update the $number in (the output) $filename.
                     * If the extension was uppercase it was already replaced with the lowercase version.
                     */
                    $filename = str_replace(array("-{$number}{$lc_ext}", "{$number}{$lc_ext}"), "-{$new_number}{$lc_ext}", $filename);
                    $number = $new_number;
                    $i++;
                }
            }
        }
    }
    /**
     * Filters the result when generating a unique file name.
     *
     * @since 4.5.0
     * @since 5.8.1 The `$alt_filenames` and `$number` parameters were added.
     *
     * @param string        $filename                 Unique file name.
     * @param string        $ext                      File extension. Example: ".png".
     * @param string        $dir                      Directory path.
     * @param callable|null $unique_filename_callback Callback function that generates the unique file name.
     * @param string[]      $alt_filenames            Array of alternate file names that were checked for collisions.
     * @param int|string    $number                   The highest number that was used to make the file name unique
     *                                                or an empty string if unused.
     */
    return apply_filters('wp_unique_filename', $filename, $ext, $dir, $unique_filename_callback, $alt_filenames, $number);
}

WordPress Version: 8.1

/**
 * Get a filename that is sanitized and unique for the given directory.
 *
 * If the filename is not unique, then a number will be added to the filename
 * before the extension, and will continue adding numbers until the filename
 * is unique.
 *
 * The callback function allows the caller to use their own method to create
 * unique file names. If defined, the callback should take three arguments:
 * - directory, base filename, and extension - and return a unique filename.
 *
 * @since 2.5.0
 *
 * @param string   $dir                      Directory.
 * @param string   $filename                 File name.
 * @param callable $unique_filename_callback Callback. Default null.
 * @return string New filename, if given wasn't unique.
 */
function wp_unique_filename($dir, $filename, $unique_filename_callback = null)
{
    // Sanitize the file name before we begin processing.
    $filename = sanitize_file_name($filename);
    $ext2 = null;
    // Initialize vars used in the wp_unique_filename filter.
    $number = '';
    $alt_filenames = array();
    // Separate the filename into a name and extension.
    $ext = pathinfo($filename, PATHINFO_EXTENSION);
    $name = pathinfo($filename, PATHINFO_BASENAME);
    if ($ext) {
        $ext = '.' . $ext;
    }
    // Edge case: if file is named '.ext', treat as an empty name.
    if ($name === $ext) {
        $name = '';
    }
    /*
     * Increment the file number until we have a unique file to save in $dir.
     * Use callback if supplied.
     */
    if ($unique_filename_callback && is_callable($unique_filename_callback)) {
        $filename = call_user_func($unique_filename_callback, $dir, $name, $ext);
    } else {
        $fname = pathinfo($filename, PATHINFO_FILENAME);
        // Always append a number to file names that can potentially match image sub-size file names.
        if ($fname && preg_match('/-(?:\d+x\d+|scaled|rotated)$/', $fname)) {
            $number = 1;
            // At this point the file name may not be unique. This is tested below and the $number is incremented.
            $filename = str_replace("{$fname}{$ext}", "{$fname}-{$number}{$ext}", $filename);
        }
        // Get the mime type. Uploaded files were already checked with wp_check_filetype_and_ext()
        // in _wp_handle_upload(). Using wp_check_filetype() would be sufficient here.
        $file_type = wp_check_filetype($filename);
        $mime_type = $file_type['type'];
        $is_image = !empty($mime_type) && 0 === strpos($mime_type, 'image/');
        $upload_dir = wp_get_upload_dir();
        $lc_filename = null;
        $lc_ext = strtolower($ext);
        $_dir = trailingslashit($dir);
        // If the extension is uppercase add an alternate file name with lowercase extension. Both need to be tested
        // for uniqueness as the extension will be changed to lowercase for better compatibility with different filesystems.
        // Fixes an inconsistency in WP < 2.9 where uppercase extensions were allowed but image sub-sizes were created with
        // lowercase extensions.
        if ($ext && $lc_ext !== $ext) {
            $lc_filename = preg_replace('|' . preg_quote($ext) . '$|', $lc_ext, $filename);
        }
        // Increment the number added to the file name if there are any files in $dir whose names match one of the
        // possible name variations.
        while (file_exists($_dir . $filename) || $lc_filename && file_exists($_dir . $lc_filename)) {
            $new_number = (int) $number + 1;
            if ($lc_filename) {
                $lc_filename = str_replace(array("-{$number}{$lc_ext}", "{$number}{$lc_ext}"), "-{$new_number}{$lc_ext}", $lc_filename);
            }
            if ('' === "{$number}{$ext}") {
                $filename = "{$filename}-{$new_number}";
            } else {
                $filename = str_replace(array("-{$number}{$ext}", "{$number}{$ext}"), "-{$new_number}{$ext}", $filename);
            }
            $number = $new_number;
        }
        // Change the extension to lowercase if needed.
        if ($lc_filename) {
            $filename = $lc_filename;
        }
        // Prevent collisions with existing file names that contain dimension-like strings
        // (whether they are subsizes or originals uploaded prior to #42437).
        $files = array();
        $count = 10000;
        // The (resized) image files would have name and extension, and will be in the uploads dir.
        if ($name && $ext && @is_dir($dir) && false !== strpos($dir, $upload_dir['basedir'])) {
            /**
             * Filters the file list used for calculating a unique filename for a newly added file.
             *
             * Returning an array from the filter will effectively short-circuit retrieval
             * from the filesystem and return the passed value instead.
             *
             * @since 5.5.0
             *
             * @param array|null $files    The list of files to use for filename comparisons.
             *                             Default null (to retrieve the list from the filesystem).
             * @param string     $dir      The directory for the new file.
             * @param string     $filename The proposed filename for the new file.
             */
            $files = apply_filters('pre_wp_unique_filename_file_list', null, $dir, $filename);
            if (null === $files) {
                // List of all files and directories contained in $dir.
                $files = @scandir($dir);
            }
            if (!empty($files)) {
                // Remove "dot" dirs.
                $files = array_diff($files, array('.', '..'));
            }
            if (!empty($files)) {
                $count = count($files);
                // Ensure this never goes into infinite loop
                // as it uses pathinfo() and regex in the check, but string replacement for the changes.
                $i = 0;
                while ($i <= $count && _wp_check_existing_file_names($filename, $files)) {
                    $new_number = (int) $number + 1;
                    // If $ext is uppercase it was replaced with the lowercase version after the previous loop.
                    $filename = str_replace(array("-{$number}{$lc_ext}", "{$number}{$lc_ext}"), "-{$new_number}{$lc_ext}", $filename);
                    $number = $new_number;
                    $i++;
                }
            }
        }
        // Check if an image will be converted after uploading or some existing images sub-sizes file names may conflict
        // when regenerated. If yes, ensure the new file name will be unique and will produce unique sub-sizes.
        if ($is_image) {
            $output_formats = apply_filters('image_editor_output_format', array(), $_dir . $filename, $mime_type);
            $alt_types = array();
            if (!empty($output_formats[$mime_type])) {
                // The image will be converted to this format/mime type.
                $alt_mime_type = $output_formats[$mime_type];
                // Other types of images whose names may conflict if their sub-sizes are regenerated.
                $alt_types = array_keys(array_intersect($output_formats, array($mime_type, $alt_mime_type)));
                $alt_types[] = $alt_mime_type;
            } elseif (!empty($output_formats)) {
                $alt_types = array_keys(array_intersect($output_formats, array($mime_type)));
            }
            // Remove duplicates and the original mime type. It will be added later if needed.
            $alt_types = array_unique(array_diff($alt_types, array($mime_type)));
            foreach ($alt_types as $alt_type) {
                $alt_ext = wp_get_default_extension_for_mime_type($alt_type);
                if (!$alt_ext) {
                    continue;
                }
                $alt_ext = ".{$alt_ext}";
                $alt_filename = preg_replace('|' . preg_quote($lc_ext) . '$|', $alt_ext, $filename);
                $alt_filenames[$alt_ext] = $alt_filename;
            }
            if (!empty($alt_filenames)) {
                // Add the original filename. It needs to be checked again together with the alternate filenames
                // when $number is incremented.
                $alt_filenames[$lc_ext] = $filename;
                // Ensure no infinite loop.
                $i = 0;
                while ($i <= $count && _wp_check_alternate_file_names($alt_filenames, $_dir, $files)) {
                    $new_number = (int) $number + 1;
                    foreach ($alt_filenames as $alt_ext => $alt_filename) {
                        $alt_filenames[$alt_ext] = str_replace(array("-{$number}{$alt_ext}", "{$number}{$alt_ext}"), "-{$new_number}{$alt_ext}", $alt_filename);
                    }
                    // Also update the $number in (the output) $filename.
                    // If the extension was uppercase it was already replaced with the lowercase version.
                    $filename = str_replace(array("-{$number}{$lc_ext}", "{$number}{$lc_ext}"), "-{$new_number}{$lc_ext}", $filename);
                    $number = $new_number;
                    $i++;
                }
            }
        }
    }
    /**
     * Filters the result when generating a unique file name.
     *
     * @since 4.5.0
     * @since 5.8.1 The `$alt_filenames` and `$number` parameters were added.
     *
     * @param string        $filename                 Unique file name.
     * @param string        $ext                      File extension, eg. ".png".
     * @param string        $dir                      Directory path.
     * @param callable|null $unique_filename_callback Callback function that generates the unique file name.
     * @param string[]      $alt_filenames            Array of alternate file names that were checked for collisions.
     * @param int|string    $number                   The highest number that was used to make the file name unique
     *                                                or an empty string if unused.
     */
    return apply_filters('wp_unique_filename', $filename, $ext, $dir, $unique_filename_callback, $alt_filenames, $number);
}

WordPress Version: 5.5

/**
 * Get a filename that is sanitized and unique for the given directory.
 *
 * If the filename is not unique, then a number will be added to the filename
 * before the extension, and will continue adding numbers until the filename
 * is unique.
 *
 * The callback function allows the caller to use their own method to create
 * unique file names. If defined, the callback should take three arguments:
 * - directory, base filename, and extension - and return a unique filename.
 *
 * @since 2.5.0
 *
 * @param string   $dir                      Directory.
 * @param string   $filename                 File name.
 * @param callable $unique_filename_callback Callback. Default null.
 * @return string New filename, if given wasn't unique.
 */
function wp_unique_filename($dir, $filename, $unique_filename_callback = null)
{
    // Sanitize the file name before we begin processing.
    $filename = sanitize_file_name($filename);
    $ext2 = null;
    // Separate the filename into a name and extension.
    $ext = pathinfo($filename, PATHINFO_EXTENSION);
    $name = pathinfo($filename, PATHINFO_BASENAME);
    if ($ext) {
        $ext = '.' . $ext;
    }
    // Edge case: if file is named '.ext', treat as an empty name.
    if ($name === $ext) {
        $name = '';
    }
    /*
     * Increment the file number until we have a unique file to save in $dir.
     * Use callback if supplied.
     */
    if ($unique_filename_callback && is_callable($unique_filename_callback)) {
        $filename = call_user_func($unique_filename_callback, $dir, $name, $ext);
    } else {
        $number = '';
        $fname = pathinfo($filename, PATHINFO_FILENAME);
        // Always append a number to file names that can potentially match image sub-size file names.
        if ($fname && preg_match('/-(?:\d+x\d+|scaled|rotated)$/', $fname)) {
            $number = 1;
            // At this point the file name may not be unique. This is tested below and the $number is incremented.
            $filename = str_replace("{$fname}{$ext}", "{$fname}-{$number}{$ext}", $filename);
        }
        // Change '.ext' to lower case.
        if ($ext && strtolower($ext) != $ext) {
            $ext2 = strtolower($ext);
            $filename2 = preg_replace('|' . preg_quote($ext) . '$|', $ext2, $filename);
            // Check for both lower and upper case extension or image sub-sizes may be overwritten.
            while (file_exists($dir . "/{$filename}") || file_exists($dir . "/{$filename2}")) {
                $new_number = (int) $number + 1;
                $filename = str_replace(array("-{$number}{$ext}", "{$number}{$ext}"), "-{$new_number}{$ext}", $filename);
                $filename2 = str_replace(array("-{$number}{$ext2}", "{$number}{$ext2}"), "-{$new_number}{$ext2}", $filename2);
                $number = $new_number;
            }
            $filename = $filename2;
        } else {
            while (file_exists($dir . "/{$filename}")) {
                $new_number = (int) $number + 1;
                if ('' === "{$number}{$ext}") {
                    $filename = "{$filename}-{$new_number}";
                } else {
                    $filename = str_replace(array("-{$number}{$ext}", "{$number}{$ext}"), "-{$new_number}{$ext}", $filename);
                }
                $number = $new_number;
            }
        }
        // Prevent collisions with existing file names that contain dimension-like strings
        // (whether they are subsizes or originals uploaded prior to #42437).
        $upload_dir = wp_get_upload_dir();
        // The (resized) image files would have name and extension, and will be in the uploads dir.
        if ($name && $ext && @is_dir($dir) && false !== strpos($dir, $upload_dir['basedir'])) {
            /**
             * Filters the file list used for calculating a unique filename for a newly added file.
             *
             * Returning an array from the filter will effectively short-circuit retrieval
             * from the filesystem and return the passed value instead.
             *
             * @since 5.5.0
             *
             * @param array|null $files    The list of files to use for filename comparisons.
             *                             Default null (to retrieve the list from the filesystem).
             * @param string     $dir      The directory for the new file.
             * @param string     $filename The proposed filename for the new file.
             */
            $files = apply_filters('pre_wp_unique_filename_file_list', null, $dir, $filename);
            if (null === $files) {
                // List of all files and directories contained in $dir.
                $files = @scandir($dir);
            }
            if (!empty($files)) {
                // Remove "dot" dirs.
                $files = array_diff($files, array('.', '..'));
            }
            if (!empty($files)) {
                // The extension case may have changed above.
                $new_ext = (!empty($ext2)) ? $ext2 : $ext;
                // Ensure this never goes into infinite loop
                // as it uses pathinfo() and regex in the check, but string replacement for the changes.
                $count = count($files);
                $i = 0;
                while ($i <= $count && _wp_check_existing_file_names($filename, $files)) {
                    $new_number = (int) $number + 1;
                    $filename = str_replace(array("-{$number}{$new_ext}", "{$number}{$new_ext}"), "-{$new_number}{$new_ext}", $filename);
                    $number = $new_number;
                    $i++;
                }
            }
        }
    }
    /**
     * Filters the result when generating a unique file name.
     *
     * @since 4.5.0
     *
     * @param string        $filename                 Unique file name.
     * @param string        $ext                      File extension, eg. ".png".
     * @param string        $dir                      Directory path.
     * @param callable|null $unique_filename_callback Callback function that generates the unique file name.
     */
    return apply_filters('wp_unique_filename', $filename, $ext, $dir, $unique_filename_callback);
}

WordPress Version: 5.4

/**
 * Get a filename that is sanitized and unique for the given directory.
 *
 * If the filename is not unique, then a number will be added to the filename
 * before the extension, and will continue adding numbers until the filename is
 * unique.
 *
 * The callback is passed three parameters, the first one is the directory, the
 * second is the filename, and the third is the extension.
 *
 * @since 2.5.0
 *
 * @param string   $dir                      Directory.
 * @param string   $filename                 File name.
 * @param callable $unique_filename_callback Callback. Default null.
 * @return string New filename, if given wasn't unique.
 */
function wp_unique_filename($dir, $filename, $unique_filename_callback = null)
{
    // Sanitize the file name before we begin processing.
    $filename = sanitize_file_name($filename);
    $ext2 = null;
    // Separate the filename into a name and extension.
    $ext = pathinfo($filename, PATHINFO_EXTENSION);
    $name = pathinfo($filename, PATHINFO_BASENAME);
    if ($ext) {
        $ext = '.' . $ext;
    }
    // Edge case: if file is named '.ext', treat as an empty name.
    if ($name === $ext) {
        $name = '';
    }
    /*
     * Increment the file number until we have a unique file to save in $dir.
     * Use callback if supplied.
     */
    if ($unique_filename_callback && is_callable($unique_filename_callback)) {
        $filename = call_user_func($unique_filename_callback, $dir, $name, $ext);
    } else {
        $number = '';
        $fname = pathinfo($filename, PATHINFO_FILENAME);
        // Always append a number to file names that can potentially match image sub-size file names.
        if ($fname && preg_match('/-(?:\d+x\d+|scaled|rotated)$/', $fname)) {
            $number = 1;
            // At this point the file name may not be unique. This is tested below and the $number is incremented.
            $filename = str_replace("{$fname}{$ext}", "{$fname}-{$number}{$ext}", $filename);
        }
        // Change '.ext' to lower case.
        if ($ext && strtolower($ext) != $ext) {
            $ext2 = strtolower($ext);
            $filename2 = preg_replace('|' . preg_quote($ext) . '$|', $ext2, $filename);
            // Check for both lower and upper case extension or image sub-sizes may be overwritten.
            while (file_exists($dir . "/{$filename}") || file_exists($dir . "/{$filename2}")) {
                $new_number = (int) $number + 1;
                $filename = str_replace(array("-{$number}{$ext}", "{$number}{$ext}"), "-{$new_number}{$ext}", $filename);
                $filename2 = str_replace(array("-{$number}{$ext2}", "{$number}{$ext2}"), "-{$new_number}{$ext2}", $filename2);
                $number = $new_number;
            }
            $filename = $filename2;
        } else {
            while (file_exists($dir . "/{$filename}")) {
                $new_number = (int) $number + 1;
                if ('' === "{$number}{$ext}") {
                    $filename = "{$filename}-{$new_number}";
                } else {
                    $filename = str_replace(array("-{$number}{$ext}", "{$number}{$ext}"), "-{$new_number}{$ext}", $filename);
                }
                $number = $new_number;
            }
        }
        // Prevent collisions with existing file names that contain dimension-like strings
        // (whether they are subsizes or originals uploaded prior to #42437).
        $upload_dir = wp_get_upload_dir();
        // The (resized) image files would have name and extension, and will be in the uploads dir.
        if ($name && $ext && @is_dir($dir) && false !== strpos($dir, $upload_dir['basedir'])) {
            // List of all files and directories contained in $dir.
            $files = @scandir($dir);
            if (!empty($files)) {
                // Remove "dot" dirs.
                $files = array_diff($files, array('.', '..'));
            }
            if (!empty($files)) {
                // The extension case may have changed above.
                $new_ext = (!empty($ext2)) ? $ext2 : $ext;
                // Ensure this never goes into infinite loop
                // as it uses pathinfo() and regex in the check, but string replacement for the changes.
                $count = count($files);
                $i = 0;
                while ($i <= $count && _wp_check_existing_file_names($filename, $files)) {
                    $new_number = (int) $number + 1;
                    $filename = str_replace(array("-{$number}{$new_ext}", "{$number}{$new_ext}"), "-{$new_number}{$new_ext}", $filename);
                    $number = $new_number;
                    $i++;
                }
            }
        }
    }
    /**
     * Filters the result when generating a unique file name.
     *
     * @since 4.5.0
     *
     * @param string        $filename                 Unique file name.
     * @param string        $ext                      File extension, eg. ".png".
     * @param string        $dir                      Directory path.
     * @param callable|null $unique_filename_callback Callback function that generates the unique file name.
     */
    return apply_filters('wp_unique_filename', $filename, $ext, $dir, $unique_filename_callback);
}

WordPress Version: .10

/**
 * Get a filename that is sanitized and unique for the given directory.
 *
 * If the filename is not unique, then a number will be added to the filename
 * before the extension, and will continue adding numbers until the filename is
 * unique.
 *
 * The callback is passed three parameters, the first one is the directory, the
 * second is the filename, and the third is the extension.
 *
 * @since 2.5.0
 *
 * @param string   $dir                      Directory.
 * @param string   $filename                 File name.
 * @param callable $unique_filename_callback Callback. Default null.
 * @return string New filename, if given wasn't unique.
 */
function wp_unique_filename($dir, $filename, $unique_filename_callback = null)
{
    // Sanitize the file name before we begin processing.
    $filename = sanitize_file_name($filename);
    $ext2 = null;
    // Separate the filename into a name and extension.
    $ext = pathinfo($filename, PATHINFO_EXTENSION);
    $name = pathinfo($filename, PATHINFO_BASENAME);
    if ($ext) {
        $ext = '.' . $ext;
    }
    // Edge case: if file is named '.ext', treat as an empty name.
    if ($name === $ext) {
        $name = '';
    }
    /*
     * Increment the file number until we have a unique file to save in $dir.
     * Use callback if supplied.
     */
    if ($unique_filename_callback && is_callable($unique_filename_callback)) {
        $filename = call_user_func($unique_filename_callback, $dir, $name, $ext);
    } else {
        $number = '';
        $fname = pathinfo($filename, PATHINFO_FILENAME);
        // Always append a number to file names that can potentially match image sub-size file names.
        if ($fname && preg_match('/-(?:\d+x\d+|scaled|rotated)$/', $fname)) {
            $number = 1;
            // At this point the file name may not be unique. This is tested below and the $number is incremented.
            $filename = str_replace("{$fname}{$ext}", "{$fname}-{$number}{$ext}", $filename);
        }
        // Change '.ext' to lower case.
        if ($ext && strtolower($ext) != $ext) {
            $ext2 = strtolower($ext);
            $filename2 = preg_replace('|' . preg_quote($ext) . '$|', $ext2, $filename);
            // Check for both lower and upper case extension or image sub-sizes may be overwritten.
            while (file_exists($dir . "/{$filename}") || file_exists($dir . "/{$filename2}")) {
                $new_number = (int) $number + 1;
                $filename = str_replace(array("-{$number}{$ext}", "{$number}{$ext}"), "-{$new_number}{$ext}", $filename);
                $filename2 = str_replace(array("-{$number}{$ext2}", "{$number}{$ext2}"), "-{$new_number}{$ext2}", $filename2);
                $number = $new_number;
            }
            $filename = $filename2;
        } else {
            while (file_exists($dir . "/{$filename}")) {
                $new_number = (int) $number + 1;
                if ('' === "{$number}{$ext}") {
                    $filename = "{$filename}-{$new_number}";
                } else {
                    $filename = str_replace(array("-{$number}{$ext}", "{$number}{$ext}"), "-{$new_number}{$ext}", $filename);
                }
                $number = $new_number;
            }
        }
        // Prevent collisions with existing file names that contain dimension-like strings
        // (whether they are subsizes or originals uploaded prior to #42437).
        $upload_dir = wp_get_upload_dir();
        // The (resized) image files would have name and extension, and will be in the uploads dir.
        if ($name && $ext && @is_dir($dir) && false !== strpos($dir, $upload_dir['basedir'])) {
            // List of all files and directories contained in $dir.
            $files = @scandir($dir);
            if (!empty($files)) {
                // Remove "dot" dirs.
                $files = array_diff($files, array('.', '..'));
            }
            if (!empty($files)) {
                // The extension case may have changed above.
                $new_ext = (!empty($ext2)) ? $ext2 : $ext;
                // Ensure this never goes into infinite loop
                // as it uses pathinfo() and regex in the check but string replacement for the changes.
                $count = count($files);
                $i = 0;
                while ($i <= $count && _wp_check_existing_file_names($filename, $files)) {
                    $new_number = (int) $number + 1;
                    $filename = str_replace(array("-{$number}{$new_ext}", "{$number}{$new_ext}"), "-{$new_number}{$new_ext}", $filename);
                    $number = $new_number;
                    $i++;
                }
            }
        }
    }
    /**
     * Filters the result when generating a unique file name.
     *
     * @since 4.5.0
     *
     * @param string        $filename                 Unique file name.
     * @param string        $ext                      File extension, eg. ".png".
     * @param string        $dir                      Directory path.
     * @param callable|null $unique_filename_callback Callback function that generates the unique file name.
     */
    return apply_filters('wp_unique_filename', $filename, $ext, $dir, $unique_filename_callback);
}

WordPress Version: 3.1

/**
 * Get a filename that is sanitized and unique for the given directory.
 *
 * If the filename is not unique, then a number will be added to the filename
 * before the extension, and will continue adding numbers until the filename is
 * unique.
 *
 * The callback is passed three parameters, the first one is the directory, the
 * second is the filename, and the third is the extension.
 *
 * @since 2.5.0
 *
 * @param string   $dir                      Directory.
 * @param string   $filename                 File name.
 * @param callable $unique_filename_callback Callback. Default null.
 * @return string New filename, if given wasn't unique.
 */
function wp_unique_filename($dir, $filename, $unique_filename_callback = null)
{
    // Sanitize the file name before we begin processing.
    $filename = sanitize_file_name($filename);
    // Separate the filename into a name and extension.
    $ext = pathinfo($filename, PATHINFO_EXTENSION);
    $name = pathinfo($filename, PATHINFO_BASENAME);
    if ($ext) {
        $ext = '.' . $ext;
    }
    // Edge case: if file is named '.ext', treat as an empty name.
    if ($name === $ext) {
        $name = '';
    }
    /*
     * Increment the file number until we have a unique file to save in $dir.
     * Use callback if supplied.
     */
    if ($unique_filename_callback && is_callable($unique_filename_callback)) {
        $filename = call_user_func($unique_filename_callback, $dir, $name, $ext);
    } else {
        $number = '';
        $fname = pathinfo($filename, PATHINFO_FILENAME);
        // Always append a number to file names that can potentially match image sub-size file names.
        if ($fname && preg_match('/-(?:\d+x\d+|scaled|rotated)$/', $fname)) {
            $number = 1;
            // At this point the file name may not be unique. This is tested below and the $number is incremented.
            $filename = str_replace("{$fname}{$ext}", "{$fname}-{$number}{$ext}", $filename);
        }
        // Change '.ext' to lower case.
        if ($ext && strtolower($ext) != $ext) {
            $ext2 = strtolower($ext);
            $filename2 = preg_replace('|' . preg_quote($ext) . '$|', $ext2, $filename);
            // Check for both lower and upper case extension or image sub-sizes may be overwritten.
            while (file_exists($dir . "/{$filename}") || file_exists($dir . "/{$filename2}")) {
                $new_number = (int) $number + 1;
                $filename = str_replace(array("-{$number}{$ext}", "{$number}{$ext}"), "-{$new_number}{$ext}", $filename);
                $filename2 = str_replace(array("-{$number}{$ext2}", "{$number}{$ext2}"), "-{$new_number}{$ext2}", $filename2);
                $number = $new_number;
            }
            $filename = $filename2;
        } else {
            while (file_exists($dir . "/{$filename}")) {
                $new_number = (int) $number + 1;
                if ('' === "{$number}{$ext}") {
                    $filename = "{$filename}-{$new_number}";
                } else {
                    $filename = str_replace(array("-{$number}{$ext}", "{$number}{$ext}"), "-{$new_number}{$ext}", $filename);
                }
                $number = $new_number;
            }
        }
        // Prevent collisions with existing file names that contain dimension-like strings
        // (whether they are subsizes or originals uploaded prior to #42437).
        // The (resized) image files would have name and extension, and will be in the uploads dir.
        if (@is_dir($dir) && $name && $ext) {
            // List of all files and directories contained in $dir (with the "dot" files removed).
            $files = array_diff(scandir($dir), array('.', '..'));
            if (!empty($files)) {
                while (_wp_check_existing_file_names($filename, $files)) {
                    $new_number = (int) $number + 1;
                    $filename = str_replace(array("-{$number}{$ext}", "{$number}{$ext}"), "-{$new_number}{$ext}", $filename);
                    $number = $new_number;
                }
            }
        }
    }
    /**
     * Filters the result when generating a unique file name.
     *
     * @since 4.5.0
     *
     * @param string        $filename                 Unique file name.
     * @param string        $ext                      File extension, eg. ".png".
     * @param string        $dir                      Directory path.
     * @param callable|null $unique_filename_callback Callback function that generates the unique file name.
     */
    return apply_filters('wp_unique_filename', $filename, $ext, $dir, $unique_filename_callback);
}

WordPress Version: 5.1

/**
 * Get a filename that is sanitized and unique for the given directory.
 *
 * If the filename is not unique, then a number will be added to the filename
 * before the extension, and will continue adding numbers until the filename is
 * unique.
 *
 * The callback is passed three parameters, the first one is the directory, the
 * second is the filename, and the third is the extension.
 *
 * @since 2.5.0
 *
 * @param string   $dir                      Directory.
 * @param string   $filename                 File name.
 * @param callable $unique_filename_callback Callback. Default null.
 * @return string New filename, if given wasn't unique.
 */
function wp_unique_filename($dir, $filename, $unique_filename_callback = null)
{
    // Sanitize the file name before we begin processing.
    $filename = sanitize_file_name($filename);
    // Separate the filename into a name and extension.
    $ext = pathinfo($filename, PATHINFO_EXTENSION);
    $name = pathinfo($filename, PATHINFO_BASENAME);
    if ($ext) {
        $ext = '.' . $ext;
    }
    // Edge case: if file is named '.ext', treat as an empty name.
    if ($name === $ext) {
        $name = '';
    }
    /*
     * Increment the file number until we have a unique file to save in $dir.
     * Use callback if supplied.
     */
    if ($unique_filename_callback && is_callable($unique_filename_callback)) {
        $filename = call_user_func($unique_filename_callback, $dir, $name, $ext);
    } else {
        $number = '';
        // Change '.ext' to lower case.
        if ($ext && strtolower($ext) != $ext) {
            $ext2 = strtolower($ext);
            $filename2 = preg_replace('|' . preg_quote($ext) . '$|', $ext2, $filename);
            // Check for both lower and upper case extension or image sub-sizes may be overwritten.
            while (file_exists($dir . "/{$filename}") || file_exists($dir . "/{$filename2}")) {
                $new_number = (int) $number + 1;
                $filename = str_replace(array("-{$number}{$ext}", "{$number}{$ext}"), "-{$new_number}{$ext}", $filename);
                $filename2 = str_replace(array("-{$number}{$ext2}", "{$number}{$ext2}"), "-{$new_number}{$ext2}", $filename2);
                $number = $new_number;
            }
            /**
             * Filters the result when generating a unique file name.
             *
             * @since 4.5.0
             *
             * @param string        $filename                 Unique file name.
             * @param string        $ext                      File extension, eg. ".png".
             * @param string        $dir                      Directory path.
             * @param callable|null $unique_filename_callback Callback function that generates the unique file name.
             */
            return apply_filters('wp_unique_filename', $filename2, $ext, $dir, $unique_filename_callback);
        }
        while (file_exists($dir . "/{$filename}")) {
            $new_number = (int) $number + 1;
            if ('' == "{$number}{$ext}") {
                $filename = "{$filename}-" . $new_number;
            } else {
                $filename = str_replace(array("-{$number}{$ext}", "{$number}{$ext}"), '-' . $new_number . $ext, $filename);
            }
            $number = $new_number;
        }
    }
    /** This filter is documented in wp-includes/functions.php */
    return apply_filters('wp_unique_filename', $filename, $ext, $dir, $unique_filename_callback);
}

WordPress Version: 7.3

/**
 * Get a filename that is sanitized and unique for the given directory.
 *
 * If the filename is not unique, then a number will be added to the filename
 * before the extension, and will continue adding numbers until the filename is
 * unique.
 *
 * The callback is passed three parameters, the first one is the directory, the
 * second is the filename, and the third is the extension.
 *
 * @since 2.5.0
 *
 * @param string   $dir                      Directory.
 * @param string   $filename                 File name.
 * @param callable $unique_filename_callback Callback. Default null.
 * @return string New filename, if given wasn't unique.
 */
function wp_unique_filename($dir, $filename, $unique_filename_callback = null)
{
    // Sanitize the file name before we begin processing.
    $filename = sanitize_file_name($filename);
    // Separate the filename into a name and extension.
    $ext = pathinfo($filename, PATHINFO_EXTENSION);
    $name = pathinfo($filename, PATHINFO_BASENAME);
    if ($ext) {
        $ext = '.' . $ext;
    }
    // Edge case: if file is named '.ext', treat as an empty name.
    if ($name === $ext) {
        $name = '';
    }
    /*
     * Increment the file number until we have a unique file to save in $dir.
     * Use callback if supplied.
     */
    if ($unique_filename_callback && is_callable($unique_filename_callback)) {
        $filename = call_user_func($unique_filename_callback, $dir, $name, $ext);
    } else {
        $number = '';
        // Change '.ext' to lower case.
        if ($ext && strtolower($ext) != $ext) {
            $ext2 = strtolower($ext);
            $filename2 = preg_replace('|' . preg_quote($ext) . '$|', $ext2, $filename);
            // Check for both lower and upper case extension or image sub-sizes may be overwritten.
            while (file_exists($dir . "/{$filename}") || file_exists($dir . "/{$filename2}")) {
                $new_number = (int) $number + 1;
                $filename = str_replace(array("-{$number}{$ext}", "{$number}{$ext}"), "-{$new_number}{$ext}", $filename);
                $filename2 = str_replace(array("-{$number}{$ext2}", "{$number}{$ext2}"), "-{$new_number}{$ext2}", $filename2);
                $number = $new_number;
            }
            /**
             * Filters the result when generating a unique file name.
             *
             * @since 4.5.0
             *
             * @param string        $filename                 Unique file name.
             * @param string        $ext                      File extension, eg. ".png".
             * @param string        $dir                      Directory path.
             * @param callable|null $unique_filename_callback Callback function that generates the unique file name.
             */
            return apply_filters('wp_unique_filename', $filename2, $ext, $dir, $unique_filename_callback);
        }
        while (file_exists($dir . "/{$filename}")) {
            $new_number = (int) $number + 1;
            if ('' == "{$number}{$ext}") {
                $filename = "{$filename}-" . $new_number;
            } else {
                $filename = str_replace(array("-{$number}{$ext}", "{$number}{$ext}"), "-" . $new_number . $ext, $filename);
            }
            $number = $new_number;
        }
    }
    /** This filter is documented in wp-includes/functions.php */
    return apply_filters('wp_unique_filename', $filename, $ext, $dir, $unique_filename_callback);
}

WordPress Version: 4.7

/**
 * Get a filename that is sanitized and unique for the given directory.
 *
 * If the filename is not unique, then a number will be added to the filename
 * before the extension, and will continue adding numbers until the filename is
 * unique.
 *
 * The callback is passed three parameters, the first one is the directory, the
 * second is the filename, and the third is the extension.
 *
 * @since 2.5.0
 *
 * @param string   $dir                      Directory.
 * @param string   $filename                 File name.
 * @param callable $unique_filename_callback Callback. Default null.
 * @return string New filename, if given wasn't unique.
 */
function wp_unique_filename($dir, $filename, $unique_filename_callback = null)
{
    // Sanitize the file name before we begin processing.
    $filename = sanitize_file_name($filename);
    // Separate the filename into a name and extension.
    $ext = pathinfo($filename, PATHINFO_EXTENSION);
    $name = pathinfo($filename, PATHINFO_BASENAME);
    if ($ext) {
        $ext = '.' . $ext;
    }
    // Edge case: if file is named '.ext', treat as an empty name.
    if ($name === $ext) {
        $name = '';
    }
    /*
     * Increment the file number until we have a unique file to save in $dir.
     * Use callback if supplied.
     */
    if ($unique_filename_callback && is_callable($unique_filename_callback)) {
        $filename = call_user_func($unique_filename_callback, $dir, $name, $ext);
    } else {
        $number = '';
        // Change '.ext' to lower case.
        if ($ext && strtolower($ext) != $ext) {
            $ext2 = strtolower($ext);
            $filename2 = preg_replace('|' . preg_quote($ext) . '$|', $ext2, $filename);
            // Check for both lower and upper case extension or image sub-sizes may be overwritten.
            while (file_exists($dir . "/{$filename}") || file_exists($dir . "/{$filename2}")) {
                $new_number = $number + 1;
                $filename = str_replace(array("-{$number}{$ext}", "{$number}{$ext}"), "-{$new_number}{$ext}", $filename);
                $filename2 = str_replace(array("-{$number}{$ext2}", "{$number}{$ext2}"), "-{$new_number}{$ext2}", $filename2);
                $number = $new_number;
            }
            /**
             * Filters the result when generating a unique file name.
             *
             * @since 4.5.0
             *
             * @param string        $filename                 Unique file name.
             * @param string        $ext                      File extension, eg. ".png".
             * @param string        $dir                      Directory path.
             * @param callable|null $unique_filename_callback Callback function that generates the unique file name.
             */
            return apply_filters('wp_unique_filename', $filename2, $ext, $dir, $unique_filename_callback);
        }
        while (file_exists($dir . "/{$filename}")) {
            if ('' == "{$number}{$ext}") {
                $filename = "{$filename}-" . ++$number;
            } else {
                $filename = str_replace(array("-{$number}{$ext}", "{$number}{$ext}"), "-" . ++$number . $ext, $filename);
            }
        }
    }
    /** This filter is documented in wp-includes/functions.php */
    return apply_filters('wp_unique_filename', $filename, $ext, $dir, $unique_filename_callback);
}

WordPress Version: 4.6

/**
 * Get a filename that is sanitized and unique for the given directory.
 *
 * If the filename is not unique, then a number will be added to the filename
 * before the extension, and will continue adding numbers until the filename is
 * unique.
 *
 * The callback is passed three parameters, the first one is the directory, the
 * second is the filename, and the third is the extension.
 *
 * @since 2.5.0
 *
 * @param string   $dir                      Directory.
 * @param string   $filename                 File name.
 * @param callable $unique_filename_callback Callback. Default null.
 * @return string New filename, if given wasn't unique.
 */
function wp_unique_filename($dir, $filename, $unique_filename_callback = null)
{
    // Sanitize the file name before we begin processing.
    $filename = sanitize_file_name($filename);
    // Separate the filename into a name and extension.
    $info = pathinfo($filename);
    $ext = (!empty($info['extension'])) ? '.' . $info['extension'] : '';
    $name = basename($filename, $ext);
    // Edge case: if file is named '.ext', treat as an empty name.
    if ($name === $ext) {
        $name = '';
    }
    /*
     * Increment the file number until we have a unique file to save in $dir.
     * Use callback if supplied.
     */
    if ($unique_filename_callback && is_callable($unique_filename_callback)) {
        $filename = call_user_func($unique_filename_callback, $dir, $name, $ext);
    } else {
        $number = '';
        // Change '.ext' to lower case.
        if ($ext && strtolower($ext) != $ext) {
            $ext2 = strtolower($ext);
            $filename2 = preg_replace('|' . preg_quote($ext) . '$|', $ext2, $filename);
            // Check for both lower and upper case extension or image sub-sizes may be overwritten.
            while (file_exists($dir . "/{$filename}") || file_exists($dir . "/{$filename2}")) {
                $new_number = $number + 1;
                $filename = str_replace(array("-{$number}{$ext}", "{$number}{$ext}"), "-{$new_number}{$ext}", $filename);
                $filename2 = str_replace(array("-{$number}{$ext2}", "{$number}{$ext2}"), "-{$new_number}{$ext2}", $filename2);
                $number = $new_number;
            }
            /**
             * Filters the result when generating a unique file name.
             *
             * @since 4.5.0
             *
             * @param string        $filename                 Unique file name.
             * @param string        $ext                      File extension, eg. ".png".
             * @param string        $dir                      Directory path.
             * @param callable|null $unique_filename_callback Callback function that generates the unique file name.
             */
            return apply_filters('wp_unique_filename', $filename2, $ext, $dir, $unique_filename_callback);
        }
        while (file_exists($dir . "/{$filename}")) {
            if ('' == "{$number}{$ext}") {
                $filename = "{$filename}-" . ++$number;
            } else {
                $filename = str_replace(array("-{$number}{$ext}", "{$number}{$ext}"), "-" . ++$number . $ext, $filename);
            }
        }
    }
    /** This filter is documented in wp-includes/functions.php */
    return apply_filters('wp_unique_filename', $filename, $ext, $dir, $unique_filename_callback);
}

WordPress Version: 4.5

/**
 * Get a filename that is sanitized and unique for the given directory.
 *
 * If the filename is not unique, then a number will be added to the filename
 * before the extension, and will continue adding numbers until the filename is
 * unique.
 *
 * The callback is passed three parameters, the first one is the directory, the
 * second is the filename, and the third is the extension.
 *
 * @since 2.5.0
 *
 * @param string   $dir                      Directory.
 * @param string   $filename                 File name.
 * @param callable $unique_filename_callback Callback. Default null.
 * @return string New filename, if given wasn't unique.
 */
function wp_unique_filename($dir, $filename, $unique_filename_callback = null)
{
    // Sanitize the file name before we begin processing.
    $filename = sanitize_file_name($filename);
    // Separate the filename into a name and extension.
    $info = pathinfo($filename);
    $ext = (!empty($info['extension'])) ? '.' . $info['extension'] : '';
    $name = basename($filename, $ext);
    // Edge case: if file is named '.ext', treat as an empty name.
    if ($name === $ext) {
        $name = '';
    }
    /*
     * Increment the file number until we have a unique file to save in $dir.
     * Use callback if supplied.
     */
    if ($unique_filename_callback && is_callable($unique_filename_callback)) {
        $filename = call_user_func($unique_filename_callback, $dir, $name, $ext);
    } else {
        $number = '';
        // Change '.ext' to lower case.
        if ($ext && strtolower($ext) != $ext) {
            $ext2 = strtolower($ext);
            $filename2 = preg_replace('|' . preg_quote($ext) . '$|', $ext2, $filename);
            // Check for both lower and upper case extension or image sub-sizes may be overwritten.
            while (file_exists($dir . "/{$filename}") || file_exists($dir . "/{$filename2}")) {
                $new_number = $number + 1;
                $filename = str_replace(array("-{$number}{$ext}", "{$number}{$ext}"), "-{$new_number}{$ext}", $filename);
                $filename2 = str_replace(array("-{$number}{$ext2}", "{$number}{$ext2}"), "-{$new_number}{$ext2}", $filename2);
                $number = $new_number;
            }
            /**
             * Filter the result when generating a unique file name.
             *
             * @since 4.5.0
             *
             * @param string        $filename                 Unique file name.
             * @param string        $ext                      File extension, eg. ".png".
             * @param string        $dir                      Directory path.
             * @param callable|null $unique_filename_callback Callback function that generates the unique file name.
             */
            return apply_filters('wp_unique_filename', $filename2, $ext, $dir, $unique_filename_callback);
        }
        while (file_exists($dir . "/{$filename}")) {
            if ('' == "{$number}{$ext}") {
                $filename = "{$filename}-" . ++$number;
            } else {
                $filename = str_replace(array("-{$number}{$ext}", "{$number}{$ext}"), "-" . ++$number . $ext, $filename);
            }
        }
    }
    /** This filter is documented in wp-includes/functions.php */
    return apply_filters('wp_unique_filename', $filename, $ext, $dir, $unique_filename_callback);
}

WordPress Version: 4.4

/**
 * Get a filename that is sanitized and unique for the given directory.
 *
 * If the filename is not unique, then a number will be added to the filename
 * before the extension, and will continue adding numbers until the filename is
 * unique.
 *
 * The callback is passed three parameters, the first one is the directory, the
 * second is the filename, and the third is the extension.
 *
 * @since 2.5.0
 *
 * @param string   $dir                      Directory.
 * @param string   $filename                 File name.
 * @param callable $unique_filename_callback Callback. Default null.
 * @return string New filename, if given wasn't unique.
 */
function wp_unique_filename($dir, $filename, $unique_filename_callback = null)
{
    // Sanitize the file name before we begin processing.
    $filename = sanitize_file_name($filename);
    // Separate the filename into a name and extension.
    $info = pathinfo($filename);
    $ext = (!empty($info['extension'])) ? '.' . $info['extension'] : '';
    $name = basename($filename, $ext);
    // Edge case: if file is named '.ext', treat as an empty name.
    if ($name === $ext) {
        $name = '';
    }
    /*
     * Increment the file number until we have a unique file to save in $dir.
     * Use callback if supplied.
     */
    if ($unique_filename_callback && is_callable($unique_filename_callback)) {
        $filename = call_user_func($unique_filename_callback, $dir, $name, $ext);
    } else {
        $number = '';
        // Change '.ext' to lower case.
        if ($ext && strtolower($ext) != $ext) {
            $ext2 = strtolower($ext);
            $filename2 = preg_replace('|' . preg_quote($ext) . '$|', $ext2, $filename);
            // Check for both lower and upper case extension or image sub-sizes may be overwritten.
            while (file_exists($dir . "/{$filename}") || file_exists($dir . "/{$filename2}")) {
                $new_number = $number + 1;
                $filename = str_replace(array("-{$number}{$ext}", "{$number}{$ext}"), "-{$new_number}{$ext}", $filename);
                $filename2 = str_replace(array("-{$number}{$ext2}", "{$number}{$ext2}"), "-{$new_number}{$ext2}", $filename2);
                $number = $new_number;
            }
            return $filename2;
        }
        while (file_exists($dir . "/{$filename}")) {
            if ('' == "{$number}{$ext}") {
                $filename = "{$filename}-" . ++$number;
            } else {
                $filename = str_replace(array("-{$number}{$ext}", "{$number}{$ext}"), "-" . ++$number . $ext, $filename);
            }
        }
    }
    return $filename;
}

WordPress Version: 4.0

/**
 * Get a filename that is sanitized and unique for the given directory.
 *
 * If the filename is not unique, then a number will be added to the filename
 * before the extension, and will continue adding numbers until the filename is
 * unique.
 *
 * The callback is passed three parameters, the first one is the directory, the
 * second is the filename, and the third is the extension.
 *
 * @since 2.5.0
 *
 * @param string   $dir                      Directory.
 * @param string   $filename                 File name.
 * @param callback $unique_filename_callback Callback. Default null.
 * @return string New filename, if given wasn't unique.
 */
function wp_unique_filename($dir, $filename, $unique_filename_callback = null)
{
    // Sanitize the file name before we begin processing.
    $filename = sanitize_file_name($filename);
    // Separate the filename into a name and extension.
    $info = pathinfo($filename);
    $ext = (!empty($info['extension'])) ? '.' . $info['extension'] : '';
    $name = basename($filename, $ext);
    // Edge case: if file is named '.ext', treat as an empty name.
    if ($name === $ext) {
        $name = '';
    }
    /*
     * Increment the file number until we have a unique file to save in $dir.
     * Use callback if supplied.
     */
    if ($unique_filename_callback && is_callable($unique_filename_callback)) {
        $filename = call_user_func($unique_filename_callback, $dir, $name, $ext);
    } else {
        $number = '';
        // Change '.ext' to lower case.
        if ($ext && strtolower($ext) != $ext) {
            $ext2 = strtolower($ext);
            $filename2 = preg_replace('|' . preg_quote($ext) . '$|', $ext2, $filename);
            // Check for both lower and upper case extension or image sub-sizes may be overwritten.
            while (file_exists($dir . "/{$filename}") || file_exists($dir . "/{$filename2}")) {
                $new_number = $number + 1;
                $filename = str_replace("{$number}{$ext}", "{$new_number}{$ext}", $filename);
                $filename2 = str_replace("{$number}{$ext2}", "{$new_number}{$ext2}", $filename2);
                $number = $new_number;
            }
            return $filename2;
        }
        while (file_exists($dir . "/{$filename}")) {
            if ('' == "{$number}{$ext}") {
                $filename = $filename . ++$number . $ext;
            } else {
                $filename = str_replace("{$number}{$ext}", ++$number . $ext, $filename);
            }
        }
    }
    return $filename;
}

WordPress Version: 3.7

/**
 * Get a filename that is sanitized and unique for the given directory.
 *
 * If the filename is not unique, then a number will be added to the filename
 * before the extension, and will continue adding numbers until the filename is
 * unique.
 *
 * The callback is passed three parameters, the first one is the directory, the
 * second is the filename, and the third is the extension.
 *
 * @since 2.5.0
 *
 * @param string $dir
 * @param string $filename
 * @param mixed $unique_filename_callback Callback.
 * @return string New filename, if given wasn't unique.
 */
function wp_unique_filename($dir, $filename, $unique_filename_callback = null)
{
    // sanitize the file name before we begin processing
    $filename = sanitize_file_name($filename);
    // separate the filename into a name and extension
    $info = pathinfo($filename);
    $ext = (!empty($info['extension'])) ? '.' . $info['extension'] : '';
    $name = basename($filename, $ext);
    // edge case: if file is named '.ext', treat as an empty name
    if ($name === $ext) {
        $name = '';
    }
    // Increment the file number until we have a unique file to save in $dir. Use callback if supplied.
    if ($unique_filename_callback && is_callable($unique_filename_callback)) {
        $filename = call_user_func($unique_filename_callback, $dir, $name, $ext);
    } else {
        $number = '';
        // change '.ext' to lower case
        if ($ext && strtolower($ext) != $ext) {
            $ext2 = strtolower($ext);
            $filename2 = preg_replace('|' . preg_quote($ext) . '$|', $ext2, $filename);
            // check for both lower and upper case extension or image sub-sizes may be overwritten
            while (file_exists($dir . "/{$filename}") || file_exists($dir . "/{$filename2}")) {
                $new_number = $number + 1;
                $filename = str_replace("{$number}{$ext}", "{$new_number}{$ext}", $filename);
                $filename2 = str_replace("{$number}{$ext2}", "{$new_number}{$ext2}", $filename2);
                $number = $new_number;
            }
            return $filename2;
        }
        while (file_exists($dir . "/{$filename}")) {
            if ('' == "{$number}{$ext}") {
                $filename = $filename . ++$number . $ext;
            } else {
                $filename = str_replace("{$number}{$ext}", ++$number . $ext, $filename);
            }
        }
    }
    return $filename;
}