各位,今天咱们来聊聊WordPress里一个看似简单,实则暗藏玄机的函数:wp_upload_dir()
。 它负责告诉我们,文件应该往哪儿上传,上传后的网址是啥。 别看它名字平平无奇,背后可是藏着一套逻辑严谨的目录生成和路径拼接的机制。 准备好了吗? 咱们这就深入源码,把它的底裤都扒下来,看看它到底是怎么运作的!
开场白:与wp_upload_dir()
的第一次亲密接触
想象一下,你正在开发一个WordPress插件,需要上传一些用户头像。 你肯定不想把这些头像直接扔到WordPress的根目录,对吧? 这时候,wp_upload_dir()
就派上用场了。 它能帮你找到合适的上传目录,并且返回一个包含各种路径信息的数组。
简单来说,wp_upload_dir()
就是WordPress的文件上传“导航员”。
源码解读:一层一层地揭开wp_upload_dir()
的神秘面纱
好了,废话不多说,直接上代码! (以下代码基于WordPress 6.4.3,不同版本可能会有细微差异,但核心逻辑不变。)
function wp_upload_dir( $time = null, $create_dir = true, $deprecated = false ) {
global $switched;
if ( ! empty( $deprecated ) ) {
_deprecated_argument( __FUNCTION__, '2.0' );
}
$siteurl = get_option( 'siteurl' );
$upload_path = trim( get_option( 'upload_path' ) );
if ( is_multisite() ) {
if ( ! isset( $switched ) ) {
$ms_files_rewriting = (bool) get_site_option( 'ms_files_rewriting' );
} else {
$ms_files_rewriting = $switched ? false : (bool) get_site_option( 'ms_files_rewriting' );
}
}
if ( empty( $upload_path ) || 'wp-content/uploads' == $upload_path || ( isset( $ms_files_rewriting ) && ! $ms_files_rewriting ) ) {
$dir = WP_CONTENT_DIR . '/uploads';
} else {
$dir = ABSPATH . $upload_path;
}
$url = str_replace( WP_CONTENT_DIR, content_url(), $dir );
if ( defined( 'UPLOADS' ) && false === strpos( $dir, WP_CONTENT_DIR ) ) {
$dir = ABSPATH . UPLOADS;
$url = $siteurl . '/' . UPLOADS;
}
if ( is_ssl() && ! is_admin() && strpos( $url, 'https' ) === false ) {
$url = str_replace( 'http://', 'https://', $url );
}
/**
* Filters the uploads directory location.
*
* @since 2.0.0
*
* @param array $uploads Array of upload directory data with keys of 'path',
* 'url', 'subdir, 'basedir', and 'baseurl'.
*/
$upload = apply_filters( 'upload_dir', [ 'path' => $dir, 'url' => $url, 'subdir' => '', 'basedir' => $dir, 'baseurl' => $url, 'error' => false ] );
$upload_dir = $upload['path'];
$upload_url = $upload['url'];
if ( ! empty( $time ) ) {
$time = preg_replace( '/[^0-9]/', '', $time );
if ( strlen( $time ) == 4 ) {
$time = $time . '/00';
}
$time = gmdate( 'Y/m', strtotime( $time ) );
}
if ( ! empty( $time ) ) {
$upload_dir .= '/' . $time;
$upload_url .= '/' . $time;
$upload['subdir'] = '/' . $time;
}
$upload['path'] = $upload_dir;
$upload['url'] = $upload_url;
if ( ( ( ! is_dir( $upload_dir ) ) && $create_dir ) ) {
$old_umask = umask( 0 );
if ( wp_mkdir_p( $upload_dir ) ) {
$error = false;
if ( get_site_option( 'upload_filetypes' ) ) {
// Set correct file permissions on upload folder, if possible.
$perms = fileperms( dirname( $upload_dir ) );
if ( $perms ) {
chmod( $upload_dir, $perms & 0777 | 0775 );
}
}
} else {
$upload['error'] = sprintf(
/* translators: %s: Directory path. */
__( 'Unable to create directory %s. Is its parent directory writable by the server?' ),
esc_html( $upload_dir )
);
$error = true;
}
umask( $old_umask );
}
/**
* Filters the uploads directory data.
*
* @since 2.0.0
*
* @param array $upload Array of upload directory data with keys of 'path',
* 'url', 'subdir', 'basedir', and 'baseurl'.
* @param string $context The context for the filter. Possible values are
* 'upload_dir', 'wp_handle_upload', and 'wp_handle_sideload'.
*/
return apply_filters( 'wp_upload_dir', $upload, 'upload_dir' );
}
是不是感觉有点头大? 别怕,咱们一步一步来分析。
1. 获取基本配置信息
首先,函数会获取一些重要的配置信息,包括:
siteurl
: WordPress的站点URL,通常是你的域名。upload_path
: 上传目录的相对路径。 这个选项可以在WordPress后台的“设置 -> 媒体”中配置。 如果没有配置,或者配置为默认值wp-content/uploads
,那么上传目录就会位于wp-content/uploads
下。
$siteurl = get_option( 'siteurl' );
$upload_path = trim( get_option( 'upload_path' ) );
2. 多站点判断( Multisite )
如果是多站点环境,还会判断是否启用了 ms_files_rewriting
。 这个选项决定了文件是如何存储和访问的。 简单来说,如果启用了,每个站点的文件都会存储在不同的子目录中;如果没有启用,所有站点的文件都会存储在同一个目录中。
if ( is_multisite() ) {
if ( ! isset( $switched ) ) {
$ms_files_rewriting = (bool) get_site_option( 'ms_files_rewriting' );
} else {
$ms_files_rewriting = $switched ? false : (bool) get_site_option( 'ms_files_rewriting' );
}
}
3. 确定上传目录的基本路径
接下来,函数会根据 upload_path
和 ms_files_rewriting
的值,确定上传目录的基本路径。
- 如果
upload_path
为空,或者为默认值wp-content/uploads
,或者在多站点环境下ms_files_rewriting
为false
,那么上传目录的基本路径就是WP_CONTENT_DIR . '/uploads'
。 - 否则,上传目录的基本路径就是
ABSPATH . $upload_path
。
if ( empty( $upload_path ) || 'wp-content/uploads' == $upload_path || ( isset( $ms_files_rewriting ) && ! $ms_files_rewriting ) ) {
$dir = WP_CONTENT_DIR . '/uploads';
} else {
$dir = ABSPATH . $upload_path;
}
$url = str_replace( WP_CONTENT_DIR, content_url(), $dir );
这里需要注意的是:
WP_CONTENT_DIR
是wp-content
目录的绝对路径。ABSPATH
是 WordPress 根目录的绝对路径。content_url()
函数返回wp-content
目录的 URL。
4. 使用 UPLOADS
常量覆盖默认路径
如果定义了 UPLOADS
常量,并且上传目录不在 wp-content
目录下,那么就使用 UPLOADS
常量来覆盖之前的设置。
if ( defined( 'UPLOADS' ) && false === strpos( $dir, WP_CONTENT_DIR ) ) {
$dir = ABSPATH . UPLOADS;
$url = $siteurl . '/' . UPLOADS;
}
这个常量允许开发者将上传目录放在 WordPress 根目录之外,提供更大的灵活性。
5. 处理 SSL 连接
如果当前连接是 SSL 连接( HTTPS ),并且不在后台管理界面,并且 URL 中没有 https
, 那么就将 URL 中的 http
替换为 https
。
if ( is_ssl() && ! is_admin() && strpos( $url, 'https' ) === false ) {
$url = str_replace( 'http://', 'https://', $url );
}
6. 应用 upload_dir
过滤器
到目前为止,我们已经确定了上传目录的基本路径和 URL。 但是,WordPress允许我们通过 upload_dir
过滤器来修改这些值。
/**
* Filters the uploads directory location.
*
* @since 2.0.0
*
* @param array $uploads Array of upload directory data with keys of 'path',
* 'url', 'subdir, 'basedir', and 'baseurl'.
*/
$upload = apply_filters( 'upload_dir', [ 'path' => $dir, 'url' => $url, 'subdir' => '', 'basedir' => $dir, 'baseurl' => $url, 'error' => false ] );
这个过滤器接收一个数组作为参数,该数组包含了以下键:
path
: 上传目录的绝对路径。url
: 上传目录的 URL。subdir
: 子目录的相对路径(相对于basedir
)。basedir
: 上传目录的基本路径。baseurl
: 上传目录的基本 URL。error
: 错误信息。
通过使用这个过滤器,你可以完全控制上传目录的位置和 URL。
7. 处理时间参数($time
)
wp_upload_dir()
函数允许我们传递一个时间参数 $time
,用来指定上传目录的子目录。 如果传递了 $time
参数,函数会根据这个时间生成一个形如 YYYY/MM
的子目录。
if ( ! empty( $time ) ) {
$time = preg_replace( '/[^0-9]/', '', $time );
if ( strlen( $time ) == 4 ) {
$time = $time . '/00';
}
$time = gmdate( 'Y/m', strtotime( $time ) );
}
if ( ! empty( $time ) ) {
$upload_dir .= '/' . $time;
$upload_url .= '/' . $time;
$upload['subdir'] = '/' . $time;
}
这里有几个需要注意的地方:
preg_replace( '/[^0-9]/', '', $time )
会移除$time
参数中所有非数字字符。- 如果
$time
参数的长度为 4,那么就假设它代表年份,并在后面添加/00
,表示 1 月份。 gmdate( 'Y/m', strtotime( $time ) )
会将$time
参数转换为YYYY/MM
格式的字符串。
8. 创建目录
如果上传目录不存在,并且 $create_dir
参数为 true
(默认值),那么函数会尝试创建该目录。
if ( ( ( ! is_dir( $upload_dir ) ) && $create_dir ) ) {
$old_umask = umask( 0 );
if ( wp_mkdir_p( $upload_dir ) ) {
$error = false;
if ( get_site_option( 'upload_filetypes' ) ) {
// Set correct file permissions on upload folder, if possible.
$perms = fileperms( dirname( $upload_dir ) );
if ( $perms ) {
chmod( $upload_dir, $perms & 0777 | 0775 );
}
}
} else {
$upload['error'] = sprintf(
/* translators: %s: Directory path. */
__( 'Unable to create directory %s. Is its parent directory writable by the server?' ),
esc_html( $upload_dir )
);
$error = true;
}
umask( $old_umask );
}
这里使用了 wp_mkdir_p()
函数来递归创建目录。 这个函数可以确保所有父目录都存在,即使它们不存在,也会被创建。
同时,还会尝试设置上传目录的权限,以确保 Web 服务器可以写入该目录。
9. 应用 wp_upload_dir
过滤器
最后,函数会再次应用一个过滤器:wp_upload_dir
。
/**
* Filters the uploads directory data.
*
* @since 2.0.0
*
* @param array $upload Array of upload directory data with keys of 'path',
* 'url', 'subdir', 'basedir', and 'baseurl'.
* @param string $context The context for the filter. Possible values are
* 'upload_dir', 'wp_handle_upload', and 'wp_handle_sideload'.
*/
return apply_filters( 'wp_upload_dir', $upload, 'upload_dir' );
这个过滤器与之前的 upload_dir
过滤器类似,但是它是在创建目录之后应用的。 这意味着你可以在这里修改最终的上传目录信息,例如,你可以根据目录创建的结果来设置错误信息。
10. 返回结果
函数最终返回一个数组,包含了以下信息:
path
: 上传目录的绝对路径。url
: 上传目录的 URL。subdir
: 子目录的相对路径(相对于basedir
)。basedir
: 上传目录的基本路径。baseurl
: 上传目录的基本 URL。error
: 错误信息。
案例分析:wp_upload_dir()
的实际应用
为了更好地理解 wp_upload_dir()
的用法,我们来看几个实际的案例。
案例 1:使用默认配置
如果你的 WordPress 站点使用默认的上传配置,那么 wp_upload_dir()
函数会返回以下信息:
键 | 值 |
---|---|
path |
/path/to/wp-content/uploads |
url |
http://yourdomain.com/wp-content/uploads |
subdir |
“ |
basedir |
/path/to/wp-content/uploads |
baseurl |
http://yourdomain.com/wp-content/uploads |
error |
false |
案例 2:自定义上传目录
如果你在 WordPress 后台的“设置 -> 媒体”中,将上传目录设置为 files
,那么 wp_upload_dir()
函数会返回以下信息:
键 | 值 |
---|---|
path |
/path/to/wordpress/files |
url |
http://yourdomain.com/files |
subdir |
“ |
basedir |
/path/to/wordpress/files |
baseurl |
http://yourdomain.com/files |
error |
false |
案例 3:使用时间参数
如果你调用 wp_upload_dir( '2023-10-27' )
,那么 wp_upload_dir()
函数会返回以下信息:
键 | 值 |
---|---|
path |
/path/to/wp-content/uploads/2023/10 |
url |
http://yourdomain.com/wp-content/uploads/2023/10 |
subdir |
/2023/10 |
basedir |
/path/to/wp-content/uploads |
baseurl |
http://yourdomain.com/wp-content/uploads |
error |
false |
案例 4:使用过滤器修改上传目录
假设你想将所有上传文件都存储在 wp-content/my-uploads
目录下,你可以使用以下代码:
add_filter( 'upload_dir', 'my_custom_upload_dir' );
function my_custom_upload_dir( $dirs ) {
$dirs['basedir'] = WP_CONTENT_DIR . '/my-uploads';
$dirs['baseurl'] = content_url( 'my-uploads' );
$dirs['path'] = $dirs['basedir'] . $dirs['subdir'];
$dirs['url'] = $dirs['baseurl'] . $dirs['subdir'];
return $dirs;
}
这段代码会将 upload_dir
过滤器的 basedir
和 baseurl
属性修改为 wp-content/my-uploads
目录,从而实现自定义上传目录的目的。
总结:wp_upload_dir()
的核心要点
wp_upload_dir()
函数负责确定 WordPress 的上传目录。- 它会考虑多种因素,包括站点配置、多站点环境、
UPLOADS
常量和时间参数。 - 你可以使用
upload_dir
和wp_upload_dir
过滤器来修改上传目录的信息。 - 该函数会尝试创建上传目录,并设置正确的权限。
- 最终,它会返回一个包含上传目录信息的数组。
最后的话:掌握wp_upload_dir()
,成为文件上传大师!
好了,今天的讲座就到这里。 希望通过这次深入的源码解读和案例分析,你已经对 wp_upload_dir()
函数有了更深刻的理解。 掌握了这个函数,你就可以灵活地控制 WordPress 的文件上传行为,更好地满足你的开发需求。 记住,编程就像剥洋葱,一层一层地剥开,才能看到最核心的东西。 下次再见!