深入理解 WordPress `_wp_http_get_remote_file_ext()` 函数的源码:如何通过远程文件头获取扩展名。

大家好,我是老码,今天咱们来聊聊 WordPress 里面一个挺有趣的小函数:_wp_http_get_remote_file_ext()。 别看它名字挺长,其实干的活儿很简单,就是从远程文件的 HTTP 头里扒拉出文件扩展名。

这玩意儿听起来好像没啥大不了的,但实际上在很多场景下都很有用,比如说,你想远程下载一个文件,但又不知道它是什么类型的,这时候就可以用这个函数先探探底,再决定怎么处理。

咱们今天就来扒一扒它的源码,看看它到底是怎么做到的。

一、 初探门径:函数签名与基本逻辑

首先,咱们得先知道这个函数的庐山真面目,看看它的函数签名:

/**
 * Get the file extension from the remote file headers.
 *
 * @since 4.6.0
 * @access private
 *
 * @param string $url Remote URL.
 * @return string|false File extension if found, otherwise false.
 */
function _wp_http_get_remote_file_ext( $url ) {
  // ... 函数体 ...
}

可以看到,这个函数接收一个 URL 作为参数,返回一个字符串(文件扩展名)或者 false。 如果找不到扩展名,它会很诚实地告诉你,返回 false

它的基本逻辑是这样的:

  1. 发送 HTTP HEAD 请求: 只请求文件的头部信息,不下载整个文件,这样可以节省资源。
  2. 检查 Content-Type 头部: 这个头部通常会告诉我们文件的 MIME 类型,比如 image/jpeg 或者 application/pdf
  3. 根据 MIME 类型推断扩展名: 不同的 MIME 类型对应不同的扩展名,比如 image/jpeg 对应 .jpg 或者 .jpeg
  4. 返回扩展名: 如果成功推断出扩展名,就返回它;否则,返回 false

二、 步步深入:源码剖析

接下来,咱们一行一行地分析源码,看看它是怎么实现这些逻辑的。

function _wp_http_get_remote_file_ext( $url ) {
  $response = wp_remote_head( $url );

  if ( is_wp_error( $response ) ) {
    return false;
  }

  $content_type = wp_remote_retrieve_header( $response, 'content-type' );

  if ( empty( $content_type ) ) {
    return false;
  }

  $content_type = sanitize_mime_type( $content_type );

  // Use MIME type mappings, similar to wp_get_mime_types().
  $mime_to_ext = array(
    'image/jpeg'                          => 'jpg',
    'image/gif'                           => 'gif',
    'image/png'                           => 'png',
    'image/bmp'                           => 'bmp',
    'image/tiff'                          => 'tif',
    'application/pdf'                     => 'pdf',
    'application/x-shockwave-flash'       => 'swf',
    'application/x-msdownload'            => 'exe',
    'application/octet-stream'            => '', // Handle this specifically below
    'text/plain'                          => 'txt',
    'text/csv'                            => 'csv',
    'application/msword'                  => 'doc',
    'application/vnd.ms-excel'            => 'xls',
    'application/vnd.ms-powerpoint'       => 'ppt',
    'application/zip'                     => 'zip',
    'application/x-gzip'                  => 'gz',
    'application/x-tar'                   => 'tar',
    'application/x-7z-compressed'         => '7z',
    'audio/mpeg'                          => 'mp3',
    'audio/x-wav'                         => 'wav',
    'video/mpeg'                          => 'mpg',
    'video/mp4'                           => 'mp4',
    'video/quicktime'                     => 'mov',
    'video/x-msvideo'                     => 'avi',
    'video/x-flv'                         => 'flv',
    'video/webm'                          => 'webm',
    'audio/ogg'                           => 'ogg',
    'video/ogg'                           => 'ogv',
    'application/ogg'                     => 'ogx',
  );

  if ( isset( $mime_to_ext[ $content_type ] ) ) {
    return $mime_to_ext[ $content_type ];
  }

  // Special handling for application/octet-stream.  Try to sniff the file
  // extension from the URL.
  if ( 'application/octet-stream' === $content_type ) {
    $pathinfo = wp_parse_url( $url );
    if ( ! empty( $pathinfo['path'] ) ) {
      $extension = pathinfo( $pathinfo['path'], PATHINFO_EXTENSION );
      if ( ! empty( $extension ) ) {
        return $extension;
      }
    }
  }

  return false;
}

现在,咱们把这段代码拆开揉碎了,一点一点地解释:

  1. 发送 HTTP HEAD 请求:

    $response = wp_remote_head( $url );
    
    if ( is_wp_error( $response ) ) {
      return false;
    }

    这里使用了 WordPress 自带的 wp_remote_head() 函数来发送 HTTP HEAD 请求。 这个函数会返回一个 WP_Error 对象,如果请求失败了,就会返回一个错误对象,然后咱们就直接返回 falsewp_remote_head 实际上就是对 WordPress HTTP API 的一个封装,它比直接用 curl 或者 fopen 要方便得多,而且可以利用 WordPress 的插件和主题机制进行扩展。

  2. 获取 Content-Type 头部:

    $content_type = wp_remote_retrieve_header( $response, 'content-type' );
    
    if ( empty( $content_type ) ) {
      return false;
    }
    
    $content_type = sanitize_mime_type( $content_type );

    这里使用了 wp_remote_retrieve_header() 函数来获取 Content-Type 头部的值。 如果这个头部不存在,或者值为空,那就说明服务器没有提供文件的 MIME 类型信息,这时候我们也只能返回 false

    sanitize_mime_type() 函数的作用是清理 MIME 类型字符串,防止出现一些奇怪的字符或者格式问题。 它会将 MIME 类型转换为小写,并移除一些不必要的空格和字符。

  3. MIME 类型到扩展名的映射:

    $mime_to_ext = array(
      'image/jpeg'                          => 'jpg',
      'image/gif'                           => 'gif',
      'image/png'                           => 'png',
      // ... 更多映射 ...
    );
    
    if ( isset( $mime_to_ext[ $content_type ] ) ) {
      return $mime_to_ext[ $content_type ];
    }

    这是一个非常关键的部分。 $mime_to_ext 数组定义了 MIME 类型和文件扩展名之间的映射关系。 如果 Content-Type 头部的值在这个数组中找到了对应的键,那么就返回对应的值,也就是文件扩展名。

    这个数组里的映射关系是比较常见的,但并不完整。 如果遇到了不在这个数组中的 MIME 类型,那么就需要添加新的映射关系。 WordPress 自身也提供了 wp_get_mime_types() 函数来获取更完整的 MIME 类型列表,但 _wp_http_get_remote_file_ext() 函数并没有直接使用它,可能是出于性能或者兼容性的考虑。

  4. 处理 application/octet-stream

    if ( 'application/octet-stream' === $content_type ) {
      $pathinfo = wp_parse_url( $url );
      if ( ! empty( $pathinfo['path'] ) ) {
        $extension = pathinfo( $pathinfo['path'], PATHINFO_EXTENSION );
        if ( ! empty( $extension ) ) {
          return $extension;
        }
      }
    }

    application/octet-stream 是一个比较特殊的 MIME 类型,它表示服务器不知道文件的具体类型,或者不想告诉客户端文件的具体类型。 遇到这种情况,我们就不能直接根据 MIME 类型来推断扩展名了。

    _wp_http_get_remote_file_ext() 函数的处理方式是从 URL 中提取文件扩展名。 它首先使用 wp_parse_url() 函数来解析 URL,然后从 URL 的路径部分提取扩展名。 如果 URL 中包含扩展名,那么就返回它;否则,返回 false

    这种处理方式并不总是可靠的,因为 URL 中可能没有包含扩展名,或者扩展名是错误的。 但是,在 application/octet-stream 这种情况下,这是唯一可行的办法。

  5. 返回 false

    return false;

    如果所有的尝试都失败了,那么就只能返回 false,表示无法确定文件的扩展名。

三、 实战演练:代码示例

光说不练假把式,咱们来写几个例子,看看这个函数到底怎么用。

$url1 = 'https://example.com/image.jpg';
$ext1 = _wp_http_get_remote_file_ext( $url1 );
echo "URL: $url1, Extension: $ext1n"; // 输出:URL: https://example.com/image.jpg, Extension: jpg

$url2 = 'https://example.com/document.pdf';
$ext2 = _wp_http_get_remote_file_ext( $url2 );
echo "URL: $url2, Extension: $ext2n"; // 输出:URL: https://example.com/document.pdf, Extension: pdf

$url3 = 'https://example.com/random_file';
$ext3 = _wp_http_get_remote_file_ext( $url3 );
echo "URL: $url3, Extension: n"; // 输出:URL: https://example.com/random_file, Extension:

$url4 = 'https://example.com/download?file=archive.zip';
$ext4 = _wp_http_get_remote_file_ext( $url4 );
echo "URL: $url4, Extension: zipn"; // 输出:URL: https://example.com/download?file=archive.zip, Extension: zip

这些例子展示了 _wp_http_get_remote_file_ext() 函数在不同情况下的表现。 可以看到,它可以正确地识别出 JPEG、PDF 和 ZIP 文件的扩展名。 但是,如果 URL 中没有包含扩展名,或者服务器没有提供 Content-Type 头部,那么它就只能返回 false

四、 深入思考:局限性与改进

_wp_http_get_remote_file_ext() 函数虽然简单实用,但也有一些局限性:

  • 依赖于 Content-Type 头部和 URL 扩展名: 如果服务器没有提供 Content-Type 头部,或者 URL 中没有包含扩展名,那么它就无法确定文件的扩展名。
  • 映射关系不完整: $mime_to_ext 数组中的映射关系是有限的,可能无法识别所有的 MIME 类型。
  • 无法处理压缩文件中的文件类型: 如果远程文件是一个压缩文件(比如 ZIP 文件),那么它只能识别出 ZIP 文件的扩展名,而无法识别出压缩文件中的文件的类型。

那么,有没有办法改进这个函数呢?

  • 使用更完整的 MIME 类型列表: 可以考虑使用 wp_get_mime_types() 函数来获取更完整的 MIME 类型列表,或者使用第三方的 MIME 类型数据库。
  • 尝试读取文件内容: 如果无法从 Content-Type 头部或者 URL 中提取扩展名,可以尝试读取文件的一部分内容,然后根据文件内容来推断文件类型。 这种方法需要消耗更多的资源,但是可以提高识别的准确率。 可以使用 wp_remote_get() 函数获取文件内容,然后使用 wp_check_filetype() 函数检查文件类型。
  • 利用第三方服务: 可以使用第三方的文件类型识别服务,比如 VirusTotal 或者 FileScan.IO。 这些服务通常会提供更准确的文件类型识别结果,但是需要付费。

五、 总结陈词:小函数,大作用

_wp_http_get_remote_file_ext() 函数是一个非常小的函数,但它在 WordPress 中扮演着重要的角色。 它可以帮助我们确定远程文件的类型,从而更好地处理这些文件。 虽然它有一些局限性,但是我们可以通过一些方法来改进它,提高识别的准确率。

希望今天的讲座能够帮助大家更好地理解 _wp_http_get_remote_file_ext() 函数的源码和使用方法。 记住,不要小看任何一个小的函数,它们都可能在你的项目中发挥重要的作用。

最后,送给大家一句老码的座右铭: “代码就像女朋友,你要用心对待,才能让她为你服务。” 下课!

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注