php中include_once和require_once性能分析

 更新时间:2016年11月25日 15:02  点击:1525
本文章来给各位朋友介绍php中include_once和require_once性能分析,有需要了解的朋友不防参考参考。


我们知道, PHP去判断一个文件是否被加载, 是需要得到这个文件的opened_path的, 意思是说, 比如:

 代码如下 复制代码

 <?php

    set_include_path("/tmp/:/tmp2/");

    include_once("2.php");

    ?>

当PHP看到include_once “2.php”的时候, 他并不知道这个文件的实际路径是什么, 也就无法从已加载的文件列表去判断是否已经加载, 所以在include_once的实现中, 会首先尝试解析这个文件的真实路径(对于普通文件这个解析仅仅类似是检查getcwd和文件路径, 所以如果是相对路径, 一般是不会成功), 如果解析成功, 则查找EG(include_files), 如果存在则说明包含过了, 返回, 否则open这个文件, 从而得到这个文件的opened_path. 比如上面的例子, 这个文件存在于 “/tmp2/2.php”.

 

然后, 得到了这个opened_path以后, PHP去已加载的文件列表去查找, 是否已经包含, 如果没有包含, 那么就直接compile, 不再需要open file了.


 

1. 尝试解析文件的绝对路径, 如果能解析成功, 则检查EG(included_files), 存在则返回, 不存在继续

 

2. 打开文件, 得到文件的打开路径(opened path)

 

3. 拿opened path去EG(included_files)查找, 是否存在, 如果存在则返回, 不存在继续
 

4. 编译文件(compile_file)

 
 

这个在大多数情况下, 不是问题, 然而问题出在当你使用APC的时候…

 
 

在使用APC的时候, APC劫持了compile_file这个编译文件的指针, 从而直接从cache中得到编译结果, 避免了对实际文件的open, 避免了对open的system call.

 

然而, 当你在代码中使用include_once的时候, 在compile_file之前, PHP已经尝试去open file了, 然后才进入被APC劫持的compile file中, 这样一来, 就会产生一次额外的open操作. 而APC正是为了解决这个问题, 引入了include_once_override, 在include_once_override开启的情况下, APC会劫持PHP的ZEND_INCLUDE_OR_EVAL opcode handler, 通过stat来确定文件的绝对路径, 然后如果发现没有被加载, 就改写opcode为include, 做一个tricky解决方案.


 

但是, 很可惜, 如我所说, APC的include_once_override实现的一直不好, 会有一些未定义的问题, 比如:

 

 代码如下 复制代码

    <?php

    set_include_path("/tmp");

    function a($arg = array()) {

        include_once("b.php");

    }

    a();

    a();

    ?>

然后, 我们的b.php放置在”/tmp/b.php”, 内容如下:

 

 代码如下 复制代码

    <?php

      class B {}

    ?>

那么在打开apc.include_once_override的情况下, 连续访问就会得到如下错误:

 
Fatal error – include() : Cannot redeclare class

 

排除这些技术因素, 我也一直认为, 我们应该使用include, 而不是include_once, 因为我们完全能做到自己规划, 一个文件只被加载一次. 还可以借助自动加载, 来做到这一点.

 

你使用include_once,只能证明, 你对自己的代码没信心.

 

所以, 建议大家, 不要再使用include_once,不过我建义大家使用autoload和spl_autoload自动加载

(1) autoload机制概述

在使用PHP的OO模式开发系统时,通常大家习惯上将每个类的实现都存放在一个单独的文件里,这样会很容易实现对类进行复用,同时将来维护时也很便利。这 也是OO设计的基本思想之一。在PHP5之前,如果需要使用一个类,只需要直接使用include/require将其包含进来即可。下面是一个实际的例 子:

 代码如下 复制代码

/* Person.class.php */
<?php
class Person {
var $name, $age;

function __construct ($name, $age)
{
$this->name = $name;
$this->age = $age;
}
}
?>

/* no_autoload.php */
<?php
require_once (”Person.class.php”);

$person = new Person(”Altair”, 6);
var_dump ($person);
?>

在这个例子中,no-autoload.php文件需要使用Person类,它使用了require_once将其包含,然后就可以直接使用Person类来实例化一个对象。

但 随着项目规模的不断扩大,使用这种方式会带来一些隐含的问题:如果一个PHP文件需要使用很多其它类,那么就需要很多的require/include语 句,这样有可能会造成遗漏或者包含进不必要的类文件。如果大量的文件都需要使用其它的类,那么要保证每个文件都包含正确的类文件肯定是一个噩梦。

PHP5为这个问题提供了一个解决方案,这就是类的自动装载(autoload)机制。autoload机制可以使得PHP程序有可能在使用类时才自动包含类文件,而不是一开始就将所有的类文件include进来,这种机制也称为lazy loading。

下面是使用autoload机制加载Person类的例子:

 代码如下 复制代码

/* autoload.php */
<?php
function __autoload($classname) {
require_once ($classname . “class.php”);
}

$person = new Person(”Altair”, 6);
var_dump ($person);
?>

大家知道,数组排序最常用的函数就是 sort($arr); 它的作用是按数组的键值升序排列,并且排序后的数组键名不再是原来的键名,是按新数组来重新设置的键名。

而有时我们要求更复杂的排序。如按键名排序,这里用到 ksort($arr); 函数,它会根据数组的键名排序,并维持原有的键值关系。相对应的 asort($arr); 函数,是按键值排序,且维持原有的键值关系。

同样原理,rsort(); arsort(); krsort(); 函数除了排序是按降序排列外,其它与sort(); rsort(); ksort(); 相同。
数组操作是PHP很重要的基础,希望能好好运用。

一维数组


例子

 代码如下 复制代码

<?php
function my_sort($a, $b)
  {
  if ($a == $b) return 0;
  return ($a > $b) ? -1 : 1;
  }

$people = array("Swanson" => "Joe",
"Griffin" => "Peter", "Quagmire" => "Glenn",
"swanson" => "joe", "griffin" => "peter",
"quagmire" => "glenn");

uksort($people, "my_sort");

print_r ($people);
?>输出:

Array
(
[swanson] => joe
[quagmire] => glenn
[griffin] => peter
[Swanson] => Joe
[Quagmire] => Glenn
[Griffin] => Peter
)

如下面这个二维数组,需要按照sort键名来排序,那么array_multisort() 就无法直接实现了:

 代码如下 复制代码

$data[5] = array('volume' => 67, 'edition' => 2);
$data[4] = array('volume' => 86, 'edition' => 1);
$data[2] = array('volume' => 85, 'edition' => 6);
$data[3] = array('volume' => 98, 'edition' => 2);
$data[1] = array('volume' => 86, 'edition' => 6);
$data[6] = array('volume' => 67, 'edition' => 7);
// 准备要排序的数组
foreach ($data as $k => $v) {
    $edition[] = $v['edition'];
}
array_multisort($edition, SORT_ASC, $data);
print_r($data);将输出:

Array
(
    [0] => Array
        (
            [volume] => 86
            [edition] => 1
        )

    [1] => Array
        (
            [volume] => 67
            [edition] => 2
        )

    [2] => Array
        (
            [volume] => 98
            [edition] => 2
        )

    [3] => Array
        (
            [volume] => 85
            [edition] => 6
        )

    [4] => Array
        (
            [volume] => 86
            [edition] => 6
        )

    [5] => Array
        (
            [volume] => 67
            [edition] => 7
        )

)


•sort() 函数用于对数组单元从低到高进行排序。
•rsort() 函数用于对数组单元从高到低进行排序。
•asort() 函数用于对数组单元从低到高进行排序并保持索引关系。
•arsort() 函数用于对数组单元从高到低进行排序并保持索引关系。
•ksort() 函数用于对数组单元按照键名从低到高进行排序。
•krsort() 函数用于对数组单元按照键名从高到低进行排序。

在php中访问mysql我们需要利用php的各种函数来实现的,最常用的函数是mysql_connect()函数了,用它就可以连接数据库,其它的我们后面再廛。

mysql_connect()函数

定义和用法

mysql_connect() 函数打开非持久的 MySQL 连接。

语法

mysql_connect(server,user,pwd,newlink,clientflag)

例子

 代码如下 复制代码

<?php
$con = mysql_connect("localhost","mysql_user","mysql_pwd");
if (!$con)
  {
  die('Could not connect: ' . mysql_error());
  }

// 一些代码...

mysql_close($con);
?>

下面我们来看个连接数据库实例

先建一个名为test的数据库(使用phpadmin)见下图:

然后,在该表内建一个名为 user的表,

准备工作完成,正式开始:)

 代码如下 复制代码

<?php
//connect.php
$db_server="localhost";// 数据库服务器名称
$db_user="root"; // 连接数据库用户名
$db_pwd="leaf";//连接数据库密码
$db_name="test";//数据库的名字
$db=mysql_connect($db_server,$db_user,$db_pwd,$db_name);
/*
面向对像
$db=new mysql($db_server,$db_user,$db_pwd,$db_name);
*/
 
if(!$db)echo "fail";
else echo "connect success"
?>


如果PHP是4.0以后版本,可以使用mysqli库,相应的代码这样写:

 代码如下 复制代码

<?php ......
$db=mysqli_connect($db_server,$db_user,$db_pwd,$db_name);
/*
面向对象
$db=new mysqli($db_server,$db_user,$db_pwd,$db_name);
*/
if(mysqli_connect_errno()){
  echo "Error: Could not connect to database. Please try again laer.";
  exit;
}
else echo "Success!";
?>

说明:使用了函数库mysqli,所以需要将php.ini文件中的extension=php.mysqli 打开

通常,我们将代码前面的几行单独放在一配制文件中,这里取名为 db_config.php

 代码如下 复制代码

<?php
//db_config.php
$db_server="localhost";// 数据库服务器名称
$db_user="root"; // 连接数据库用户名
$db_pwd="leaf";//连接数据库密码
$db_name="test";//数据库的名字
?>

如此一来,最初的那个连接测试代码就成了这样的:

 代码如下 复制代码

<?php
// connect.php
require_once(“db_config.php”);//包含配置文件
$db=mysql_connect($db_server,$db_user,$db_pwd,$db_name);
//同样也可以用面向对像的语法
if(!$db)echo "fail";
else echo "connect success"
?>

以前有讲过关于php mvc的各种用法,本文章主要介绍PHP MVC框架之错误捕捉用法说明,各位同学可参考。

前三篇文章已经把MVC的结构原理都简单的进行了说明和演示。。。当然,不是说一个框架就只有那点东西,一个良好的框架所要求的功能还是要很全面的,例如友好的错误输出,安全处理,防止跨域攻击等

好了,我们一步一步来。这节要说的是错误捕捉,其实这个不难,只要理解2个方法就可以做好错误捕捉的程序。

 代码如下 复制代码


register_shutdown_function(‘functionname’);  
set_error_handler(‘functionname’); 

第一个方法,是注册一个shutdown的回调函数,当php脚本关闭时调用,当然这个关闭可以是正常的代码执行完毕,也可以是程序抛出致命错误关闭的脚本,那我怎么区分呢?别急,php还有一个方法叫error_get_last() 就像函数名说的那样,获取php最后一个错误信息,这样就可以判断是代码正常执行完毕还是遇到fatal error。。。

第二个方法,是注册一个error的回调函数,当脚本执行遇到错误时调用。

这2个方法最大的区别是,第一个是脚本关闭调用,也就是1次访问,只会调用一次(因为发生致命错误脚本就退出执行了),二第二个方法的触发调用是发生错误的时候,例如访问数组中不存在的index,使用未定义的变量,就是这些warning,notice错误的捕捉。。。如果你想更了解二者的区别,建议访问php官网查看文档说明。。。

这里我就不贴代码了,有兴趣的同学,可以去github那里下回来慢慢看。。。

 

 代码如下 复制代码

<?php

# 定义application路径

define('APPPATH', trim(__DIR__,'/'));

# 获得请求地址

$root = $_SERVER['SCRIPT_NAME'];

$request = $_SERVER['REQUEST_URI'];

$URI = array();

# 获得index.php 后面的地址

$url = trim(str_replace($root, '', $request), '/');


# 如果为空,则是访问根地址

if (empty($url))

{

    # 默认控制器和默认方法

    $class = 'Index';

    $func = 'welcome';

}

else

{

    $URI = explode('/', $url);

 


    # 如果function为空 则默认访问index

    if (count($URI) < 2)

    {

        $class = ucfirst($URI[0]);

        $func = 'index';

    }

    else

    {

        $class = ucfirst($URI[0]);

        $func = $URI[1];

    }

}

 

# 把class加载进来

include(APPPATH . '/' . 'application/controllers/' . $class . '.php');

 


#实例化

$obj = new $class;

 


call_user_func_array(

    # 调用内部function

    array($obj,$func),

    # 传递参数

    array_slice($URI, 2)

);

 

 

我们来看看最终的效果。。。我们来试试访问不存在的controller里的方法试试

先看看没有错误捕捉的时候:

 

再来对比下做了错误捕捉的时候

文章主要讲到了关于PHP MVC框架路由使用方法,有需要了解的同学可进入参考。


说到PHP开发web,自然离不开开发框架,开发框架为我们提供了灵活的开发方式,MVC层分离,业务解耦等。。。

第一篇先来简单点的,说说MVC框架的路由功能。。。

一般的单入口框架路由都是这样的结构:

domain/index.php/classname/functionname/var1/var2

这里的index.php 就被称为入口文件。。。对于服务器而言,你这里访问的就只有index.php 后面调用的controller 和里面的方法,甚至传值都是在框架内部基于PHP层面实现的。

Talk is cheap, show you the code !!


首先,先建立好下面的文件结构

 

我们来动手试试,怎么才能访问到controllers里面的文件。。。

在index.php里面输入以下内容

print_r($_SERVER);

然后访问 以下地址试试。

yourdomain/index.php/class/function/var1

这里作者我是用本地环境的,我访问的地址是localhost/MVC/index.php/class/function/var1

我贴出最重要的2个变量

[REQUEST_URI] => /MVC/index.php/class/function/var1

[SCRIPT_NAME] => /MVC/index.php

其实路由最基本的原理就在这里:

通过这2个变量来提取url地址里的class 和 function,参数等,然后把class include进来,通过PHP的回调函数  call_user_func_array 调用对应的function和传递相应的参数。

接下来上代码,读代码应该比我写的易懂。哈哈~~

index.php 的内容如下

 代码如下 复制代码

 
<?php  
 
# 定义application路径  
define(‘APPPATH’, trim(__DIR__,’/'));  
 
# 获得请求地址  
$root = $_SERVER['SCRIPT_NAME'];  
$request = $_SERVER['REQUEST_URI'];  
 
$URI = array();  
 
# 获得index.php 后面的地址  
$url = trim(str_replace($root, ”, $request), ‘/’);  
 
# 如果为空,则是访问根地址  
if (empty($url))  
{  
    # 默认控制器和默认方法  
    $class = ‘index’;  
    $func = ‘welcome’;  
}  
else 
{  
    $URI = explode(‘/’, $url);  
 
    # 如果function为空 则默认访问index  
    if (count($URI) < 2)  
    {  
        $class = $URI[0];  
        $func = ‘index’;  
    }  
    else 
    {  
        $class = $URI[0];  
        $func = $URI[1];  
    }  
}  
 
 
# 把class加载进来  
include(APPPATH . ‘/’ . ‘application/controllers/’ . $class . ‘.php’);  
 
#实例化  
$obj = new ucfirst($class);  
 
call_user_func_array(  
    # 调用内部function  
    array($obj,$func),   
    # 传递参数  
    array_slice($URI, 2)  
);  
 

在application/controllers 里面添加下面2个文件

index.php   用于作为默认控制器

 

 代码如下 复制代码


<?php  
 
class Index  
{  
 
    function welcome()  
    {  
        echo ‘I am default controller’;  
    }  
 
}  
 
 
?> 
 

hello.php

 
<?php  
class Hello  
{  
    public function index()  
    {  
        echo ‘hello world’;  
    }  
 
    public function name($name)  
    {  
        echo ‘hello ‘ . $name;  
    }  
}  
 
?> 
 

测试一下看看,能不能访问了。根据上面的路由结构。我们来试试

 

这个访问正常,正确调用了hello这个class内部的name方法,然后把参数barbery传递过去了。。。

再试试不输入function name,看看能不能默认调用index。。

 

答案也是可以的。。。

最后一个,访问root地址看看

 

也正确的映射到了默认控制器上。。。

ok,一个简单的MVC路由功能就完成了。。。

[!--infotagslink--]

相关文章

  • 带你了解PHP7 性能翻倍的关键

    20岁老牌网页程序语言PHP,最快将在10月底释出PHP 7新版,这是十年来的首次大改版,最大特色是在性能上的大突破,能比前一版PHP 5快上一倍,PHP之父Rasmus Lerdorf表示,甚至能比HHVM虚拟机下的PHP程序性能更快。HHVM 是脸书为自...2015-11-24
  • 利用 Chrome Dev Tools 进行页面性能分析的步骤说明(前端性能优化)

    这篇文章主要介绍了利用 Chrome Dev Tools 进行页面性能分析的步骤说明(前端性能优化),本文给大家介绍的非常想详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下...2021-02-24
  • JavaScript提高网站性能优化的建议(二)

    这篇文章主要介绍了JavaScript提高网站性能优化的建议(二)的相关资料,需要的朋友可以参考下...2016-07-29
  • VS Code C/C++环境配置教程(无法打开源文件“xxxxxx.h”或者检测到 #include 错误,请更新includePath)(POSIX API)

    这篇文章主要介绍了VS Code C/C++环境配置教程(无法打开源文件“xxxxxx.h” 或者 检测到 #include 错误。请更新includePath) (POSIX API),本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下...2020-08-13
  • 提升jQuery的性能需要做好七件事

    这篇文章主要介绍了提升jQuery的性能需要做好的七件事,希望真的帮助大家提升jQuery性能,需要的朋友可以参考下...2016-01-14
  • require、backbone等重构手机图片查看器

    这篇文章主要为大家详细介绍了require、backbone等重构手机图片查看器的相关资料,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2016-11-22
  • 基于mybatis中<include>标签的作用说明

    这篇文章主要介绍了基于mybatis中<include>标签的作用说明,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2021-02-20
  • php测试性能代码

    php测试性能代码 function microtime_float () { list ($usec, $sec) = explode(" ", microtime()); return ((float) $usec + (float) $sec); } functio...2016-11-25
  • css中空路径对页面性能影响的解决方案

    文章介绍了css中空路径对页面性能影响的解决方案,这个可能很多美工朋友不会去注意这一点,下面我们来看看吧。 在写 CSS 的时候,用 background:url(#) 还是会对页面进...2017-07-06
  • 如何用Node.js编写内存效率高的应用程序

    这篇文章主要介绍了如何用Node.js编写内存效率高的应用程序,对Node.js感兴趣的同学,可以参考下...2021-05-01
  • Redis 执行性能测试

    这篇文章主要介绍了Redis 执行性能测试的方法,文中讲解非常细致,帮助大家更好的理解和学习redis,感兴趣的朋友可以了解下...2021-01-15
  • include包含头文件的语句中,双引号和尖括号的区别(详解)

    下面小编就为大家带来一篇include包含头文件的语句中,双引号和尖括号的区别(详解)。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧...2020-04-25
  • 浅析Mysql Join语法以及性能优化

    一.Join语法概述join 用于多表中字段之间的联系,语法如下:复制代码 代码如下:... FROM table1 INNER|LEFT|RIGHT JOIN table2 ON conditionatable1:左表;table2:右表。JOIN 按照功能大致分为如下三类:INNER JOIN(内连接,或...2014-05-31
  • Lua中的loadfile、dofile、require详解

    这篇文章主要介绍了Lua中的loadfile、dofile、require详解,本文分别用实例讲解它的用法和特点等内容,需要的朋友可以参考下...2020-06-30
  • C#导出数据到excel如何提升性能

    这篇文章主要介绍了C#导出数据到excel如何提升性能,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下...2020-07-13
  • C#中Dynamic和Dictionary性能比较

    开发中需要传递变参,考虑使用 dynamic 还是 Dictionary,dynamic 的编码体验显著优于 Dictionary,如果性能差距不大的话,我会选择使用dynamic。下面通过本文给大家详细介绍下C#中Dynamic和Dictionary性能比较,一起看看吧...2020-06-25
  • Nginx + php 搭建 超性能 WEB 服务器

    Nginx ("engine x") 是一个高性能的 HTTP 和反向代理服务器,也是一个 IMAPPOP3SMTP 代理服务器。...2016-01-27
  • Golang标准库和外部库的性能比较

    这篇文章主要介绍Golang标准库和外部库的性能比较,下面文章讲围绕这两点展开内容,感兴趣的小伙伴可以参考一下...2021-10-19
  • Java并发编程如何降低锁粒度并实现性能优化

    这篇文章主要介绍了Java并发编程如何降低锁粒度并实现性能优化,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下...2020-08-29
  • 不可忽视的 .NET 应用5大性能问题

    ASP.NET 或是 Windows Forms 容器中,使用 ADO 库与运行在 CLR 交互,而 CLR 运行在操作系统中而该硬件又与其他包含不同技术堆栈的硬件通过网络相连。在你的应用与外部环境之间,。我们还有 API 管理服务以及多级缓存基础构造数量庞杂,都可能影响应用程序的性能!...2021-09-22