千家信息网

PHP中Mkdir函数如何使用

发表于:2026-11-01 作者:千家信息网编辑
千家信息网最后更新 2026年11月01日,本篇文章给大家分享的是有关PHP中Mkdir函数如何使用,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。此过程中例如需要写入目标文件为:首
千家信息网最后更新 2026年11月01日PHP中Mkdir函数如何使用

本篇文章给大家分享的是有关PHP中Mkdir函数如何使用,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。

此过程中例如需要写入目标文件为:

首先需要先写一张

为什么会这样?假设直接写目标文件,过程中会首先创建目录:

其实这个过程是没有创建任何目录的,因为判断是directoryalready ,到下一步写入图片这里是Imagick::writeImage,在这里就会出问题。invaildfile path.报错。因为这里不存在 /var/www/html/wordpress/wp-content/uploads/2019/03/1.jpg? 这个目录,这涉及到系统调用,因系统的不同相对于的系统处理函数处理的方式也不同。

列如在kali 下 Imagick::writeImage写入 ./1?/../1.png , ./1? 这个目录是会报错的。具体系统调用如下:

首先判断了这个文件的状态,而后调用openat 打开这个文件并不存在。AT_FDCWD 表示打开的文件位置相对于当前目录。这是我在做的时候遇到的情况。

但是在 WORDPRESS IMAGE 远程代码执行漏洞分析 一文中,甚至其他另一篇。都没提到两次写图片。难道因为window和linux的不同吗?就这个问题我进行了一次对mkdir的探究。发现其实有很有趣。

二、PHP内核 && 系统差异 之mkdir()

2.1 Linux &&PHP 7.3.2-3

mkdir('./1?/../1',777 ,true);

mkdir('./1?/../1',777 ,false);

当第三参数为$recursive为true 时可以写目录,先说一下这个参数的含义$recursive用来循环创建目录。什么意思呢,当false时只能创建1级目录,即目录连接符最后的一个目录。而当true 时是可以创建多级目录至到最后一个目录。列如./a/b/c当abc都不存在时,会通过系统函数mkdir循环创建目录,abc都会被创建,但若为false 会因为走到a处目录不存在,则不回去创建最后一个c。

但是第一个mkdir即使为true却也没有创建1? 目录,这里我们从php内部mkdir执行情况和 系统 mkdir 执行情况来探究。

2.1.1PHP_FUNTCION(mkdir)

PHP内调过程如下图:


我们在出现分支的地方细分
/php-src/main/streams/plain_wrapper.c

2.1.1.1 $recursive =fasle

其中出现的分支的地方在判断$recursive 若是不需要循环创建则直接进入php_mkdir

/php-src/ext/standard/file.c

跟进php_mkdir_ex

首先会检查open_basedir,接着会进入VCWD_MKDIR ,VCWD_MKDIR是个宏命令,有三种不同定义:

在这里我刚开始并没有考虑太多,跟着gdb的流程走,直接执行mkdir(),会直接调用系统的_mkdir() . mkdir("./1?/../1", 01411) = - 1 ENOENT (No such file or directory)

会直接报错。在预料之类,linux系统下mkdir是不允许这样创建目录的,会效验每一层目录的有效性。回到第一次出现分叉的时候。

2.1.1.2 $recursive =true

这里会进入expand_filepath_with_mode,这里其实很熟悉,之前也是在看路径处理的时候看到过这个函数,它是一个展开函数,会通过递归的方式展开需要被创建的目录。在其过程会先把相对目录和当前脚本执行目录评价起来,若是绝对目录则忽略.
其中我们的相对目录为 ./1?/../1 会变成 /var/www/html/WordPress/wp-content/themes/4/5/6/./1?/../1
当前我所在的目录为 /var/www/html/WordPress/wp-content/themes/4/5/6
然后通过递归的方式 去掉 ../, ./ ,//.并且对应目录前移,会变成 /var/www/html/WordPress/wp-content/themes/4/5/6/1
然后在传递给系统的mkdir函数。

在这个函数里面存在win32 和linux的不同分支,但在具体处理之前win32判断了目录名不能存在 *,?

注意一下此处!

附上strace ,也是验证上诉分析过程

1. mkdir("/var/www/html/WordPress/wp-content/themes/4/5/6/1", 01411) = 0

2.1.2 Mkdir In Linux

在linux中单纯的mkdir是会层层验证目录,而后在创建一级目录。mkdir 也可以带参 -p,代表系统层面循环的创建目录。
当执行mkdir -p 时 :

strace -f -e trace =mkdir mkdir -p ./1 ?/../1

mkdir("1?", 0777 ) = 0

mkdir("1", 0777 ) = 0

我们能看到它并不像php内部那样,展开而后处理 。它会层层按照输入的目录创建。

2.2 window&& PHP 7.0.12

这里是我为什么要探究的一个重要问题点所在,在前面我提到的那篇文章中作者在window下实验当$recursive为false 才能创建成功,正好是反着的。作者的解释的false的时候不会去层层判断,但是真的是这样吗?

而后我也做了一个验证性的实验,在window 上用 php 5.6做了这个测试,但是结果让我疑惑了,无论在false还是 true 的情况都不会创建目录.而且报错也很有意思,在false的情况下报错 no error 但是就是无法创建。在true的情况下报错 invaild path。

难道是php-cli 问题?我又用cgi测了一遍,发现同样是这样。有意思,而后我通过邮件联系了那篇文章作者,询问其版本号。很快,得到了他的答复,php-7.0.12。

于是下载php-7.0.12源码 重新编译加debug,此处省略1000字...
在编译完成后我迫不及待的试了一下,同样如此和我的php5.6 一摸一样,无论在cli 模式 或者 cgi 模式下都是无法复现作者文中的情况。这到底问题出在哪呢?

先调了再说,VS调试php 网上基本上没有详细的介绍,有的都是Vscode。我不知道如何启动并调试,只好想了个attach的办法。在mkdir前面写上sleep(10) ,但是这样做,其实是有一点鸡肋的,php内核初始化过程你其实抓不到的,但是用在这里够了,还是在php_plain_files_mkdir这个地方下断,刷新页面,attach到启动的php-cgi 上。

2.2.1PHP_FUNCTION(mkdir)

2.2.1.1 $recursive== false

还是先分析false的情况,前面都一样,不同的是在php_mkdir_ex 中VCWD_MKDIR调用的函数不一样

这次走到不一样的调用上

跟进virtual_mkdir

同样调用了virtual_file_ex(),前面有一点没提到,在expand展开路径的过程中最后其实也是进入的这个函数,前面说过在处理的过程中若是win32的情况会判断路径存不存在 * , ?.若是存在则会直接返回1,不会进入后面写路径。为什么那篇文章的作者会在false的情况下写成功呢?

2.2.1.1 $recursive== true

这里前面说过这里会进行expand过程,但是同样会判断路径名中存不存在*, ? ,会报错 Invaild Path。

2.2.2 mkdir inwindow

这里因为没有都没有执行到写目录。此处我们还无法探究window系统mkdir 函数是如何执行的。

三、程安全与非线程安全

重新梳理一下,现在是三种不一样的情况:
linux /true 可写
window/7.0.12 :

1. false 可写
2. true/false 都不可写

window 出现了两种情况。仔细在走一遍window/false的情况,现在我唯一没有考虑到是VCWD_MKDIR 选择情况。前面都是跟着调试流程走的,这是唯一可能出现分叉的地方,重新看一下它的两种种宏定义:

若非那片文章作者,是走的第二个define,于是我把第一个define先注释掉了,换上了第二个define,再重新编译一边,结果竟然出现了和那篇作者一样的情况。但是这里有一个小小不同,写入的目录是相对于php-cgi.exe解释器的,不是相对于WWW的网站根目录下的,当你看了下面的分析以后,应该会给你一个答案,那么很显然问题现在出现在 VIRTUAL_DIR 定义的情况,在它没有定义的情况下,才会走到第二个define,我看看VIRTUAL_DIR

是在哪被定义的

/php-src/Zend/zend_virtual_cwd.h

熟悉php内核的朋友不会陌生ZTS,这是php 线程安全的标志。用来应对那些使用线程来处理并发请求的Web服务器,列如window下的IIS,worker_mpm模式下的apahce,生活在线程里面的php需要考虑线程间的读写同时也要保证线程间是安全,所以php需要自己提供ZTS层来管理线程间的操作。当定义了ZTS时候,就也同时定义了虚拟目录(VIRTUAL_DIR)。为什么会存在虚拟目录这一说法呢,其实很简单你通过对应virtual_file_ex() 可以看出来,这个函数的目的在于针对相对路径替换出完整的绝对路径。举很简单的例子,php脚本中写的相对路径,其相对路径一定是针对于该脚本的。在执行脚本的过程中,会进入相应的php 内核里面的php_execute_script(),其中有一步是VCWD_CHDIR_FILE(filename) ,这是用来根据要执行的脚本位置去切换当前目录,同样这个宏定义有两个不同的函数,一个是在虚拟目录下切换目录,一个是非线程安全环境下单线程切换目录,不同是在线程安全下切换目录,并不是直接调用系统的_chdir(), 而是将执行脚本的目录存储在TSRMG中,并给定一个cwd_globals_id,要用的时候再去取,比如创建目录,写文件。因为在多线程环境不能直接修改当前进程的目录,只能预定义一个变量保存各线程的当前目录。 可以看到在线程安全的模式下,若是给的相对路径,都会出现当前目录和相对目录的拼接。且都在win32的环境都会检测目录是否包含* ,? .

四、结论汇总

我有注意到那篇的文章作者是在window 上用的phpstudy,我也去看了一下phpstudy的是否有7.0.12的版本,存在一个 php-7.0.12-nts+Apache 确实也是非线程安全。也印证上面我修改php7.0.12 重新编译的结果,但是一个很有趣的东西是,window的系统调用API _mkdir() 是存在和php内部一样的路径展开功能,即他是允许这样写的./1?/../1 可以在当前目录下写入文件夹1的,这和linux不一样,linux的系统函数是逐层判断。在php7.1之后,改变了系统创建目录的API,从_mkdir 变成了CreateDirectoryW,但是不变的是还是可以存在路径展开的功能。即便你这样写:@@#@$@#$^%$&&**/@!#@!$!%/../../evil 也是可以创建目录evil的,可以算是一个小技巧。但是条件是在window下php为非线程安全模式和PHP_FUNCTION(mkdir) 第三个参数为false的情况下是可以这样写目录的。可以算是一个小tips吧。结合相应的应用特点,是可以用到的,而且php版本一般都是非线程安全的,在nginx下都是多进程处理php,即非线程安全。apache只有在worker_mpm才是多线程的,一般也不常用。一般都是prefork_mpm + php_mod,即便是fastcgi也是多进程。利用环境还是比较常见的。

以上就是PHP中Mkdir函数如何使用,小编相信有部分知识点可能是我们日常工作会见到或用到的。希望你能通过这篇文章学到更多知识。更多详情敬请关注行业资讯频道。

目录 情况 线程 系统 函数 路径 过程 安全 不同 作者 是在 处理 文件 时候 脚本 问题 模式 篇文章 若是 还是 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 互联网科技图片 苏州互联网科技贷款公司 睿民互联网科技 苏州大垚互联网科技 衡水科技产业互联网平台 武汉健翔互联网络科技有限公司 崇明区互联网教育科技诚信经营 中国航天科技大学国际工业互联网 科技类公司包括互联网吗 乌鲁木齐新华智原互联网科技学校 伊宁互联网科技做什么的 华中科技大学互联网的牛人 高科技互联网套路 江苏互联网智能科技软件 厦门江牛科技互联网 宇信科技在杭州互联网排第几 广州燕处互联网科技有限公司 亚光科技低轨卫星互联网 长沙易易互联网科技有限公司 优雇互联网科技有限公司 互联网科技工作委员会主任 疫情对互联网科技公司影响 互联网科技集团 西安互联网科技 互联网还是高科技吗 四川慕妮娅互联网科技有限公司 黑云互联网科技有限公司 云南省自由互联网科技有限公司 易租星互联网科技有限公司 深圳互联网科技园地址
0