用户
 找回密码
 入住 CI 中国社区
搜索
查看: 13209|回复: 3
收起左侧

使用 CodeIgniter 框架快速开发 PHP 应用(十二)

[复制链接]
发表于 2008-2-21 14:20:30 | 显示全部楼层 |阅读模式
作者: David Upton
翻译: chenz1117

第 12 章 产品版本,升级和重大决定

有意义的一天来临了。 你开发的网站在你的本地开发环境中运行得足够好了,是时候把它上传到远程的WEB服务器上成为一个正式运行的网站了。实现它应该是容易的。上传所有的文件,包括系统文件夹的全部, 更新 config 设置, 复制完毕并连接到数据库, 把它们传送出去。 有时候,它真的是很容易。

但是当它还没有成功的时候,它处在你把重要的一切呈现在一个风险资本家或公众视野面前的前夜。 因此, 为了防止出现意外, 这一章涉及:

。 该在你的 config 文件中设置什么

。 如果出现问题需要的一些诊断工具

。 本地服务器与可能让你出现差错的服务器上的隐性差异

。 几点安全提示, 现在你将处身于大千世界

下一步, 这一章包括升级, 以及留意那些CI在年内已经改变的一些方式。它稳定吗? 如果你提交一个重要网站你应该如何决策?你该怎么行动?一旦你的网站开始运作,而CI又推出新版本你又如何应对?

最后,我们简短地讨论如何针对 CI 的核心作出你自己的修改。它都在那里; 它是开放源代码的,修改是可能的。 是否可行是另一件事情。

连接: 检查 Config 文件

系统通常会在接口处出错。 那是为什么需要 config 文件的原因: 给你一个地方放那些接口。 如果你还没有这么做,你已经错过 CI 的主要优点之一。

主要的接口问题可能是:

URL

CI 通过查找文件工作。 你的用户连接到 index.php, 然后开始运行整个程序。 至少,它应该运行。 确定你在你的 config 文件中已经正确地设置好WEB地址和其它服务器地址。 WEB地址是你的网站根目录; 你可能需要咨询你的ISP以便得到服务器地址,他们有他们自己的文件管理系统,一查便知。

我曾经尝试运行子域。 许多主机允许这么做, 但是把域映射到你的目录也许不是你希望的。

数据库

定位并连接到你的数据库经常是一个主要的话题。 查看你的 config 文件和你的 config/database文件。 你需要确定你有正确的网站和服务器地址和正确的数据库名称、地址, 用户名和密码。 小心前缀-有时这些自动地被添加的。 (你的网站叫 'fred' 。 你的数据库, 叫做 'mydata' 和你的用户名是 'mylogin'. 但是在服务器上他们叫做 'fred_mydata' , 'fred_mylogin', 等等。)

有时,它帮助在你的数据库上创建一个新的用户,即使你已经有了一个, 而且设定为以新的用户名和密码登录。 我不知道为什么会这样,但是经常会这样。

在 config 文件中,你能设定 CI 接受不同的URL协议类型,决定服务器如何处理 URI 字符串。 默认值是:

$config['uri_protocol']="auto";

但是如果这不能正常工作,有其他四个选项可以尝试。 如果正确的选项没被设定,你可能发现你的网站只有部分工作正常(举例来说) 表单不能调用目标页。

其他的 config 文件

config/routes文件设定程序执行的默认路径,如果用户没有指定一个controller/method (也就是如果他们仅仅登录到www.mysite.com). 这通常应该在默认值中被设定:

$route['default_controller']='index';

如果你重新命名系统文件夹了,必须记得你还需要在网站的根目录中改变 index.php 文件。 默认值设定为:

$system_folder="system";

找出PHP 4/5 和操作系统间的差异

CI 应该能够兼容PHP 4.3及以后的任何版本. 然而,这不意味着你写的任何的 PHP 编码都能正常工作-因此如果你在使用 PHP 5, 并且正在向一个 PHP 4 服务器迁移, 由于版本不同有可能引发问题。

PHP(无论哪一个版本) 可能以不同的方式被建立。值得一做的是在你本地和远程服务器上运行phpinfo(), 找到它们之间的差异。

大小写敏感在微软和 Linux 服务器之间是不同的。 因此如果你在一台windows操作系统的PC上开发你的网站, 然后上传到一台 Linux 服务器上,可能会收到出错信息,报告它找不到一些你要装载的模型或者类库。如果你检查过确定它们已经被上传, 你也需要确定大小写是正确的。 因为在 CI 的类定义和构造函数命名是要求以一个大写字母开始的,而且以一个大写字母开始一个文件名实现也很容易。 因此在一个控制器内,你装载一个模型,冠以一个大写字母开始,比如:($this->load->Mymodel), 但是windows和 Linux 可能会通过$this->Model调用不同的视图。

作为不同服务器的一个极端例子, 我有一次开始写一个控制器,并决定修改使它成为一个模型, 我把它保存在moel目录中,却没有意识到我已经写了头几行代码如下:

class Myclass extends Controller {
   function Myclass() {
       parent::Controller();

而不是把它改成:

class Myclass extends Model {
    function Myclass() {
        parent::Model();

在本地的Xampplite上运行,这没有出错。 迁移到一个远程的Linux服务器上, 立刻报错。(而且你能想像调试它花费了多长时间…)

一些 PHP 函数在不同的操作系统上似乎也有不同的行为表现: 举例来说, include_once()在windows上是大小写敏感的,但在其它系统上不是。虽然这与CI本身无关。

同时,你的数据库可能是一个不同的版本-许多ISP还在使用MySQL 3.23! 这似乎引起一些不兼容, 这意味着,通过SQL查询上传一个数据库不是很容易实现。(举例来说,它可能不接受数据表的提交。)

Linux与windows相比,有一个不同的文件权限管理系统。 确定你有对应的文件和目录的权限。CI有几个目录文件的权限在它能够正常运行前必须正确设置。

诊断工具

index.php文件的第一行是:

   error_reporting(E_ALL);

会在你的屏幕上显示所有的 PHP 错误。

很显然,这种错误报告很糟糕,可能给黑客太多的信息,所以在产品服务器上你应该改成:

error_reporting;(0)

但是然后,任何的问题可能只是造成一个空白的屏幕,没有可提供帮助的诊断信息。 你可能必须把错误报告再打开直到你已经使网站正常运行。 一个折中的做法是把它设定为一个中间状态, 像是:

error_reporting(E_ERROR);

这将会避免 'warning' 但是仍然会给你关于严重的问题信息。 'warning' 通常不会中止程序的执行, 但是可能转向其它你没有考虑到的问题。

CI Profiler 类-见第 8 章-也非常有用: 它显示你正在做什么查询, POST数组的内容是什么。

当这些工具都不起作用时,需要使用其它的工具,下面列出一些我已经发现的其它工具:

1.  设定 CI 开启日志文件。(通过修改 config 文件实现-见第 8 章-你需要设定:
    $config['log_threshold']=4;

4显示所有的信息, 包括notices和warnings; 有时这些会显示潜在的问题。 然后查看日志文件 (保存在
/system/logs中.) 这将会告诉你哪一个 CI 部分已经被调用,因此,你能至少能看到程序中止的地方。 (把值设定回 0 会停止日志记录。 记得这样做, 调试完毕后请删除日志文件: 它尺人地占用空间。)

2.  如果你能存取他们, 打印出 PHP 服务器和会话变量:

    print_r($_SERVER)
   
    而且:
   
    print_r($_SESSION)

而且使用他们检查 document_root 和 script_filename 值是不是你意料之中的。 如果不是,你可能需要调整config文件中的 base_url,server的值的设置,你还可以看一下是否有[HTTP_COOKIE]的设置,它会显示是否你的session类能够工作。

3.  用PHP的方法检查 CI 把什么装载进它的“超级对象”:

    get_declared_classes();

    和:
   
    get_class_methods();

4.  CI 自己的 show_error() 函数只是格式化生成的错误报告. 因此在你的代码中加入下面的这一行,可以显示出程序运到到哪一个命令分支:

    show_error('test of error function');

会在你的屏幕上显示:

test of error function

我没有发现这非常有用。 当我想要系统在我需要时给我一个完整的有帮助的错误报告, 在何时以及哪里找到他们, 但是当我不想要他们出现的时候将不显示他们。 我需要编写我自己的函数,如下:

function reportme($file, $line, $message)
{
   $obj =& get_instance();  
   if (isset($_POST)) {
       $bert = print_r($_POST, TRUE);
   } else {
       $bert = 'no post array';
   }
   if (isset($_SESSION)) {
       $sid = print_r($_SESSION, TRUE);
   } else {
       $sid = 'no session array';
   }
   $time = Gmdate("H:i j-M-Y");
   /*full report*/
   $errorstring =  "$time - $file - $line: $message: POST array: $bert SESSION array: $sid\n";
   /*short report*/
   $shortstring =  "$file - $line: $message";
   /*set $setting to 'test' if you want to write to the screen*/
   $setting = 'test';
   if ($setting == 'test') {
       echo $errorstring;
   }
   /*set $action to 'log' if you want to log errors*/
   $action = 'log';
   if ($action == 'log') {   
          $filename = $obj->config->item('errorfile');
          $fp = fopen("$filename", "a+")or die("cant open file");
          fwrite($fp, $errorstring);
          fclose($fp);
   }        
}                                          


把它放在一个叫做errors的library文件中。我需要装载这个library, 然后, 每当我对一段代码没有信心的时候,我include这个函数:

    $this->errors->reportme(__FILE__,__LINE__,'if the code has reached here it is becase...');

然后我能设定 reportme() 函数在屏幕上显示, 或保存在日志文件中。


这样一个简单的方法的有几个优点:第一,我能容易地修改 reportme() 函数, 使它写错误信息到一个文件, 或全然什么也不做: 因此我能立刻使我所有的错误信息从屏幕消失, 或再回来, 只要修改一行代码。

第二,我生成一个包含特别的值的变量。 (一个ID,类型为整型,比方说。) 我生成一个尽可能完整和有帮助的信息。我试着找到一个整数, 连同我实际上得到的值。 函数也用PHP__FILE__ 和 __LINE__魔术常数完整地告诉我它发生的地方。

因此, 如果当我把程序迁移到另外的一个服务器的时候,这块特别的代码在我写了一段时间之后如果出现一个问题,我能立刻找到这段代码,而且文字信息帮助我记起它为什么是一个问题。 在你编程完成之后六个月,你不可能马上弄明白, 尤其如果它发生在深夜,一位客户在电话中要求你给他解释什么的! 更有帮助的错误本文,将使你容易地作出反应。

第三,如果网站的完整性真的至关紧要,我可以设定函数用电邮把错误报告发送给我。在开发阶段可能造成邮箱爆满,但是一旦网站运行稳定,如果网站遇到问题,有一个即时的警告邮件发给我会是非常有用的。你将会在你的用户知道之前就发现问题。

应对新的CI版本带来的变化

在2006年2月28日到2006年10月30日之间, CI 从它的第一个Beta版升级到1.5版。 那是相当令人印象深刻的升级。

在那期间,Rick Ellis做了一些非常激进的变化, 特别地在网站的结构上。 大致上,他已经小心使他们向后兼容-但是并没有完全做到。 如果你刚使用 CI 并下载了最新版本,你能够跳越这一个阶段。 但是如果你们写了使用较早的版本的程序。如果你正在使用 CI libraries或者是其他人写的plug-ins。

瑞克已经努力解决二个主要的问题:

该如何装载模型、和如何调用他们

起先,没有模型,只是使用目录来管理脚本和libraries。没有准备要自动地初始化它们作为CI超级对象的一部份。结果是, 你有了一个 MVC 系统没有 'M' 文件,似乎令人困惑。

不仅这样,还有二个libraries目录: /system/application/libraries保存你为自己编写的一些文件, 而 /system/libraries保存系统自己的操作文件。 这可能让一些人感到混乱: 这二者之间存在很大的差异! 你应该增加到或改变前一个目录中的文件,但你或许不需要改变后者,当然这很容易搞错。 (而且如果你这样做了,如果你对 CI 版本升级,你会面临无法兼容的严重危险: 在下面可以看到。)

1.3 版带来了一个新的 'Model'类。 用户手册定义模型为" 被设计用来与你的数据库中的数据合作的PHP类 ". 当第一次使用时, CI 模型自动地与数据库连接。 然而, 从1.3.3 版起, 你一定要在模型或控制器中显式地连接数据库。

或者, 当你从控制器调用模型的时候,你可以以如下格式实现:

$this->load->model('Mymodel', '', TRUE);


然后 'TRUE' 指定当与默认的数据库连接时才装载模型,正象你的 config 文件所定义的那样。 (第二个叁数, 在这里的空格,是模型的一个可选择的别名.)

如果你把'模型 '功能(在 MVC 意义上的) 放到一个 'library' 或者一个 (声明:不赞成这么做)脚本之内,CI 或许还可以工作。 较早的版本并没有'model'目录: 你不得不用其它方式存取CI资源-见到下一个区段!

如何初始化你自己的 'library' 类

本来,你无法使你自己的类成为 CI 超级对象的一部份。这是一个问题,因为它意味着你的library代码不能通过AR读写数据库,或使用其他的 CI library,这样的限制太大了。

1.2 版本增加了读对类的 get_instance() 函数,允许你读写CI的超级对象(见第七章),你可以在你的 'library' 或者脚本中include它,然后使用 CI 资源。(除非你的新文件是一个函数脚本而不是一个 OO 类。 当然,脚本文件可能性用来编写简单的基本函数的选择。)

1.4 版引进了一个新的系统。你必须为每个'library'类创建二个文件。第一个是类本身,比如Newclass.php ,保存在application/libraries目录中,第二个,保存在application/init目录中, 必须叫做 init_newclass.php 必须包含几个标准代码行,用来初始化,让它成为CI超级对象的一部分,你仍然需要使用
get_instance()函数存取 CI 资源。

在1.5版中, init 文件夹已经被不鼓励使用了,初始化将自动地进行。 每个'library'只需要一个文件。

旧的脚本目录也已经不鼓励使用了。'不鼓励使用'通常意味着相关的实现方法能够工作,但是开发者应该尽量不要这么做,因为CI不能保证在未来的版本中还支持它。 如果你仍然有一个system/application/scripts目录,不需要紧张-但是也不要再使用它。

如果你正在计划使用由 CI 社区编写的libraries或者plug-ins,请首先检查这些资源是否是为CI的最新版本开发的。 有相当多的是为1.4.1版本开发的,还有另一个'init'文件。 更新它们不困难,但是需要小心行事,确保它们正常工作。

如果新的 CI 版本出来,我应该更新吗?

CI 的新版本时不时会推出。 它们带有如何更新的指南。 通常,这包括拷贝一组新的文件到你的system目录。 有时,你需要改变 config 文件, 或者你的 index.php 文件。不会有巨大的改变。因为目录结构把你的应用保存在他们自己的位置,这样可以在升级系统时不用涉及到你的应用系统。

但是, 假如说你已经在 1.5 版中写了一个你的优秀作品. 它被上传到你的生产系统并且运行得很好。 如果Rick推出了CI 1.6 版.(或 2.8 或任何其它的…) 它有一些有趣的新功能、还有一些Bug修正。你是否需要对它进行升级?

我会说, '是的', 如果它是一个较小的升级, 比如在 1.5.2 和 1.5.3之间,你应该升级. 但是如果它是一个主要的版本变化, 而你的现有系统正在工作, 暂缓升级是个比较明智的选择。 你可能从数字部分分辨出版本升级变化大小的程度, 但是也可以从新版本附带的'变化清单' 列表得出结论。从去年开始 CI 划分了三个目录来列出三类不同的变化:

Bug修正: 令人惊讶地少,CI有优良的代码, 而且大部份的基础类已经被数以千计的使用者精心地测试了上百遍。

新的功能:. 这些经常地出现,但是如果你不使用这些新功能来开发你的应用,它们并不会对你有什么帮助。

敏感的变化: 就象我已经描述过的, CI 已经经过一个内存的升级的过程,而且它理所当然会继续这么做。你能从下面的列表看到,其中的一些向后兼容, 否则它们可能导致你重写你的部分代码。

在 CI 的版本之间的一些变化:

版本   变化记录

1.2   增加一个全局函数,名为get_instance(),允许你自定义的读写CI的主对象。

1.3   增加对模型的支持。

1.3   增加传递你自己的初始化参数到客户自定义library能力,你可以这样做:$this->load->library()

1.3   增加较好的类和函数的命名空间-避免冲突。所有的CodeIgniter类开始冠以CI_前缀和所有的控制器方法
      以_ci为前缀避免控制器命名冲突。

1.3.3 模型不自动连接数据库。

1.4   增加了用你自定义的类替换核心系统类的能力。

1.4   升级模型的装载函数,允许对同一模型多次装载。

1.4.1 更新了plugins, helpers和language类,允许你的应用程序目录包含你自己的plugins, helpers
      和language类。先前,在安装时它们总是被当作全局的,如果你的应用目录包含了它们中的任一个,你自定义的将会覆盖系统中的这些资源。

1.4.1 声明不鼓励使用application/script目录。 但它将会继续为以前的使用者提供正常使用的可能性,但
      是建议你改为创建你自己的library或model。在CI支持用户自定义library或者model之前,它本来就存在,但是它不再是必不可少的。

1.5   增加能力扩展library和核心类的功能,而且也能替换它们。

1.5   声明不鼓励使用init 文件夹。初始化现在自动进行。

不要误解我。 这些全部是有意义的改变,并且他们全部是对CI的升级。 如果你开始一个新的项目, 请使用最新的 CI 版本。 但是如果你使用1.3版开发应用,你会发现你的scripts目录被声明不鼓励使用,而且你的模型也不会自动连接数据库。我个人在使用CI的1.3版,而不尝试升级它。 生命苦短。

如何修改CI的基础类

一般使用者不可能需要改变基本的CI类。 它是相当好的框架,它做许多事物, 而且毕竟,使用框架会使做事更容易, 对不对? 然而, 如果你一定….

CI 是开源产品, 一旦下载你能看到所有的代码。 这包括使 CI 工作 (在system/libraries保存) 的基本library以及你一旦编写后保存system/application/libraries的你自己的libraries,因此你可能改变CI让它以你喜欢的方式工作。

修改系统libraries文件有二个问题,就是:

。 没有保证,你的新密码是否会与 CI 的其余部分兼容,或与更新的版本兼容。这可能导致不容易跟踪敏感或者奇怪的错误。

。 如果你稍后升级你的 CI 版本,系统目录可能被改变。 你改变过去的libraries将被新的文件覆盖或升级,    因此你需要维护你自己的修改并把它们迁移到升级的版本。

不过,从 1.5 版起, 现在针对修改CI类库有二个有意义的机制(除了针对'数据库'和'控制器'类,触及这两个都很危险.)

第一,无论在哪个操作系统中,你能在你的 /system/application目录中创建一个与系统基础类同名的文件。 系统会优先使用这一个, 如果这个文件不存在或不可用则使用/system目录中的那个。 这需要有精确的命名限制-详见用户手册。 它也需要你复制所有在现有的类中存在的功能,让它们和你自己修改过的那些一起工作。

第二, 更方便地,你能从现有系统类派生出一个新类。 (因此可能派生一个子类是更好的做法),当然也有命名限制,详见用户手册。继承一个系统类意味着你的新子类潜在继承了CI类中的所有资源,并增加几个你自己的额外方法。这应该意味着,如果你升级你的 CI 版本,祖先类将会被替换,但是你的新子类(你应该把它放在system/application目录)将安然无恙。

然而, 两者都不能保证你的代码与CI的其它部分完全兼容。

通过CI在线论坛,有扩充校验类、单元测试和会话类的各种不同的建议。 单元测试, 举例来说,只有二个函数和比较有限的字符数。 也许你想要一个函数,用红色显示错误信息,因此,当测验结果被返回时,他们会显得比较醒目 ?

如果你想要扩充一些其他的测试函数,通过一个子类把它加入会是比较简单的,每次在控制器中编写代码调用单元测试。

如果你想要这样做,你可以这样开始你的新子类:

class MY_Unit_test extends CI_Unit-test {
    function My_Unit_test() {
        parent::CI_Unit_test();
    }

    function newfunction() {
        //new code here!
    }
}

在这里注意三件事:

。 单元测试类的名称是CI_Unit_test,即使类的代码文件名是system/libraries.unit_test。

。 如果你需要在你的子类中使用一个构造函数,确定你首先在里面调用父类的构造函数。

。 你的新子类名称应该与 MY_ 前缀并保存为application/libraries/MY_unit_test.php。(不像系统中那些主要的类, 它们是以CI_为前缀,但是不是文件名, 在这里MY_前缀是两者的一部份.)

一旦你已经创建你的子类,你像这样装载它:

$this->load->library('unit_test');

换句话说, 完全地与你以前编写的了类相同; 而且你以同样的方式调用一个函数,但是这次你不但能调用原来的单元测试函数,还能调用你自己编写的新函数:

$this->unit_test->newfunction();

当你以后升级你的 CI 的时候,在系统文件夹中的单元测试类库将会覆盖,但是那个在application目录中的将不会,因此你的代码将会仍然在那里。 当然,你将会需要检查被更新的系统类是否仍然与你自己的代码兼容。

摘要

在这一章里,当你尝试迁移一个本地应用到远程服务器的时候,有些东西可能会出错。 这可能包括:

。 PHP 或 MySQL 的版本差异

。 不同的操作系统

特别地,我们分析了大小写敏感问题, PHP 差异、和 MySQL 问题。 我们也谈及几个诊断工具。

然后我们分析了CI 的升级。 这些都带来了重要的进步, 但是我的忠告是, 如果你在现在的 CI 版本上工作得很好,如果CI有新的版本推出,仔细评估是否需要升级和如何升级。

最后,我们分析修改 CI 基础类的正反两方面。 大多数的使用者将不需要这么做。但是如果你确信需要这么做,我强烈建议:实现它的最好方式是从一个现存的library类中派生一个子类。

下一章关于

实现CRUD—或让它们进行组合查询
发表于 2008-7-31 18:35:30 | 显示全部楼层
讲得很清楚哈
发表于 2008-8-20 17:52:39 | 显示全部楼层
看完
发表于 2013-7-31 16:19:11 | 显示全部楼层
支持楼主!!

本版积分规则