Php路径问题及其解决方法

初学PHP的时候,我们经常被PHP的绝对路径和相对路径弄的晕头转向。路径问题经常导致include及require命令不能加载到指定的页面,从而导致Web程序运行错误。本文将跟大家一起探讨一下PHP路径问题的常用解决方案。
下面我们先做一个个简单的示例:

示例1图片

上图是一个简单Web项目的结构图,其中各文件的代码如下:

root/index.php :

<?php
require_once ‘app/blog.php’;

$blog=new Blog();
echo $blog->GetBlogInfo();
?>

root/app/blog.php :

<?php
require_once ‘../lib/smarty/functions.php’;
?>
<?php
class Blog
{
   public function GetBlogInfo()
   {
     return ‘This is a test of blog!’;
   }
}
?>
<?php
echo ‘root/app/blog.php loaded successed!</br>’;
?>

首先,我们从浏览器里直接输入http://localhost/root/app/blog.php,浏览器显示:

———————————————————————-

root/app/blog.php loaded successed!

———————————————————————-

文件加载成功,没发生任何警告或错误。

接下来,我们在浏览器中请求如下网站:http://localhost/root/index.php,浏览器显示出如下错误信息:

———————————————————————————
Warning: require_once(../lib/smarty/functions.php) [function.require-once]: failed to open stream: No such file or directory in T:\Study\PHP_REL\Projects\root\app\blog.php on line 2
Fatal error: require_once() [function.require]: Failed opening required ‘../lib/smarty/functions.php’ (include_path=’.;C:\php5\pear’) in T:\Study\PHP_REL\Projects\root\app\blog.php on line 2
———————————————————————————

为什么’../lib/smarty/functions.php’没能加载? 我们访问http://localhost/root/app/blog.php没有出现任何异常,这说明出现错误的原因很可能在root/index.phprequire_once ‘app/blog.php’语句。从Google里搜索一下:php路径问题,你就能找到问题的答案。当root/index.php引用root/app/blog.php后,blog.php中的require_once语句的参照点变成了root/index.php所在的目录(根目录root)。所以,以root/index.php为参照点加载‘../lib/smarty/functions.php’时,自然就出错了。

找到了问题所在,接下来,我们就来寻找解决问题的办法:

1.将所有文件放在一个文件夹下

将所有文件放在一个文件夹下,那么就不会存在路径问题了。但是,这绝对是个馊主意!!除非你写一个小的不能再小的项目,不然请不要尝试这种方法,没有结构的系统太可怕了!

2.使用绝对路径

注:PHP中的include和require使用的是文件系统的绝对路径,如“c:\wwwroot\yourproject\index.php”

step1:刚刚发生异常是因为我们使用了相对路径,如果换成绝对路径就不会出现以上错误。让我们简单修改一下root/app/blog.php:
将前三行由

<?php
require_once ‘../lib/smarty/functions.php’;
?>

修改为:

<?php
require_once ‘T:\\Study\\PHP_REL\\Projects\\root\\lib\\smarty\\functions.php’;
?>

现在,我们访问:http://localhost/root/index.php ,浏览器显示:

—————————————-
root/app/blog.php loaded successed!
This is a test of blog!
—————————————-

程序执行成功了。

step2:虽然程序不报错了,但很明显,我们并没有真正的解决问题。没有人会在程序里写require_once ‘T:\Study\PHP_REL\Projects…functions.php’这样的东西,这样写将会使程序完全丧失灵活性,使程序难以移植!

让我们再重新回想一下出现错误的原因:1.不同层次文件之间的引用使require_once参照点发生了变化;2.参照点发生变化后,按相对路径加载文件会出错。如果我们把require_once的参照点固定,问题不就解决了吗.如何固定参照点?当然是使用绝对路径,具体实现方法:一个函数+一个常量:dirname()和__FILE__。让我们重新修改root/app/blog.php如下:
将前三行由

<?php
require_once ‘T:\\Study\\PHP_REL\\Projects\\root\\lib\\smarty\\functions.php’;
?>

修改为:

<?php
require_once dirname(__FILE__).‘/’.‘../lib/smarty/functions.php’;
?>

再次访问http://localhost/root/index.php程序运行正常,但这种解决方式很明显优于step1中的解决方式。应该说,这是一种"绝对路径+相对路径"的解决方式。

step3:上面的方法已经能够解决路径问题,但感觉代码不够优雅。让代码更优雅,我们可以这样做:在根目录(root)下新建一个settings.php:

示例2图片

root/settings.php :

<?php
if(!defined(ABSPATH))
define(‘ABSPATH’,dirname(__FILE__).‘/’);
?>

root/app/blog.php中的代码修改为:

<?php
require_once ABSPATH.‘lib/smarty/functions.php’;
?>

同时root/index.php中的代码修改为:

<?php
require_once ’settings.php’;
require_once ABSPATH.‘app/blog.php’;

$blog=new Blog();
echo $blog->GetBlogInfo();
?>

仔细考虑一下,如果直接访问http://localhost/root/app/blog.php又会出现问题:常量ABSPATH没有定义。所以,如果你的程序有直接访问http://localhost/root/app/blog.php这种情况类似的情况,那么最好直接使用dirname(__FILE__).‘/’.‘相对路径’,或者在使用ABSPATH前加一个判断(但这样有点脱裤子放X的感觉)。

:在WordPress中使用了ABSPATH与dirname(__FILE__).‘/’.‘相对路径’相结合的方法,从网站统一入口(根目录/index.php)加载的文件,使用ABSPATH的解决方法(ABSPATH在根目录/wp-config-sample.php 中定义),而那些不直接通过统一入口访问的php文件,WP使用dirname(__FILE__).‘/’.‘相对路径’的解决方案。

3.设置Apache的include_path参数

在前面的错误信息中,有一句值得我们注意:

———————————————
Fatal error: require_once() [function.require]: Failed opening required ‘../lib/smarty/functions.php’ (include_path=’.;C:\php5\pear’) in T:\Study\PHP_REL\Projects\root\app\blog.php on line 2
———————————————

Apache的include_path参数保存的是require/include的读取目录,在上面的错误信息里,include_path包含了两个位置:
1) "."表示从当前文件所在的目录中加载
2) "C:\php5\pear"表示从C盘的php5/pear目录下加载。
php函数库为我们提供了set_include_path()函数用于设置include_path参数。通过set_include_path()函数,我们可以自定义加载位置(ZendFramework中就是使用set_include_path()函数来解决路径问题)。

下面我们来演示一下如何用set_include_path()函数:

root/index.php :

<?php
set_include_path(‘./’.PATH_SEPARATOR.dirname(__FILE__));

require_once ‘app/blog.php’;

$blog = new Blog();
echo $blog->GetBlogInfo();
?>

root/app/blog.php ;

<?php
require_once ‘lib/smarty/functions.php’;
?>
<?php
class Blog
{
  public function GetBlogInfo()
  {
    return ‘This is a test of blog!’;
  }
}
?>
<?php
echo ‘root/app/blog.php loaded successed!<br/>’;
?>

测试http://localhost/root/index.php,运行正常。从root/app/blog.php可以看出,require_once的路径写法更简洁了(不需要使用ABSPATH或者dirname(__FILE__))。这种实现方式跟ABSPATH的解决方式一样,需要保证系统有统一的入口点(一般通过.htaccess文件实现)。

当然,解决php路径的方法不止以上几种,网上有很多人提供了针对php路径问题的解决方案,但多数都有一定的适用场景,不能生搬硬套。在下一篇日志中,我会对php路径问题的其他解决方法做一个简单的总结。

.htaccess文件简介

前一阵在学习ZendFramework时,在配置.htaccess文件的过程中出了点小问题。现在问题已经解决,在此把.htaccess相关的内容整理了一下:

1. .htaccess文件简介

.htaccess文件又称为”分布式配置文件”,它为我们提供了针对目录改变配置的方法。.htaccess可以做大量的事情,包括:文件夹密码保护、用户自动重定向、自定义错误页面、改变你的文件扩展名、封禁特定IP地址的用户、只允许特定IP地址的用户、禁止目录列表,以及使用其他文件作为index文件。

以下是Apache指南中关于.htaccess文件的介绍:

.htaccess文件(或者”分布式配置文件”)提供了针对目录改变配置的方法, 即,在一个特定的文档目录中放置一个包含一个或多个指令的文件, 以作用于此目录及其所有子目录。
说明:
如果需要使用.htaccess以外的其他文件名,可以用AccessFileName指令来改变。 例如,需要使用.config,则可以在服务器配置文件中按以下方法配置:
AccessFileName .config
允许放在这些文件中的指令取决于AllowOverride指令, 此指令按类别决定了.htaccess文件中哪些指令才是有效的。 如果一个指令允许放在.htaccess文件中,则,在Apache手册的说明中,此指令会有一个覆盖段, 其中说明了为使此指令生效而必须在AllowOverride指令中设置的值。

…….

更多内容:Apache指南—.htaccess文件

2. 使用过程中遇到的问题
1).htaccess文件的创建
在使用过程中第一个遇到的问题就是.htaccess文件创建的问题。因为我使用的是Windows操作系统,Windows不允许以正常的方式创建.htaccess。原因很简单,.htaccess只有扩展名。不能以正常的方式创建那么,意味着非正常的方式可以创建:
a)从网上直接下载一个(从Google或百度里搜索一下),这是最简单有效的方法,唯一要注意的问题就是小心病毒。在病毒横行的年代,我们要时刻保持警惕。
b)本人在网上搜了一段VB代码:

Dim fso, f1
Set fso = CreateObject(”Scripting.FileSystemObject”)
Set f1 = fso.CreateTextFile(”d:\.htaccess”, True)

用记事本创建一个文本文件,将以上三行代码复制到此文本文件。把文本文件的扩展名由.txt改为.vbs,然后双击即可。默认将.htaccess文件保存在D盘根目录,你可以根据自己的需要修改上面的代码。注:必须使用已存在的路径,若路径不存在,则会抛出异常!

2).htaccess文件的使用
使用zendframework时,需要在根目录下建一个.htaccess文件,并创建如下内容:

RewriteEngine on
RewriteRule .* index.php
php_flag magic_quotes_gpc off
php_flag register_globals off

然后在php.ini中AllowOverride 设置为 All

以上设置的目的是使所有请求都重定向到index.php,由index.php来处理所有请求(为什么这样做,我会在其他文章中介绍)。但设置完成后,无论我访问任何页面,都会报错:
————————————————————–
Internal Server Error
The server encountered an internal error or misconfiguration and was unable to complete your request.

Please contact the server administrator, myleoliu@gmail.com and inform them of the time the error occurred, and anything you might have done that may have caused the error.

More information about this error may be available in the server error log.
————————————————————-

查看Apache的日志,发现如下错误
————————————————————–
[Sat May 15 14:45:43 2008] [alert] [client 127.0.0.1] E:/Projects/BS/PHP/zendtest/.htaccess: Invalid command ‘RewriteEngine’, perhaps misspelled or defined by a module not included in the server configuration
————————————————————–
原来是模块rewrite_module 没有加载(apache默认是不加载此模块),在httpd.config中,将#LoadModule rewrite_module modules/mod_rewrite.so前面的#去掉。然后保存、重启Apache,问题解决。