php ini_set( include_path)设置容易导致require_once包含文件产生冲突想到的

jackxiang 2008-9-3 10:55 | |
requireOnce性能耗费很大:
andot<***@qq.com> 16/8/9 星期二 20:43:21
刚才测试了一下hprose的http服务性能,发现基于swoole的hprose http服务比php内置web server的hprose http 服务快20倍。主要性能差别就是 require_once 这样的 php 加载。swoole 服务启动就加载一次,普通的web server,每个请求都要进行一次加载,而这个加载要读磁盘文件,所以就有了明显的性能差距。
ღ梦  无悔⊰<***@qq.com> 16/8/9 星期二 20:44:35

Corz(***) 16/8/9 星期二 20:44:49
区别就是一个只跑一个函数  一个从头路到尾
andot<***@qq.com> 16/8/9 星期二 20:44:49
代码再优化,也赶不上一次 require_once 对性能的影响大。


      一般情况下,我们在开发程序的时候,为了方便包含经常使用的库文件或者共同用的文件,我们就会去设置php的include_path,我们一般都会通过修改php.ini来实现,但是有时候,我们没有服务器的权限。同时在有些特别的时候,我们把一个目录加到include_path会让已有的程序冲突。在我们参考受cakephp的时候受到了启发:发现在app/webroot目录下index.php有如下代码:
       ini_set('include_path', CAKE_CORE_INCLUDE_PATH . PATH_SEPARATOR . ROOT . DS . APP_DIR . DS . PATH_SEPARATOR . ini_get('include_path'));
     我们看到这个程序动态修改include_path。不过cake在这儿是把 CAKE_CORE_INCLUDE_PATH 和 APP_DIR 加到 include_path里,并且优先在这两个目录下找包含程序。
注意到它这里用到了PATH_SEPARATOR这个变量。这样这段代码在windows和linux下能通用。
     为此我们从中受到启发,我们可以根据自己的需要把一些include目录动态的加入进来。比如说我们有很多libs:lib1,lib2,lib3等等。我们不必把这些libs都加到include_path里,因为它们之间可能冲突(注意它怎么包含目录的)。
可以建立一个inc_dir,并把这个目录加入到include_path。在inc_dir下,分别建立inc_path1.php inc_path2.php inc_path3.php
分别写入



在写程序的时候,比如要用lib2的functions.php
就可以这么写
<?php
require 'inc_path2.php';
require 'functions.php';
//....


我们遇到的问题描述:
        我们在把starcxc复制到starfxg下面目录后,保持为libs下的frontapp.class.php包含的绝对路径如下:
  
        include_once("/data1/www/htdocs/app.space.sina.com.cn/starcxc/stdafx.php");    

    如果我们要在这次对星爷痴话和冯人冯语的移植过程中要是没有发现如下错误是不会注意这个文件包含的小问题的:页面提示出现两次包含了mysqlpdo.class.php中的MysqlPdo类而刷新浏览器后就不纯在错误提示直接显示白页了,同时在Apache错误日志中可以看到出现的错误日志:[Tue Sep 02 20:36:08 2008] [notice] child pid 10757 exit signal Segmentation fault (11),我们就很难发现上面老是谈到冲突这件事情,我们看看代码:
stdafx.php:

<?php
define("SERVER_ROOT",dirname(__FILE__));

function init_www()
{
  if(PHP_OS=='WINNT') {
    $xg  = "\\";
    $fg  = ";";
  }else{
    $xg  = "/";
    $fg  = ":";
  }
  $need_include = array(

    SERVER_ROOT . "{$xg}admin{$xg}operation{$xg}",
    SERVER_ROOT ,
    SERVER_ROOT . "{$xg}libs{$xg}",
    dirname(SERVER_ROOT) . "{$xg}libs{$xg}",
  );
  $include_path = explode($fg,ini_get('include_path'));
  foreach($need_include as $path)
  {
    if(!in_array($path,$include_path))
    {
      $include_path[] = $path;
    }
  }
  ini_set('include_path',join($fg,$include_path));
}
init_www();

include_once('config/commstr.php');
include_once('config/db_config.php');
include_once('clsfactory.class.php');
include_once('page.class.php');
include_once('mysqlpdo.class.php');
include_once('user.class.php');
include_once('Json.php');
include_once('interface.class.php');
include_once('mysmarty.class.php');
include_once('adminsmarty.class.php');

?>



我们在:ini_set('include_path',join($fg,$include_path)); 后pirnt_r($include_path);发现打印出如下数据:

Array
(
    [0] => .
    [1] => /usr/local/sinasrv2/lib/php
    [2] => /data1/www/htdocs/app.space.sina.com.cn/starcxc/admin/operation/
    [3] => /data1/www/htdocs/app.space.sina.com.cn/starcxc
    [4] => /data1/www/htdocs/app.space.sina.com.cn/starcxc/libs/
    [5] => /data1/www/htdocs/app.space.sina.com.cn/libs/
)



以下这三行:

    SERVER_ROOT . "{$xg}admin{$xg}operation{$xg}",
    SERVER_ROOT ,
    SERVER_ROOT . "{$xg}libs{$xg}",
    dirname(SERVER_ROOT) . "{$xg}libs{$xg}",



我们发现包含的文件include_path中出现目录的多次包含:例如:starcxc目录和starcxc/libs都出现在了include_path中,在这里没有出现冲突问题是由于在starcxc目录下和starcxc/libs目录下没有出现相同文件名,虽然也并不可能出现相同文件名,但是我们在将前台移植到starfxg的时候在libs下面的php文件:frontapp.class.php中有如下绝对地址的代码,如下:
include_once("/data1/www/htdocs/app.space.sina.com.cn/starcxc/stdafx.php");

这个时候就体现出什么叫做冲突了【当然改成相对路径一切问题都会得到解决】,同样一套代码尽管移到了starfxg,由于包含的是starcxc下的stdafx.php其在第一行有:

define("SERVER_ROOT",dirname(__FILE__));
肯定会得出包含文件的路径也是和上边一样的:
(
    [0] => .
    [1] => /usr/local/sinasrv2/lib/php
    [2] => /data1/www/htdocs/app.space.sina.com.cn/starcxc/admin/operation/
    [3] => /data1/www/htdocs/app.space.sina.com.cn/starcxc
    [4] => /data1/www/htdocs/app.space.sina.com.cn/starcxc/libs/
    [5] => /data1/www/htdocs/app.space.sina.com.cn/libs/
)





include_once('config/commstr.php');
include_once('config/db_config.php');
include_once('clsfactory.class.php');
include_once('page.class.php');
include_once('mysqlpdo.class.php');
include_once('user.class.php');
include_once('Json.php');
include_once('interface.class.php');
include_once('mysmarty.class.php');
include_once('adminsmarty.class.php');

那就出现系统会去找哪个目录的commstr.php ..等php文件呢?当然肯定会去starcxc下找了,但与此同时还会包含到路径为starfxg下的lib等吗?那就会在这里涉及到一个require_once()的问题,equire_once() 语句主要用于需要包含多个文件时,可以有效地避免把同一段代码包含进去而出现函数或变量重复定义的错误。
例如:如果创建两个文件util.inc和fool.inc,程序代码分别为:
   util.inc:

    <?php
       define(PHPVERSION,floor(phpversion()));
       echo "GLOBALS ARE NICE<br>\n";
       function goodTea()
       {
           return "Olong tea tasts good!";
       }
   ?>

    和fool.inc:

   <?php
       require ("util.inc");
       function showVar($var)
       {
           if(PHPVERSION==4)
           {
               print_r($var);
           }
           else
           {
               var_dump($var);
           }
       }
   ?>

    然后在error_require.php中包含这两个文件:

    <?php
        require("fool.inc");
        require("util.inc");//此句会产生一个错误
        $foo=array("1",array("complex","quaternion"));
        echo "this is requiring util.inc again which is also<br>\n";
        echo "required in fool.inc\n";
        echo "Running goodTea:".goodTea()."<br>\n";
        echo "Printing foo:<br>\n";
        showVar($foo);
    ?>

     当运行error_require.php时,输出结果如下:
     GLOBALS ARE NICE
     GLOBALS ARE NICE

    Fatal error:Cannot redeclare goodTea() in util.inc on line 4
    
    如果使用require_once()语句来代替 require()语句,就不会出现上面的错误。我们把error_require.php和fool.inc中的require()语句改为 require_once()语句并重命名为error_require_once.php,这是显示结果如下:
    GLOBALS ARE NICE
    this is requiring util.inc again which is also
    required in fool.inc Running goodTea:Olong tea tastes good!
    Printing foo:
    Array([0] => 1 [1] => Array ([0] => complex [1] = quaternion))
  
    include_once()语句的语法和include()语句类似,主要区别也是避免多次包含一个文件而引起函数或变量的重复定义。

    require_once语句有一个引用链,它可以保证文件加入你的程序仅仅只有一次,而且会避开变量值和函数名之间的冲突。

但实际的测试试验中我们发现这儿的require_once居然不起到避免包含两次的左右才出现导致服务器抛出MysqlPdo重复定义,再刷新出现白页,apache日志同时出现:[Tue Sep 02 20:36:08 2008] [notice] child pid 10757 exit signal Segmentation fault (11)

我们在这儿既然用到了require_once为何我们的程序任然没有避免到重复包含的问题呢???
答案初步估计是这样的:
    这就设计到require_once的问题,上面我们举例说明require_once的包含是和php.ini中include_path无关的,而我们这次用到了php.ini的include_path,有可能导致require_once没有能够起到包含一次的作用,也就是require_one出现两次包含了,如不相信,我们又做了个实验,将starfxg下的mysqlpdo.class.php 和其他几个相关的文件里面的类全部删除留其空白,最后测试首页发现一切ok了,这就验证了我们的猜想是真确的!
  
       但为何require_once没有能起到包含once的作用呢?初步估计和我们通过ini_set()来设置的php.ini 中include_path相关,这个我们有待研究研究!!!
  
  但我们如何避免类似的问题出现倒是可以探讨一下,我个人觉得如下比较直观和便于管理尽量避免出现类似问题即可:

1.在设置incluce_path的时候不要设置子目录(如:starcxg  starcxc/lib 最好别这样设置include_path)
2.在移植的时候用相对路径(include_once("/data1/www/htdocs/app.space.sina.com.cn/starcxc/stdafx.php"); 不要这样包含)
3.建议一个想通过哪个php.ini的东西包含一切,然而又加上了config等的东西,我认为最好就一个就在php_ini里面设置一个include_path的值,不要搞成一个数组来填入,就是搞成数组也不要出现重复包含,我建议就一个足够了,然后在include_once里面来和这个值做成绝对路径,如:


<?php
define("SERVER_ROOT",dirname(__FILE__));
function init_www()
{  
               ini_set('include_path', ini_get('include_path').PATH_SEPARATOR.SERVER_ROOT);
               init_www();
               include_once('config/commstr.php');
               include_once('config/db_config.php');
               include_once('libs/clsfactory.class.php');
               include_once('libs/page.class.php');
               include_once('libs/mysqlpdo.class.php');
               include_once('libs/user.class.php');
               include_once('libs/Json.php');
               include_once('libs/interface.class.php');
               include_once('libs/mysmarty.class.php');
               include_once('libs/adminsmarty.class.php');
?>



最后,关于为何require_once在设置ini_set后出现两次包含某个不同路径的文件且提示类重复,我还是没有搞明白。。。呵呵,有谁明白的为何的还请不吝赐教,谢谢!
One Road in,One Road out,One prisoner,One thousand soldiers。。。。
答案:
其根本原因是我们包含不同位置的两个文件:一个是starcxc下的mysqlpdo.class.php 另外包含的是starfxg下面的mysqlpdo.class.php,也就是说require_once包含的是不同位置的两个不同路径的文件,当然认为是可以包含的,也就进一步说明了,mysqlpdo.class.php的require_once包含是通过路径来作为其中一个判读的条件的,而类的重复则是另外一个问题,(require_once也就是先判断文件路径在判断内容的这样一个流程),require_once并没有任何的问题:)


直白点:
programme/application/index.php

include_once 'libs/mysqlpdo.class.php';

include_once 'libs/interface.class.php';
include_once 'libs/mysmarty.class.php';

在stdafx.php行:38,41,42都以包含了starcxc下的目录,但在programme/application/index.php下面又包含了starfxg下的三个文件,尽管用的是require_once但是它并不能阻挡来自不同目录的文件包含,其实在设计的时候就应该去掉不管是不是移植和绝对路径等等等,都包含了两次了还不应该去掉programme/application/index.php里面的那三个类吗。。。


最后这儿还涉及到一个环境变量的先后顺序的问题:

在programme/application/index.php下的libs/mysqlpdo.class.php 和直接include_once 'mysqlpdo.class.php'; 是不一样的:
   include_once 'libs/mysqlpdo.class.php';//包含的是starfxg下的lib/mysqlpdo.class.php ,首先到starfxg下面的libs/找mysql.class.php这个文件,找到了,也就不去环境变量中找了,和stdafx.php包含的是不一样的路径,一个是starcxc下的一个是starfxg下的mysqlpdo.class.php,require_once后由于路径不同也就当然产生了冲突!
  
   include_once 'mysqlpdo.class.php';//包含的是starcxc下的lib/mysqlpdo.class.php 同一个路径,在本文件也就是先在starfxg下的mysqlpdo.class.php的programme/application/找,找不到然后到环境变量中去找,找到了,也就是starcxc,require_once后发现路径一样类一样但由于是require_once对同一路径的文件和重复的类可以做处理当然也就显示完全正确了!








作者:jackxiang@向东博客 专注WEB应用 构架之美 --- 构架之美,在于尽态极妍 | 应用之美,在于药到病除
地址:https://jackxiang.com/post/1215/
版权所有。转载时必须以链接形式注明作者和原始出处及本声明!


最后编辑: jackxiang 编辑于2016-8-10 11:15
评论列表
发表评论

昵称

网址

电邮

打开HTML 打开UBB 打开表情 隐藏 记住我 [登入] [注册]