这是一个老问题。我们先进入一个情境,在写代码时,我们会把刚写完的函数或类归并到不同的文件中,根据功能把这些文件保存在某个目录里,再使用include()/include_once()或require()/require_once()包含它们来执行,以提高代码的重用性和简洁性。
这4个函数在产品代码里可以交替使用,但在性能上有着一些细微差别,特别是对性能要求高的项目,需要花点儿心思来分析。
include()(中文意为包含)和require()(中文意为必须)允许在当前脚本中多次执行包含的文件。include_once()和require_once()确保在执行时对包含的文件只执行一次,即使在代码中调用很多次。
如果PHP虚拟机在PHP脚本中扫描到include()或include_once()语句,若包含文件失败,会显示警告错误(Warning Error),然后还会继续执行。如果是require()或require_once()语句,包含文件失败后会抛出致命的错误提示(Fatal Error)并且中止脚本的执行。
这样当文件意外丢失或者逻辑(比如重复性包含、函数重命名等)出错时,想要脚本继续执行并输出页面,我们可以使用include()或include_once()。
在开发一个严谨应用时,要使用require()或require_once()来进行包含操作,即便包含的不是PHP文件,这样有利于应用程序的安全、完整以及健壮性。
错误只会在一个文件未被包含时触发,多数是目录存取权限或路径写错这些原因引起的。在实际的运营环境里,注意千万别把程序的错误信息抛给用户,可在代码中使用error_reporting(0)禁止所有的错误显示,内部加入完善的错误与日志处理,只给用户显示正常的内容。
从性能角度考虑,由于在导入PHP脚本时进行大量的操作状态(stat)调用,使用require()要快于require_once()。比如你请求包含的文件都在/var/www/myapp/modules/myclasss.php下,则操作系统会在到达myclasss之前的每个目录进行一次stat调用,如代码清单1-11所示:
代码清单1-11 使用require_once()包含文件
<?php require_once('ClassA.php'); require_once('ClassB.php'); require_once('ClassC.php'); require_once('ClassD.php'); echo '测试require_once'; ?>
它们一共用了4个文件,均通过require_once()请求并包含。它们所请求的类均在以下代码中,它们仅声明了自己,并没有包含任何函数实现,如代码清单1-12所示:
代码清单1-12 声明要包含的类
<?php class A{ } class B{ } class C{ } class D{ }?>
以上4个空类可以帮助我们模仿一个需要在主脚本中使用外部PHP文件的PHP脚本。
我们排除了任何额外的函数调用,专注于使用require_once()函数的文件加载。把每个类分别放于一个单独的文件中,命名为ClasssA.php、ClassB.php、ClassC.php、ClassD.php,与代码清单1-10放在同一个文件夹下。
Apache为我们提供了ab(apache benchmark)工具,可以用它来测试使用require_once()的脚本性能:
ab –c 10 –n 100000 localhost/index.php
本例中我们模拟了10万个请求,同一时间有10个并发请求,结果如图1-1所示。
图1-1 使用ab测试并发请求结果
使用ab工具测试require_once(),可以看到响应时间为38.653 ms,另外结果还显示,这个脚本每秒可以支持2587.10个请求。
现在我们将require_once()改为require(),如代码清单1-13所示:
代码清单1-13 使用require()包含文件
<?php require('ClassA.php'); require('ClassB.php'); require('ClassC.php'); require('ClassD.php'); echo '测试require_once'; ?>
重新启动Web服务器,再运行刚才的ab测试命令,结果如图1-2所示。
测试结果表明,使用require()后,每秒可以支持的请求数量有所提升,从2587.10提升到2589.38。此结果还说明,代码的响应时间从上面的38.653 ms降到了38.619 ms,减少了2 ms。
如果想自动加载某个文件,可以用类似下面的autoload函数,如代码清单1-14所示:
图1-2 使用ab测试require()函数的性能
代码清单1-14 使用autoload函数自动引用
spl_autoload_register('autoload'); function _autoload($name) { require('include/' . $name . '.php'); }
我们用spl_autoload_register告诉PHP在执行时自动加载某函数,在这里我们告诉它调用autoload函数,autoload函数根据需要的文件,使用require来包括相关的文件。
可以确定的是,require_once要慢于require,使用autoload速度最快。另外,在代码中函数调用越少,它的运行速度就越快。