当发明计算机编程不久之后,便有人发现,这其中涉及到了太多的重复操作。之后,也许是 Ada Lovelace(人类历史上的首位程序员),又或许是 Alan Turning,决定将计算机程序模块化,从而使得片段程序代码可以重复使用。PHP 程序员们早已习惯了将需要重复使用的代码写在函数中,并将这些函数放在 include 文件里。
结果是 CI 诞生了,作为一个业余时间开发的作品,Rick 慷慨地决定使它成为开源作品。在跑生意间隙,他保持经常更新 CI。他也创建了一个优秀的论坛,CI 使用者能提出问题并且分享开发心得。所有这些资源可从下列网址获得:http://www.codeigniter.com/。
+
他能实现自己的目标吗?相信你使用后会得出自己的结论……
+
+
1.4 “开源”商业模式
+
这类软件可能会有一些让人感到困惑的地方。如果你喜欢你的软件与昂贵的支持合同和“大公司”联系起来的话,那么 CI 并不适合你。(但是,你使用 PHP 能做什么呢?PHP 的用户都知道,支持和 PHP 软件的开发,一定程度上依赖于“社区”数百或数千用户的义务劳动。)
+
社区支持也存在一些问题。一致性和高质量不是“有保证的”—任何人都可以发表到论坛上,有时这些发表的内容是完全错误的。(注意:如果你去看一下一些昂贵的商业软件的授权细则,也是不对产品质量做出保证的。)但是对于“开源”产品而言,则必须自行细加推敲,而不能一味的相信论坛上的表象言论。如果你对研究新事物饶有兴致,那么 CI 非常适合你!
+
然而,所有明智的开发者都想知道把时间和精力投入到“一个人的团队”的产品中是否明智。Rick Ellis 利用业余时间编写这个项目,并从他在 pMachine 的同事 Paul Burdick 那得到了一些帮助。它是免费的。他不对维护或开发 CI 做出任何承诺。他可能回去继续做一个摇滚音乐家。
+
另一方面,下载 CI 之后,您下载的版本将继续工作。您不必依赖升级和修补程序。Rick 的代码写的非常好,只在以前有过几个严重的 BUG。如果 CI 工作正常,你便没有理由不继续使用 CI。到目前为止,我只发现了两个导致我的代码出错的情况,是这个框架而不是我的代码上的 BUG。(这两个 BUG 已被修复。)
+
CI 的网站是社区和论坛的门户。
+
+
+
1.5 CI 不能做什么
+
CI 有它本身的缺点。Rick 把 CI 定义为小型“轻量级”框架。(1.5 版压缩后只有 737 KB,可以在几秒种内下载完毕。Zend Framework 是 10 MB)CI 不能解决你所有的问题。但它能够:
Start with MySQL time 20070101120000
+now convert to unix timestamp 1167652800
+then back to 'human' time 2007-01-01 12:00 PM
+now convert unix stamp to local time in Tehran 1167661800
+and say that in human time 2007-01-01 02:30 PM
function converttext()
+{
+ $this->load->helper('text');
+ $this->load->helper('inflector');
+ $mytext = "Mr Bill Gates is a man I like. He is a very clever man and writes superb software";
+ echo"$mytext<br />";
+ $disallowed = array('like', 'clever', 'superb');
+ $string = word_censor($mytext, $disallowed);
+ echo"Censored, this might read: ";
+ echo"$string<br />";
+ $mywtext = word_limiter($mytext, 3);
+ $mytext = underscore($mywtext);
+ echo" His name could be written like this $mytext";
+ $mytext = camelize($mywtext);
+ echo"or like this $mytext";
+}
Mr Bill Gates is a man I like. He is a very clever man and writes superb software
+Censored, this might read: Mr Bill Gates is a man I ####. He is a very ######
+man and writes ###### software
+His name could be written like this mr_bill_gates...or like this mrBillGates...
<?php
+$lang['welcome_title'] = 'Willkommen auf dieser Web Seite';
+$lang['welcome_text1'] = 'Guten tag ';
+$lang['welcome_text2'] = 'jetzt sind wir dynamisch!';
+?>
本章看几个有用的 CI 功能和辅助函数。他们中的每一个都是如何用几行 CI 代码无逢存取一系列应用和动作的好例子,如果从头开始编码的话你需要具备许多专业知识。在许多情况下,CI 提供一个对已有类的简单接口,这些类可能是你从 PEAR 或者其他源代码中提取的。但 CI 给你一个标准接口:你只需把它作为本地 CI 代码,并且框架向你提供所有接口的内容。
//list files actually found
+$files_there = get_filenames('e:/rootfolder/system/application/controllers');
+// list files we expected
+$files_expected = array('start.php', 'index.php');
+// any found that we didn't expect?
+$difference = array_diff($files_there, $files_expected);
+echo"<br />Missing files are:";
+print_r($difference);
+// any expected that we didn't find?
+$difference = array_diff($files_expected, $files_there);
+echo"<br />Extra files are:";
+print_r($difference);
CI 不比较两个文件的内容,只比较文件名。如果你允许多人上传文件,那么,其中的两个人,将很有可能在无意中,使用相同的文件名来上传不同的文件,并且,你可能不希望丢失第一个文件。另一方面,如果你通过网站上传名字总是相同的文件,你也许宁愿只在网站上保存最新的文件,在这种情况下,覆盖文件是一个简单的节省空间的方法。
+
顺便说一下这个图片,我们将在下一节用到它。
+
+
+
11.4 CI 的图像类
+
如果你允许用户上传图片,你还需要看看 CI 的图像处理类。他支持 PHP 最流行的三个图像类库:GD/GD2、NetPBM 和 ImageMagick。(使用 phpinfo() 查看你的服务器是否支持这些类库。)虽然图像水印效果只支持 GD/GD2:
<?php
+class Sites extends Controller {
+/*the filename, class name, constructor function names and this
+variable are the only thing you need to change: to the name of the
+table/controller (First letter in upper case for the Class name and
+constructor function, lower case for the file and variable.lower
+case!)*/
+ var$controller = 'sites';
+/*constructor function*/
+ function Sites()
+ {
+ parent::Controller();
+ $this->load->model('crud');
+ }
+/*function to update an entry (if an ID is sent) or to insert a new
+one. Also includes validation, courtesy of CI */
+ function insert($id)
+ {
+ $this->crud->insert($this->controller, $id);
+ }
+/*interim function to pass post data from an update or insert through
+to Crud model, which can't receive it directly*/
+ function interim()
+ {
+ $this->crud->insert2($this->controller, $_POST);
+ }
+/*function to delete an entry, needs table name and id. If called
+directly, needs parameters passed to function; if not, from Post
+array*/
+ function delete($idno=0, $state='no')
+ {
+ if(isset($_POST['id'])&& $_POST['id'] > 0)
+ {$idno = $_POST['id'];}
+ if(isset($_POST['submit']))
+ {$state = $_POST['submit'];}
+ $this->crud->delete($this->controller, $idno, $state);
+ }
+/*function to show all entries for a table*/
+ function showall()
+ {
+ $this->crud->showall($this->controller, $message);
+ }
+/*function to show all data in a table, but doesn't allow any alterations*/
+ function read()
+ {
+ $this->crud->read($this->controller);
+ }
+/*function to set off the test suite on the 'crud' model. This
+function need only appear in one controller, as these tests are made
+on a temporary test table so that your real data is not affected*/
+ function test()
+ {
+ $this->crud->test();
+ }
+}
+?>
+
+
你领会之后,你会发现它是优美苗条并且十分通用的。如果你想让 people 控制器去代替 Sites 控制器--换句话说,允许你在 people 表中去建立,读取,更新或删除记录,等等,你需要做如下事情:
/*this function lists all the entries in a database table on one
+page. Note that every db table must have an 'id' field and a 'name'
+field to display!
+This page is a jumping-off point for the other functions - ie to
+create, read, update or delete an entry.
+When you've done any of these, you are returned to this page. It has a
+'message' parameter, so you can return with a message - either success
+or failure.*/
+
+function showall($controller='', $message = '', $test ='no')
+{
+ $result = '';
+ $mysess = $this->session->userdata('session_id');
+ $mystat = $this->session->userdata('status');
+ if(!$this->db->table_exists($controller))
+ {
+ $place = __FILE__.__LINE__;
+ $outcome = "exception:$place:looking for table $controller: it doesn't exist'";
+/*test block: what if there is no controller by that name?*/
+ if($test =='yes')
+ {
+ return$outcome;
+ }
+ else{
+ $this->failure($outcome, 'sites');
+ }
+ }
+/*end test block*/
+ $this->db->select('id, name');
+ $query = $this->db->get($controller);
+ if($query->num_rows() > 0)
+ {
+ $result .= "<table class='table'>";
+ $result .= "<tr><td colspan='3'><h3>$controller</h3></td></tr>";
+ $result .= "<tr><td colspan='3' class='message'>$message</td></tr>";
+ $result .= "<tr><td colspan='3'>";
+ $result .= anchor("$controller/insert/0", 'New entry');
+ $result .= "</td></tr>";
+ $result .= "<tr><td colspan='3'>";
+ $result .= anchor("$controller/read", 'Show all entries in the table');
+ $result .= "</td></tr>";
+ foreach($query->result()as$row)
+ {
+ $result .= "<tr><td>";
+ $result .= $row->id;
+ $result .= " ";
+ $result .= $row->name;
+ $result .= "</td><td>";
+ $result .= anchor("$controller/insert/$row->id",'Update this entry');
+ $result .= "</td><td>";
+ $result .= anchor("$controller/delete/$row->id",'Delete');
+ $result .= "</td></tr>";
+ }
+ $result .= "</table>";
+ $data['text'] = $result;
+ $this->display->mainpage($data, $this->status);
+ }
+ else
+ {$place = __FILE__.__LINE__;
+ $outcome = "exception: $place: no results from table $controller";
+/*test block: were there results from this table/ controller?*/
+ if($test == 'yes')
+ {$place = __FILE__.__LINE__;
+ return$outcome;
+ }
+/*end test block*/
+ else{
+ $message = "No data in the $controller table";
+/*note: this specific exception must return to another controller
+which you know does contain data... otherwise, it causes an infinite
+loop! */
+ $this->failure($message, 'sites');
+ }
+ }
+}
+
+
它列出了一张表,展示了关于每个条目的一些数据(ID 和名称)。你也可以选择更新或删除功能,以更新或删除该条目:这是利用 CI 的 anchor 函数创建超链接,并链接到适当控制器中的适当函数。
+
这也有一行代码,为你提供了创造一个新站点的机会,再通过超链接连接到控制器的 insert 函数。(注:我将添加新的条目和更新旧的条目都称为 insert 函数,这是因为,模型假设如果插入的是已存在的 ID 号码,它将更新相应的条目,如果没有对应的 ID,它会创建一个新的条目)。
执行删除查询以前,它会检查 ID 号是否已设置(否则可能删除所有内容)。然后,它使用 CI 的 Active Record 执行删除,并确定此行已从数据库表中删除。如果此行已删除,则返回到 showall 函数中。你会发现它传回两个参数--控制器名称和一个报告删除已成功执行的消息。(这是 showall 的第二个参数。如果设置它,则会在表格的顶部显示一个红框,以便让用户知道怎么回事。)
/*DELETE FUNCTION: given table name and id number, deletes an entry*/
+ function delete($controller, $idno, $state='no', $test='no')
+ {
+/*first check that the 'yes' flag is set. If not, go through the
+trydelete function to give them a chance to change their minds*/
+ if(!isset($state) || $state != 'yes')
+ {
+/*test block: are 'yes' flags recognised?*/
+ if($test == 'yes')
+ {
+ $place = __FILE__.__LINE__;
+ $outcome = "exception at $place: sent state value $state to trydelete function ";
+ return$outcome;
+ }
+ else
+/*end test block*/
+ {$this->trydelete($controller, $idno, 'no');}
+ }
+ else{
+/*'yes' flag is set, so now make sure there is an id number*/
+ if(isset($idno) && $idno > 0 && is_int($idno))
+/*test block: with this id no, am I going to do a delete?*/
+ {
+ if($test == 'yes')
+ {
+ $place = __FILE__.__LINE__;
+ $outcome = "OK at $place: doing delete on id of $idno ";
+ return$outcome;
+ }
+ else{
+/*end test block*/
+/*if there is an id number, do the delete*/
+ $this->db->where('id', $idno);
+ $this->db->delete($controller);
+ $changes = $this->db->affected_rows();
+ }
+ if($changes != 1)
+ {
+/*test block: did I actually do a delete? */
+ $place = __FILE__.__LINE__;
+ $outcome = "exception at $place: cdnt do delete op on $controller with id no of $idno";
+ if($test == 'yes')
+ {return$outcome;}
+ else
+/*end test block*/
+/*if there was no update, report it*/
+ {$this->failure($outcome);}
+ }
+ else{
+/*test block: I did do a delete*/
+ if($test == 'yes')
+ {return'OK';}
+ else{
+/*end test block: report the delete*/
+ $this->showall($controller, "Entry no. $idno deleted.");}
+ }
+ }
+ else
+ /*test block: report id number wasn't acceptable'*/
+ {
+ $place = __FILE__.__LINE__;
+ $outcome = "exception at: $place : id no of $idno set for delete op in $controller, expecting integer";
+ if($test == 'yes')
+ {return$outcome;}
+ else
+/*end test block: if I failed, report me*/
+ {$this->failure($outcome);}
+ }
+ }
+ }
/*the most complex function. This creates an HTML form, based on the
+description of the fields in the form array. This is sent to our
+display model, which sets up a view and shows it to the user.
+The view then sends a POST array back to the controller. The form
+can't call this model directly, so it has to call the controller,
+which refers it back to the model.
+Note the function parameters:
+1. The controller parameter is whichever controller/ table has called
+the model - eg the 'sites' controller, or the 'domains' controller.
+The controller has the same name as the table it manipulates.
+2. The optional id parameter is the id of an individual entry in that
+table.
+3. The optional 'test' parameter is so you can set the form up to make
+usable responses to self-test functions.
+*/
+ function insert($controller='', $id=0, $test='no')
+ {
+ $myform = '';
+ $myid = 0;
+ $currentvalue = array();
+/*test if the table exists*/
+ if(!$this->db->table_exists($controller))
+ {
+ $place = __FILE__.__LINE__;
+ $outcome = "exception: $place:looking for table $controller: it doesn't exist'";
+ if($test =='yes')
+ {
+ return$outcome;
+ }
+ else{
+ $this->failure($outcome, $controller);
+ }
+ }
+ else
+ {
+ if($test =='yes')
+ {
+ return'OK';
+ }
+ }
+/*end test block*/
+/*next check if there is an id number. If there is, we need to get the values to populate the table fields*/
+ if(isset($id) && $id > 0)
+ {$myid = $id;
+ $this->db->where('id', $id);
+ $query = $this->db->get($controller);
+ if($query->num_rows() > 0)
+ {
+ $row = $query->row();
+//--------------work out the values we want!
+ foreach($rowas$key =>$value)
+/*
+first of all work out what value you want to show as the existing
+value in each line of the form. In priority order these are:
+1. the last value the user entered, from the post array
+2. the value from the database
+3. nothing, if neither of these is set.
+if we got here, the id does exist and is returning values, so get the
+existing values into a value array. Or, if there is something in the
+validation array, use that instead*/
+ {
+ $_POST[$key] = $this->validation->$key;
+ if(isset($_POST[$key]))
+ {$currentvalue[$key] = $_POST[$key];}
+ else
+ {$currentvalue[$key] = $value;}
+ }
+/*test block: there was an id number, so has the program gone for an
+update? if this is not a test, of course, just do the update*/
+ if($test == 'yes')
+ {
+ $place = __FILE__.__LINE__;
+ $outcome = "exception: $place: id of $id returned results from $controller table so have gone for update";
+ return$outcome;
+ }
+
+/*end test block*/
+ $myform .= "<tr><td colspan='2'>Update existing entry number $id</td></tr>";
+ }
+/*now catch situation where this query isn't returning results. We
+could only have got here with an integer set as our ID number, so
+this probably means we are trying to delete an entry that doesn't
+exist.*/
+ else{
+ $place = __FILE__.__LINE__;
+ $outcome = "exception: $place: despite id of $id cant get any results from $controller table";
+ if($test == 'yes')
+/*test block: there was and ID but there were no results*/
+ {
+ return$outcome;
+ }
+/*end test block*/
+ else
+ {$this->failure($outcome, $controller);}
+ }
+ }
+/*there was no ID number, so this is a new entry*/
+ else{
+/*If the user has filled in values, and has returned here because some
+of them didn't validate, we still need to repopulate the form with
+what he entered, so he only has to alter the one that didn't validate.
+Get these from the post array*/
+ if(isset($_POST))
+ {
+ foreach($_POSTas$key => $value)
+ {
+ if(isset($_POST[$key]))
+ {$currentvalue[$key] = $_POST[$key];}
+ }
+ }
+ $myform .= "<tr><td colspan='2'>New entry</td></tr>";
+
+/*test block: there was no ID, so this is a new entry*/
+ if($test == 'yes')
+ {
+ $place = __FILE__.__LINE__;
+ $outcome = "exception: $place: id of $id treated as no id, so going for new entry";
+ return$outcome;
+ }
+/*end test block*/
+ }
+
+/*the table exists, whether this is an update or new entry, so start to build the form*/
+ $myform .= "<table class='table'>";
+ $myform .= form_open("$controller/interim");
+ $myform .= '<p>This entry could not be made because...</p>';
+ $myform .= $this->validation->error_string;
+
+/*the rest of this function is common to inserts or update.
+Look up in the form array which form field type you want to display,
+and then build up the html for each different type, as well as
+inserting the values you want it to echo.*/
+
+
+ foreach($this->form[$controller]as$key => $value)
+ {
+/*This switch statement develops several types of HTML form field
+based on information in the form array.
+It doesn't yet cover checkboxes or radio or password fields. It adds
+a 'readonly' type, which is a field that only displays a value and
+doesn't let the user modify it*/
+ $fieldtype = $value[1];
+ $val_string = $this->validation->$key;
+ switch($value[1])
+ {
+/*a simple input line*/
+ case'input':
+ $data = array(
+ 'name' => $key,
+ 'id' => $key,
+ 'value' => $currentvalue[$key],
+ 'maxlength' => '100',
+ 'size' => '50',
+ 'style' => 'width:50%',
+ );
+ $myform .= "<tr><td>$value[0]</td><td>";
+ $myform .= form_input($data);
+ $myform .= "</td></tr>";
+ if($test == 'second')
+ {
+ return'input';
+ }
+ break;
+
+ case'textarea':
+/*a text area field.*/
+ $data = array(
+ 'name' => $key,
+ 'id' => $key,
+ 'value' => $currentvalue[$key],
+ 'rows' => '6',
+ 'cols' => '70',
+ 'style' => 'width:50%',
+ );
+ $myform .= "<tr><td valign='top'>$value[0]</td><td>";
+ $myform .= form_textarea($data);
+ $myform .= "</td></tr>";
+ break;
+ case'dropdown':
+/*a drop-down box. Values are dynamically generated from whichever
+table was specified in the forms array. This table must have an id
+field (which is now entered in the form) and a name field (which is
+displayed in the drop-down box).*/
+ $dropbox = array();
+ if(isset($value[3]))
+ {
+ $temptable = $value[3];
+ $this->db->select('id, name');
+ $query = $this->db->get($temptable);
+ if($query->num_rows() > 0)
+ {
+ foreach($query->result()as$row)
+ {
+ $dropbox[$row->id] = $row->name;
+ }
+ }
+ }
+ $myform .= "<tr><td valign='top'>$value[0]</td><td>";
+ $myform .= form_dropdown($key, $dropbox, $currentvalue[$key]);
+ $myform .= "</td></tr>";
+ break;
+ case'submit':
+/*a submit field*/
+ $myform .= "<tr><td>$value[0]</td><td>";
+ $time = time();
+ $data = array(
+ 'name' => 'submit',
+ 'id' => 'submit',
+ );
+ $myform .= form_submit($data);
+ $myform .= "</td></tr>";
+ break;
+ case'hidden':
+/*generates a hidden field*/
+ $myform .= form_hidden($key, $currentvalue[$key]);
+ break;
+ case'readonly':
+/*generates a field the user can see, but not alter.*/
+
+ $myform .= "<tr><td>$value[0]</td><td>$currentvalue[$key]";
+ $myform .= form_hidden($key, $currentvalue[$key]);
+ $myform .= "</td></tr>";
+
+ break;
+ case'timestamp':
+/*generates a timestamp the first time it's set*/
+// $myform .= "<tr><td>$value[0]</td><td>now()";
+ $timenow = time();
+ if($currentvalue[$key]==''||$currentvalue[$key]==0)
+ {$time = $timenow;}
+ else{$time = $currentvalue[$key];}
+ $myform .= form_hidden($key, $time);
+ $myform .= "</td></tr>";
+
+ break;
+ case'updatestamp':
+/*generates a timestamp each time it's altered or viewed*/
+// $myform .= "<tr><td>$value[0]</td><td>now()";
+ $timenow = time();
+ $myform .= form_hidden($key, $timenow);
+ $myform .= "</td></tr>";
+ break;
+
+ default:
+ $place = __FILE__.__LINE__;
+ $outcome = "exception: $place: switch can't handle $fieldtype";
+/*test block: what if the switch doesn't recognise the form type?'*/
+ if($test == 'second')
+ {
+ return$outcome;
+ }
+/*test block ends*/
+ else{
+ $this->failure($outcome, $controller);
+ }
+ }
+/*end the foreach loop which generates the form*/
+ }
+ $myform .= form_hidden('submit',$time);
+ $myform .= form_close();
+ $myform .= "</table>";
+/*Finally we've built our form and populated it! Now, stuff the form
+in an array variable and send it to the model which builds up the rest
+of the view.*/
+ $data['text'] = $myform;
+ $this->display->mainpage($data);
+}
function insert2($controller, $newpost, $test = 'no')
+ {
+ $myform = '';
+/*test the incoming parameters*/
+ if(!$this->db->table_exists($controller))
+ {
+//test here!
+ }
+
+ $this->load->library('validation');
+/*handle the validation. Note that the validation class works from
+the post array, whereas this function only has a $newpost array: same
+data, but different name. So we re-create the $_POST array.
+*/
+ $_POST = $newpost;
+/*now build up the validation rules from the entries in our master array*/
+ $errorform = '';
+ $newtemparray = $this->form[$controller];
+ foreach($newtemparrayas$key => $value)
+ {$rules[$key]= $value[2];}
+ $this->validation->set_rules($rules);
+/*and the name fields*/
+ foreach($newtemparrayas$key => $value)
+ {$fields[$key]= $value[0];}
+ $this->validation->set_fields($fields);
+
+ $this->validation->set_fields($fields);
+/*now do the validation run*/
+ if($this->validation->run() == FALSE)
+ {
+/*if the validation run fails, re-present the entry form by calling the 'insert' function*/
+ $id = $_POST['id'];
+ $this->insert($controller, $id, 'no', $_POST);
+ }
+ else
+ {
+/*The validation check was OK so we carry on. Check if there is an id number*/
+ if(isset($_POST['id']) && $_POST['id'] > 0)
+ {
+/*if yes: this is an update, so you don't want the id number in the
+post array because it will confuse the autoincrement id field in the
+database. Remove it, but save it in $tempid to use in the 'where'
+condition of the update query, then do the update*/
+ $tempid = $_POST['id'];
+ unset($_POST['id']);
+ $this->db->where('id', $tempid);
+ $this->db->update($controller, $_POST);
+ if($this->db->affected_rows()== 1)
+ {$this->showall($controller, "Entry number $tempid updated.");}
+ else{$this->failure("Failed to update $controller for id no $tempid", __FILE__,__LINE__);}
+
+/*if no id number, we assume this is a new entry: no need to unset the
+post array id as it isn't there! the database will create its own id
+number. Do the new entry*/
+ $this->db->insert($controller, $_POST);
+ if($this->db->affected_rows()== 1)
+ {$this->showall($controller, "New entry added.");}
+ else{$this->failure("Failed to make new entry in $controller ", __FILE__,__LINE__);}
+ }
+ }
+ }
/*this function builds a new temporary table. 'fred', in your database
+so you can test the CRUD functions on it without losing real data*/
+function testbuild()
+{
+ $this->db->query("DROP TABLE IF EXISTS fred");
+ $this->db->query("CREATE TABLE IF NOT EXISTS fred (id INT(11) default NULL, name varchar(12) default NULL)");
+ $this->db->query("INSERT INTO fred VALUES (1, 'bloggs')");
+}
/*this function destroys the temporary table, to avoid any confusion later on*/
+function testdestroy()
+{
+ $this->db->query("DROP TABLE IF EXISTS fred");
+}
/*given $state set to 'yes', test another array of values for the id number. Start by building a test table*/
+ $this->testbuild();
+/*then another array of values to test, and the results you expect...*/
+ $numbers = array(
+ '9999' => 'OK',
+ '-1' => 'exception',
+ 'NULL' => 'exception',
+ '0' => 'exception',
+ '3.5' => 'exception',
+ '' => 'exception',
+ '1' => 'OK'
+ );
+/*now do the tests*/
+ foreach($numbersAS$testkey => $testvalue)
+ {
+ $test = $this->delete('fred', $testkey, 'yes', 'yes');
+ $result .= $this->unit->run(preg_match("/$testvalue/", $test), 1, $test);
+ }
+/*destroy the test table, just in case*/
+ $this->testdestroy();
+/*return the results of this test*/
+ return$result;
+}
/*this function prepares a report on existing tests and allows you to choose which to do.
+ First, it selects a site and reports on that*/
+ function report($site=0,$message='')
+ {
+/*have you chosen a site yet?*/
+ $siteid = $this->uri->segment(3, 0);
+ if(!$siteid > 0)
+ {
+ $text = "<table class='table'>";
+ $text .= "<tr><td colspan = '2'>Select a site to work on</td></tr><tr>";
+ $this->db->select('name, id');
+ $query = $this->db->get('sites');
+ if($query->num_rows() > 0)
+ {
+ foreach($query->result()as$row)
+ {
+ $text .= "<tr><td>";
+ $text .= $row->name;
+ $text .= "</td><td>";
+/*note the next line uses the CI anchor function to generate hyperlinks*/
+ $text .= anchor("tests/report/$row->id","Select");
+ $text .= "</td></tr>";
+ }
+ }
+ $response['mytext'] = $text;
+ $response['message']= $message;
+ $this->display->mainpage($response);
+}
/*do db query here */
+/*format the results using the CI HTML table library*/
+ $report .= "Test history:";
+/*redefine our CI table layout if we want to, using our css file*/
+ $tmpl = array('table_open' => '<table border="1", class="table">',);
+ $this->table->set_template($tmpl);
+ if($query->num_rows() > 0)
+ {
+ $this->table->set_heading('Time of test', 'Name ,'Time taken','Result');
+ $report .= $this->table->generate($query3);
+ }
+ $this->table->clear();
用户论坛的地址是 http://www.codeigniter.com/forums/,这里对大多数 CI 问题进行活跃的、几乎连续不断的讨论。评论和建议并不一定总有用(或准确),但也有一些“资深会员”,他们经常贡献很多智慧。它同样是一个非常友好的论坛;人们问一些非常明显的“菜鸟”问题,也会得到耐心并有价值的回复。有时 Rick Ellis 自己也会被一些东西所吸引,但他并没有去涉及所有领域确实是明智之举。
在第十三章中,我们已经尝试开发了我们自己的 CRUD 应用程序。这是一个相当简单的模型,它截掉了很多细枝末节,仅仅允许你使用 HTML 表单对象的部分功能;但它确实拥有验证功能。
+
在本章中,我们已经提到了 Glossopteris 类库。
+
另外一个有趣的实现是“CodeCrafter”,它被列在 CI 的 Wiki 上面,同时也发表在南非的 Datacraft 软件咨询网站上(http://www.datacraft.co.za/index.php?contents=codecrafter/codecraft)。该网站声称:“CodeCrafter 将会帮助你在仅仅几秒钟内生成你的全部 CodeIgniter 应用程序。”它有一个 26 页的在线手册,该手册向你展示了如何使用它的接口去生成 CI 代码。与大多数其他方法不同的评论如下:它使用图形化的界面为你构建 CI 代码,而不是提供供你调用的类库或代码。
在 SuperModel 作者的一篇评论中阐述了编写这类代码所面临的困难以及用户所承担的风险。他说:“请注意这个类库是一个正在开发的产品。我目前正在做很多修改,包括对 API 的修改,这些修改将不兼容于旧的程序。在我写此文(2006年5月30日)的同时,我正在努力实现一对多和多对多的关联查询。不幸的是,这个类库迫使你以某种方式来工作。我试图让它尽可能的小巧灵活,但是同时,在小巧灵活和复杂臃肿之间必须找到一个平衡点,以上就是其作为一个外部第三方类库出现的原因—你可以自由的编写你需要的模型,或者使用其他类似功能的第三方类库。”
有许多关于 PHP 的好书,包括《PHP Programming with PEAR》,作者是 Carsten Lucke、Aaron Wormus、Stoyan Stefanov 和 Stephan Schmidt,出版社是 Packt。
+
在你自己的机器上运行本地 Web 服务器,尝试看一下 http://www.apachefriends.org/en/index.html—一个免费提供 XAMPP 包下载的站点。它将安装一个 Apache Web 服务器、MySQL、PHP 和 Perl。如果 XAMPP 包对你来说太全面,请尝试这个站点上的 Minixampp,本书的代码就是在这个环境下编写的。
+
MySQL 同样拥有自己的网页— http://www.mysql.com/ —不过,如果你想免费下载最新版本,请到 http://dev.mysql.com/。(请记住虽然很多 ISP 都没有使用最新的版本。虽然 MySQL 的最新版本是 5,但是大多数 ISP 仍旧使用版本 4。这阻止了你使用某些更有趣的新特性,例如存储过程。)更多内容请参考《Creating your MySQL Database: Practical Design Tips and Techniques》, 作者是 Marc Delisle,出版社是 Packt。
+
虽然 MySQL 有它自己的工具,但是最流行(也是最常见的)的工具是 PHPMyAdmin。(更多内容请参考《Mastering phpMyAdmin 2.8 for Effective MySQL Management》,作者也是 Marc Delisle,出版社是 Packt。)
+
+
15.3 总结
+
在本章中,我们为你展示了一些当你开始用 CI 编程时要用到的资源。有很多现成的代码可以使用,你需要在使用之前好好的看看它们:不要一看到满足你需要的插件或类库就直接开始使用。你需要好好的研究这些代码,看看他们是如何工作的,这对你整体把握代码很有帮助,并且有利于你更好的理解它。不管怎样,只要你决定使用 CI 框架,你就能找到各种不同层次和复杂程度的类库,它们将完成很多要由你手工完成的任务。
+
我们详细介绍了这些类库:
+
+
AJAX 和 JavaScript
+
身份验证
+
图表
+
CRUD
+
+
最后,我们介绍了一些关于 PHP、MySQL 和运行一个本地 Web 服务器所需要的资源。
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/2.html b/2.html
new file mode 100644
index 0000000..f37d756
--- /dev/null
+++ b/2.html
@@ -0,0 +1,106 @@
+
+
+
+
+第二章 2 分钟:建立一个 CodeIgniter 网站
+
+
+
+
+
在 system 文件夹中的文件是 CI 本身的代码。(system/libraries、system/codeigniter、system/drivers 等)。如果你愿意,你可以研读它们,或者改变他们—不过要等到你了解了 CI 是如何工作的,才能这么做。而且如果你改变了框架内的代码,记住,当你下载了 CodeIgniter 新版本时,备份它们,否则,新的版本会覆盖它们。当然,你也可能不需要自己修改代码而直接使用 CI 本身的代码,Rick 写的代码应该是很不错的。
/*
+|------------------------------------------------
+| Base Site URL
+|------------------------------------------------
+|
+| URL to your Code Igniter root. Typically this
+| will be your base URL, WITH a trailing slash:
+|
+| http://www.your-site.com/
+|
+*/
+$config['base_url'] = "http://127.0.0.1/";
+/*
+
+
+
注意 CI 的注释多详尽啊!
+
修改引号中的数据以匹配你网站的根目录。如有疑问,请查询在线手册以得到详细指导。
+
作为一项基本的原则,使用 config.php 文件储存关于你网站的信息好过散布在你项目的不同文件中。这样做有几个好处:首先,更新比较容易;其次,当你把项目从开发服务器转移到实际存放的服务器时,修改配置较容易;最后,许多 CI 函数会首先在配置文件中寻找需要的信息。
<html>
+<head>
+<title>Welcome to Code Igniter</title>
+<styletype="text/css">
+ body
+ {
+ background-color: #fff;
+ margin: 40px;
+ font-family: Lucida Grande, Verdana, Sans-serif;
+ font-size: 14px;
+ color: #4F5155;
+ }
+. . . . . more style information here . . . .
+</style>
+</head>
+<body>
+<h1>Welcome to Code Igniter!</h1>
+<p>The page you are looking at is being generated dynamically by Code Igniter.</p>
+<p>If you would like to edit this page you'll find it located at:</p>
+<code>system/application/views/welcome_message.php</code>
+<p>The corresponding controller for this page is found at:</p>
+<code>system/application/controllers/welcome.php</code>
+<p>If you are exploring Code Igniter for the very first time, you should start by reading the <ahref="user_guide/">User Guide</a>.</p>
+</body>
+</html>
以我的经验来看, 最困难的事情之一就是把新的CI网站连接到数据库。你可能需要查询你的ISP。有时他们的数据库运行在与他们的web server IP地址不同的地方。如果你正在使用 MySQL,他们可能提供 phpMyAdmin,通常告诉你hostname-这可能是 'localhost' 或者它可能是一个 IP 地址。
使用AR的潜在好处是已经进行了自动的转义,因此,你不必关心转义的问题。这适用于这样的函数像$this->db->where(),以及在下一个段中被描述的数据插入和修改语句。(安全警告:这不同于阻止交叉脚本攻击-对付这个你需要CI的xss_clean()函数。它也不相同于验证你的数据-对付这个你需要 CI 的验证类,见第五章。)
function index()
+{
+ $data['mytitle'] = "A website monitoring tool";
+ $data['mytext'] = "This website helps you to keep track of the other websites you control.";
+}
+
+
注意我们并没有把它们定义为单独的变量,而是作为数组$data的元素。对于第一个元素, 键名是 'mytitle',值是 "A website monitoring tool"。
function index()
+{
+ $data['mytitle'] = "A website monitoring tool";
+ $data['mytext'] = "This website helps you to keep track of the other websites you control.";
+ $this->load->view('basic_view', $data);
+}
<?php
+class Display extends Model {
+
+/*create the array to pass to the views*/
+ var$data = array();
+/*two other class variables*/
+ var$base;
+ var$status = '';
+/*the constructor function: this calls the 'model' parent class, loads
+other CI libraries and helpers it requires, and dynamically sets
+variables*/
+ function Display()
+ {
+ parent::Model();
+ $this->load->helper('form');
+ $this->load->library('user_agent');
+ $this->load->library('errors');
+ $this->load->library('menu');
+ $this->load->library('session');
+/*now set the standard parts of the array*/
+ $this->data['css'] = $this->config->item('css');
+ $this->data['base'] = $this->config->item('base_url');
+ $this->base = $this->config->item('base_url');
+ $this->data['myrobots'] = '<meta name="robots" content="noindex,nofollow" />';
+/*note that CI's session stuff doesn't automatically recall the extra
+variables you have added, so you have to look up the user's status in
+the ci_sessions table*/
+ $sessionid = $this->session->userdata('session_id');
+ $this->db->select('status');
+ $this->db->where('session_id', $sessionid);
+ $query = $this->db->get('ci_sessions');
+ if($query->num_rows() > 0)
+ {
+ $row = $query->row();
+ $this->status = $row->status;
+ }
+ }
+/*function to assemble a standard page. Any controller can call this.
+Just supply as $mydata an array, of key/value pairs for the contents
+you want the view to display. Available variables in this view are:
+mytitle. menu, mytext, diagnostic
+*/
+ function mainpage($mydata)
+ {
+ $this->data['mytitle'] = 'Monitoring website';
+ $this->data['diagnostic'] = $diagnostic;
+ foreach($mydataas$key => $variable)
+ {
+ $this->data[$key] = $variable;
+ }
+/*here's the menu class we looked at in Chapter 3*/
+ $fred = new menu;
+ $this->load->library('session');
+ $mysess = $this->session->userdata('session_id');
+ if(isset($this->status) && $this->status > 0)
+ {
+ $this->data['menu'] = $fred->show_menu($this->status);
+ }
+ $this->load->view('basic_view', $this->data);
+ }
+}
+?>
/*receives the username and password fromthe POST array*/
+ function assessme(){
+ $username = $_POST['username'];
+ $password = $_POST['password'];
+
+ /*calls the checkme function to see if the inputs are OK*/
+ if($this->checkme($username,$password)=='yes'){
+ /*if the inputs are OK, calls the mainpage function */
+ $this->mainpage;()
+ }
+ /*if they are not OK, goes back to the index function, which re-presents the log-in screen */
+ else{
+ $this->index();
+ }
+ }
+ /*called with a u/n and p/w, this checks them against some list. For the moment, there's just one option. Returns 'yes' or 'no' */
+
+ function checkme($username='', $password=''){
+ if($username == 'fred' && $password =='12345'){
+ return'yes';
+ }else{
+ return('no';
+ }
+ }
CI 有一个 Session 类来处理会话相关工作。事实上,它大大减少了编码量。我们在上一章学习到CI有各种各样的library,大大简化了PHP的编程。这些就是框架的核心:集成大量代码,为你提供各种强大的功能。你要做的只是使用它们,而不必关心他们是如何运作的。作为结果,你的应用使用了最专门的代码,而你却不需要自己去编写它们!
/*--------------------------------------------------------------------------
+| Session Variables
+|--------------------------------------------------------------------------
+|
+| 'session_cookie_name' = the name you want for the cookie
+| 'encrypt_sess_cookie' = TRUE/FALSE (boolean). Whether to encrypt the cookie
+| 'session_expiration' = the number of SECONDS you want the session to last.
+| by default sessions last 7200 seconds (two hours). Set to zero for no expiration.
+|
+*/
+$config['sess_cookie_name'] = 'ci_session';
+$config['sess_expiration'] = 7200;
+$config['sess_encrypt_cookie'] = FALSE;
+$config['sess_use_database'] = FALSE;
+$config['sess_table_name'] = 'ci_sessions';
+$config['sess_match_ip'] = FALSE;
+$config['sess_match_useragent'] = FALSE;
/*remember to load the library!*/
+ $this->load->library('session');
+
+ /*look for a 'status' variable in the contents of the session cookie*/
+ $status = $this->session->userdata('status');
+
+ /*if it's not there, make the user log in*/
+ if (!isset($status) || $status != 'OK')
+ {/*function to present a login page again...*/}
+
+ /*otherwise, go ahead with the code*/
$fred = $this->agent->is_robot();
+if($fred == TRUE)
+ {$agent = $this->agent->agent_string();
+/*add code here to store or analyse the name the user agent is returning*/
+}
/*remember to load the library!*/
+ $this->load->library('session');
+/*look for an 'ip_address' variable in the contents of the session cookie*/
+$ip = $this->session->userdata('ip_address');
当我刚开始使用CodeIgniter的时候,对象使我迷惑。我是在使用PHP4的时候接触CI的,PHP4并不是真正的面向对象(OO)语言。我在一大堆的对象和方法,属性和继承,还有封装等数据结构中转悠,总是被类似的出错信息包围“Call to a member function on a non-object”。我如此频繁地看到它们,因此我想到要印一件T恤衫,上写:神秘,无规律可循,而我仿佛正穿着它站在一个现代艺术展会的会场上。
class my_new_class{
+var$base;
+My_new_class()
+{
+$obj =& get_instance();
+// geolocation code here, returning a value through a switch statement
+//this value is assigned to $local_url
+$this->base = $obj->config->item('base_url');
+$this->base .= $local_url;
+}
+
+
如果你不清楚这些概念,“Call to a member function on a non-object”就会频繁的出现。上例中,如果你试着调用 $obj->base或 $this->config->item() 时,这个出错信息就出现了。
/*
+|---------------------------------------------------------------
+| PHP ERROR REPORTING LEVEL
+|---------------------------------------------------------------
+|
+| By default CI runs with error reporting set to ALL. For security
+| reasons you are encouraged to change this when your site goes live.
+| For more info visit: http://www.php.net/error_reporting
+|
+*/
+ error_reporting(E_ALL);
/*
+|--------------------------------------------------------------------------
+| Error Logging Threshold
+|------------------------------------------------------------------------
+|
+| If you have enabled error logging, you can set an error threshold to
+| determine what gets logged. Threshold options are:
+|
+| 0 = Disables logging
+| 0 = Error logging TURNED OFF
+| 1 = Error Messages (including PHP errors)
+| 2 = Debug Messages
+| 3 = Informational Messages
+| 4 = All Messages
+|
+| For a live site you'll usually only enable Errors (1) to be logged
+| otherwise your log files will fill up very fast.
+|
+*/
+$config['log_threshold'] = 4;
if($test == 'yes')
+{
+ $place = __FILE__.__LINE__;
+ $dbvalue = "exception at $place: sent state value $state to trydelete function ";
+ return$dbvalue;
+}
$test = $this->db->count_all('fred');
+$expected_result = 0;
+$test_name = 'tests number of entries left in table after unique entry removed';
+$this->unit->run($test, $expected_result, $test_name);
Your message has been successfully sent using the following protocol: mail
+User-Agent: Code Igniter
+Date: Wed, 18 Apr 2007 13:50:41 +0100
+From:
+Return-Path:
+Bcc: fred@somewhere.com
+Reply-To: "david@mysite.com"
+X-Sender: david@mysie.com
+X-Mailer: Code Igniter
+X-Priority: 3 (Normal)
+Message-ID: <462614219c1a6@upton.cc>
+Mime-Version: 1.0
+Content-Type: multipart/mixed; boundary="B_ATC_462614219d14d"
+This is a multi-part message in MIME format.
+Your email application may not support this format.
+--B_ATC_462614219d14d
+Content-Type: text/plain; charset=utf-8
+Content-Transfer-Encoding: 8bit
+test message
+hello world
+--B_ATC_462614219d14d
+Content-type: text/html; name="myfile.html"
+Content-Disposition: attachment;
+Content-Transfer-Encoding: base64
+
diff --git a/CodeIgniter.hhp b/CodeIgniter.hhp
new file mode 100644
index 0000000..e72a710
--- /dev/null
+++ b/CodeIgniter.hhp
@@ -0,0 +1,48 @@
+
+[OPTIONS]
+Binary TOC=Yes
+Compatibility=1.1 or later
+Compiled file=CodeIgniter.chm
+Contents file=CodeIgniter.hhc
+Default Font=宋体, 9,134
+Default Window=Main
+Default topic=index.html
+Display compile notes=No
+Display compile progress=Yes
+Error log file=_errorlog.txt
+Full-text search=Yes
+Index file=CodeIgniter.hhk
+Language=0x804 中文(简体,中国)
+Title=PHP 敏捷开发框架 CodeIgniter - 快速 Web 应用开发详解 【更新日期: 2009.1.19】
+
+[WINDOWS]
+Main="PHP 敏捷开发框架 CodeIgniter - 快速 Web 应用开发详解 【更新日期: 2009.1.19】","CodeIgniter.hhc","CodeIgniter.hhk","index.html","table_of_contents.html",,,,,0x71520,180,0x70387e,[0,0,708,727],0xb0000,,,,,,0
+
+
+[FILES]
+1.html
+2.html
+3.html
+4.html
+5.html
+6.html
+7.html
+8.html
+9.html
+10.html
+11.html
+12.html
+13.html
+14.html
+15.html
+about_the_author.html
+about_the_reviewers.html
+credits.html
+credits_cn.html
+index.html
+preface.html
+preface_cn.html
+table_of_contents.html
+
+[INFOTYPES]
+
diff --git a/README.md b/README.md
index 1c6d342..59f1184 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,30 @@
-rapid-php-application-development
+鍥句功锛氥奀odeIgniter for Rapid PHP Application Development銆嬬炕璇戦」鐩
=================================
-鍥句功锛氥奀odeIgniter for Rapid PHP Application Development銆嬬炕璇戦」鐩
+鍘熶功鍚嶏細銆奀odeIgniter for Rapid PHP Application Development銆
+
+涓枃鍚嶏細銆奝HP 鏁忔嵎寮鍙戞鏋 CodeIgniter - 蹇 Web 搴旂敤寮鍙戣瑙c
+
+
+
+缁忚繃绀惧尯寰堝鏈嬪弸鐨勫叡鍚屽姫鍔涳紝缁堜簬鍦ㄦ槬鑺傚墠鎺ㄥ嚭浜嗚繖鏈ぇ瀹舵湡寰呭凡涔呯殑涔︼紝甯屾湜澶у澶氬鎵惧嚭涔﹂噷鐨勯敊璇紝鍖呮嫭璇硶銆佺炕璇戙侀敊鍒瓧绛夊悇涓柟闈紝寤鸿涓缁忛噰绾筹紝灏嗕細璁板綍鍦ㄥ埗浣滆呭悕鍗曚腑銆
+
+鎴戜滑瑕佺壒鍒劅璋 鏉庡旦娉(鍘熸柊娴狢TO) 鍏堢敓鍦ㄧ櫨蹇欎箣涓负鏈功浣滃簭锛屼篃瑕佹劅璋 chenz1117 瀵逛簬缈昏瘧宸ヤ綔鎵浣滃嚭鐨勫法澶ц础鐚紝杩樺寘鎷钵钃濈瓑鏈嬪弸瀵圭炕璇戞妧鏈笂鐨勯棶棰樻墍缁欎簣鐨勫法澶у府鍔╋紝杩樺寘鎷澶氫负鏈功鍋氳繃璐$尞鐨勬湅鍙嬶紝鍦ㄨ繖閲屽氨涓嶄竴涓鍒椾妇浜嗭紝涔︿腑鏈夎缁嗙殑鍒朵綔鑰呭悕鍗曪紝璁╂垜浠啀娆″浠栦滑琛ㄧず琛峰績鐨勬劅璋紒
+
+鏈功鏄涓鏈缁嗕粙缁 CodeIgniter 妗嗘灦鐨勮憲浣滐紙鐩墠宸茬粡鏈変簡鍙﹀鍑犳湰浠嬬粛 CI 鐨勮憲浣滀簡锛夛紝涓嬮潰鏄湰涔︾殑涓涓畝鐭粙缁嶏細
+
+> 鏈功璇︾粏璁茶В浜 CI 鐨勪竴浜涗富瑕佺壒鎬с傛湰涔﹀苟涓嶅寘鍚 CI 鐨勬墍鏈夊唴瀹瑰拰鍏ㄩ儴缁嗚妭銆侰I 鏈変竴鏈嚭鑹茬殑鍦ㄧ嚎銆婄敤鎴锋寚鍗椼嬶紝瀹冭缁嗚瑙d簡澶у鏁扮殑鍐呭銆傚畠鍙互涓 CI 涓璧蜂笅杞姐
+
+> 鏈功骞朵笉鎯抽噸澶嶃婄敤鎴锋寚鍗椼嬩腑鐨勫唴瀹广傜浉鍙嶏紝鏈功璇曞浘璁╀綘杞绘澗浜嗚В CI 妗嗘灦鏄浣曞伐浣滅殑锛岄偅涔堬紝浣犲彲浠ュ厛鍐冲畾瀹冩槸鍚﹀浣犳湁浠峰硷紝鐒跺悗鍐嶉槄璇绘湰涔︺
+
+> 鍦ㄨ瘯鍥捐В閲 CI 鏄浣曞伐浣滄椂锛屾湰涔︾殑鏌愪簺鍐呭宸茬粡瓒呭嚭浜嗐婄敤鎴锋寚鍗椼嬬殑鑼冨洿銆傦紙銆婄敤鎴锋寚鍗椼嬫洿娉ㄩ噸瀹為檯搴旂敤銆傦級杩欐剰鍛崇潃鍦ㄢ滃疄鎴樿缁冣濅腑鏈変竴浜涢潪甯哥悊璁哄寲鐨勭珷鑺傘傛垜鍙戠幇杩欐湁鍔╀簬鐞嗚В CI 鍐呴儴鐨勮繍琛屾満鍒讹紱鍚﹀垯锛屽綋浣犻亣鍒颁护浜鸿垂瑙g殑閿欒娑堟伅鏃跺氨涓嶅鏄撹В鍐炽
+
+> 鎴戝皾璇曞湪灞曠ず CI 浠g爜娈垫椂浣跨敤涓涓滅湡瀹炰笘鐣屸濈殑渚嬪瓙銆傛垜鎯冲睍绀虹殑鏄紝CI 鍙互鐢ㄤ簬寮鍙戜竴涓寮忕殑缃戠珯銆傜洰鍓嶏紝鎴戞墜澶翠笂鏈夊嚑涓鍦ㄨ繍琛岀殑瀹㈡埛缃戠珯锛屾垜甯屾湜渚濈収鎴戞寚瀹氱殑鏂瑰紡鍘诲鍏惰繘琛屾娴嬫帶鍒朵互鍙婃祴璇曪紝鍚屾椂璁板綍涓嬬▼搴忔搷浣滆涓猴紝鍦ㄦ垜闇瑕佹椂鎴戝彲浠ュ緱鍒颁竴浠界浉鍏崇殑鎶ュ憡銆
+
+> 鏈功涓殑鑼冧緥鏃犳硶灏 CI 鐨勫姛鑳戒竴涓濅笉婕忕殑瀹屽叏灞曠ず锛屼絾鎴戞兂杩欎簺鑼冧緥搴斿綋杩樻槸鍦ㄤ竴瀹氱▼搴︿笂灞曠幇浜 CI 鍦ㄧ畝鍖栧鐞嗗父鐢ㄥ簲鐢紙浠ュ強涓浜涢潪甯哥敤搴旂敤锛変笂鐨勮兘鍔涖
+
+> 鏈功绯荤粺鍦拌瑙d簡 CodeIgniter 鐨勪富瑕佺壒鎬э紝骞堕厤鍚堢浉搴旂殑浠g爜鑼冧緥杩涜浜嗚灏界殑瑙i噴锛屼娇浣犺兘澶熺敱娴呭叆娣卞湴鎺屾彙 CodeIgniter銆
+
+鏈鍚庯紝鎰熻阿绀惧尯鎵鏈夋湅鍙嬬殑澶у姏鏀寔涓庡叧蹇冿紒
+
+鐩稿叧閾炬帴: http://codeigniter.org.cn/forums/thread-1464-1-1.html
diff --git a/_code/Chapter 11/Do Upload.php b/_code/Chapter 11/Do Upload.php
new file mode 100644
index 0000000..5c0c6ad
--- /dev/null
+++ b/_code/Chapter 11/Do Upload.php
@@ -0,0 +1,31 @@
+load->helper(array('form', 'url'));
+ $this->load->library('upload');
+ }
+
+/*now the function which does all the work!*/
+
+function do_upload()
+ {
+
+ if ( ! $this->upload->do_upload())
+ {
+ $error = array('error' => $this->upload->display_errors());
+
+ $this->load->view('upload_form', $error);
+ }
+ else
+ {
+ $data = array('upload_data' => $this->upload->data());
+
+ $this->load->view('upload_success', $data);
+ }
+ }
+}
diff --git a/_code/Chapter 12/Function Report me.txt b/_code/Chapter 12/Function Report me.txt
new file mode 100644
index 0000000..e5d6a1f
--- /dev/null
+++ b/_code/Chapter 12/Function Report me.txt
@@ -0,0 +1,32 @@
+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);
+ }
+ }
diff --git a/_code/Chapter 13/Controller_for_sites_table.php b/_code/Chapter 13/Controller_for_sites_table.php
new file mode 100644
index 0000000..524804d
--- /dev/null
+++ b/_code/Chapter 13/Controller_for_sites_table.php
@@ -0,0 +1,55 @@
+load->model('crud');
+ }
+
+/*function to update an entry (if an ID is sent) or to insert a new one. Also includes validation, courtesy of CI */
+ function insert($id)
+ {
+ $this->crud->insert($this->controller, $id);
+ }
+
+/*interim function to pass post data from an update or insert through to Crud model, which can't receive it directly*/
+ function interim()
+ {
+ $this->crud->insert2($this->controller, $_POST);
+ }
+
+/*function to delete an entry, needs table name and id. If called directly, needs parameters passed to function; if not, from Post array*/
+ function delete($idno=0, $state='no')
+ {
+ if(isset($_POST['id'])&& $_POST['id'] > 0)
+ {$idno = $_POST['id'];}
+ if(isset($_POST['submit']))
+ {$state = $_POST['submit'];}
+ $this->crud->delete($this->controller, $idno, $state);
+ }
+
+/*function to show all entries for a table*/
+ function showall()
+ {
+ $this->crud->showall($this->controller, $message);
+ }
+
+
+/*function to show all data in a table, but doesn't allow any alterations*/
+ function read()
+ {
+ $this->crud->read($this->controller);
+ }
+
+/*function to set off the test suite on the 'crud' model. This function need only appear in one controller, as these tests are made on a temporary test table so that your real data is not affected*/
+ function test()
+ {
+ $this->crud->test();
+ }
+}
+?>
diff --git a/_code/Chapter 13/delete_function.txt b/_code/Chapter 13/delete_function.txt
new file mode 100644
index 0000000..c19986e
--- /dev/null
+++ b/_code/Chapter 13/delete_function.txt
@@ -0,0 +1,72 @@
+/*DELETE FUNCTION: given table name and id number, deletes an entry*/
+ function delete($controller, $idno, $state='no', $test='no')
+ {
+/*first check that the 'yes' flag is set. If not, go through the trydelete function to give them a chance to change their minds*/
+ if(!isset($state) || $state != 'yes')
+ {
+/*test block: are 'yes' flags recognised?*/
+ if($test == 'yes')
+ {
+ $place = __FILE__.__LINE__;
+ $outcome = "exception at $place: sent state value $state to trydelete function ";
+ return $outcome;
+ }
+ else
+/*end test block*/
+ {$this->trydelete($controller, $idno, 'no');}
+ }
+ else{
+/*'yes' flag is set, so now make sure there is an id number*/
+ if(isset($idno) && $idno > 0 && is_int($idno))
+/*test block: with this id no, am I going to do a delete?*/
+ {
+ if($test == 'yes')
+ {
+ $place = __FILE__.__LINE__;
+ $outcome = "OK at $place: doing delete on id of $idno ";
+ return $outcome;
+ }
+ else{
+/*end test block*/
+/*if there is an id number, do the delete*/
+
+ $this->db->where('id', $idno);
+ $this->db->delete($controller);
+ $changes = $this->db->affected_rows();
+ }
+
+ if($changes != 1)
+ {
+/*test block: did I actually do a delete? */
+ $place = __FILE__.__LINE__;
+ $outcome = "exception at $place: cdnt do delete op on $controller with id no of $idno";
+ if($test == 'yes')
+ {return $outcome;}
+ else
+/*end test block*/
+/*if there was no update, report it*/
+ {$this->failure($outcome);}
+ }
+ else{
+/*test block: I did do a delete*/
+ if($test == 'yes')
+ {return 'OK';}
+ else{
+/*end test block: report the delete*/
+ $this->showall($controller, "Entry no. $idno deleted.");}
+ }
+ }
+ else
+ /*test block: report id number wasn't acceptable'*/
+ {
+ $place = __FILE__.__LINE__;
+ $outcome = "exception at: $place : id no of $idno set for delete op in $controller, expecting integer";
+
+ if($test == 'yes')
+ {return $outcome;}
+ else
+/*endtest block: if I failed, report me*/
+ {$this->failure($outcome);}
+ }
+ }
+ }
diff --git a/_code/Chapter 13/insert2_function.txt b/_code/Chapter 13/insert2_function.txt
new file mode 100644
index 0000000..5311c5b
--- /dev/null
+++ b/_code/Chapter 13/insert2_function.txt
@@ -0,0 +1,62 @@
+ function insert2($controller, $newpost, $test = 'no')
+ {
+ $myform = '';
+
+/*test the incoming parameters*/
+ if(!$this->db->table_exists($controller))
+ {
+//test here!
+ }
+
+ $this->load->library('validation');
+
+/*handle the validation. Note that the validation class works from the post array, whereas this function only has a $newpost array: same data, but different name. So we re-created the $_POST array.
+*/
+ $_POST = $newpost;
+
+/*now build up the validation rules from the entries in our master array*/
+ $errorform = '';
+ $newtemparray = $this->form[$controller];
+ foreach($newtemparray as $key => $value)
+ {$rules[$key]= $value[2];}
+ $this->validation->set_rules($rules);
+
+/*and the name fields*/
+ foreach($newtemparray as $key => $value)
+ {$fields[$key]= $value[0];}
+ $this->validation->set_fields($fields);
+
+
+
+ $this->validation->set_fields($fields);
+
+/*now do the validation run*/
+ if ($this->validation->run() == FALSE)
+ {
+/*if the validation run fails, re-present the entry form by calling the 'insert' function*/
+ $id = $_POST['id'];
+ $this->insert($controller, $id, 'no', $_POST);
+
+ }
+ else
+ {
+/*The validation check was OK so we carry on. Check if there is an id number*/
+ if(isset($_POST['id']) && $_POST['id'] > 0)
+ {
+/*if yes: this is an update, so you don't want the id number in the post array because it will confuse the autoincrement id field in the database. Remove it, but save it in $tempid to use in the 'where' condition of the update query, then do the update*/
+ $tempid = $_POST['id'];
+ unset($_POST['id']);
+ $this->db->where('id', $tempid);
+ $this->db->update($controller, $_POST);
+ if($this->db->affected_rows()== 1)
+ {$this->showall($controller, "Entry number $tempid updated.");}
+ else{$this->failure("Failed to update $controller for id no $tempid", __FILE__,__LINE__);}
+
+/*if no id number, we assume this is a new entry: no need to unset the post array id as it isn't there! the database will create its own id number. Do the new entry*/
+ $this->db->insert($controller, $_POST);
+ if($this->db->affected_rows()== 1)
+ {$this->showall($controller, "New entry added.");}
+ else{$this->failure("Failed to make new entry in $controller ", __FILE__,__LINE__);}
+ }
+ }
+ }
diff --git a/_code/Chapter 13/insert_function.txt b/_code/Chapter 13/insert_function.txt
new file mode 100644
index 0000000..1a11815
--- /dev/null
+++ b/_code/Chapter 13/insert_function.txt
@@ -0,0 +1,266 @@
+/*the most complex function. This creates an HTML form, based on the description of the fields in the form array. This is sent to our display model, which sets up a view and shows it to the user.
+The view then sends a POST array back to the controller. The form can't call this model directly, so it has to call the controller, which refers it back to the model.
+Note the function parameters:
+1. The controller parameter is whichever controller/ table has called the model - eg the 'sites' controller, or the 'domains' controller. The controller has the same name as the table it manipulates.
+2. The optional id parameter is the id of an individual entry in that table.
+3. The optional 'test' parameter is so you can set the form up to make usable responses to self-test functions.
+*/
+
+ function insert($controller='', $id=0, $test='no')
+ {
+ $myform = '';
+ $myid = 0;
+ $currentvalue = array();
+
+/*test if the table exists*/
+ if(!$this->db->table_exists($controller))
+ {
+ $place = __FILE__.__LINE__;
+ $outcome = "exception: $place:looking for table $controller: it doesn't exist'";
+ if($test =='yes')
+ {
+ return $outcome;
+ }
+ else{
+ $this->failure($outcome, $controller);
+ }
+ }
+ else
+ {
+ if($test =='yes')
+ {
+ return 'OK';
+ }
+ }
+/*end test block*/
+
+/*first check if there is an id number. If there is, we need to get the values to populate the table fields*/
+ if(isset($id) && $id > 0)
+ {$myid = $id;
+ $this->db->where('id', $id);
+ $query = $this->db->get($controller);
+ if ($query->num_rows() > 0)
+ {
+ $row = $query->row();
+//--------------work out the values we want!
+ foreach($row as $key =>$value)
+/*
+first of all work out what value you want to show as the existing value in each line of the form. In priority order these are:
+1. the last value the user entered, from the post array
+2. the value from the database
+3. nothing, if neither of these is set.
+if we got here, the id does exist and is returning values, so get the existing values into a value array. Or, if there is something in the validation array, use that instead*/
+ {
+ $_POST[$key] = $this->validation->$key;
+ if(isset($_POST[$key]))
+ {$currentvalue[$key] = $_POST[$key];}
+ else
+ {$currentvalue[$key] = $value;}
+ }
+
+/*test block: there was an id number, so has the programme gone for an update? if this is not a test, of course, just do the update*/
+ if($test == 'yes')
+ {
+ $place = __FILE__.__LINE__;
+ $outcome = "exception: $place: id of $id returned results from $controller table so have gone for update";
+ return $outcome;
+ }
+
+/*end test block*/
+ $myform .= "
Update existing entry number $id
";
+ }
+/*now catch situation where this id number isn't returning results. In this instance this is an error, since it shdn't have got here with a false ID'.*/
+ else{
+ $place = __FILE__.__LINE__;
+ $outcome = "exception: $place: despite id of $id cant get any results from $controller table";
+
+ if($test == 'yes')
+/*test block: there was and ID but there were no results*/
+ {
+ return $outcome;
+ }
+/*end test block*/
+ else
+ {$this->failure($outcome, $controller);}
+
+ }
+ }
+
+/*there was no ID number, so this is a new entry*/
+ else{
+/*If the user has filled in values, and has returned here because some of them didn't validate, we still need to repopulate the form with what he entered, so he only has to alter the one that didn't validate. Get these from the post array*/
+
+ if(isset($_POST))
+ {
+ foreach($_POST as $key => $value)
+ {
+ if(isset($_POST[$key]))
+ {$currentvalue[$key] = $_POST[$key];}
+ }
+
+ }
+ $myform .= "
New entry
";
+
+/*test block: there was no ID, so this is a new entry*/
+ if($test == 'yes')
+ {
+ $place = __FILE__.__LINE__;
+ $outcome = "exception: $place: id of $id treated as no id, so going for new entry";
+ return $outcome;
+ }
+/*end test block*/
+ }
+
+
+/*the table exists, whether this is an update or new entry, so start to build the form*/
+ $myform .= "
';
+ $myform .= $this->validation->error_string;
+
+
+/*the rest of this function is common to inserts or update.
+Look up in the form array which form field type you want to display, and then build up the html for each different type, as well as inserting the values you want it to echo.*/
+
+
+
+ foreach($this->form[$controller] as $key => $value)
+ {
+
+/*This switch statement develops several types of HTML form field based on information in the form array.
+It doesn't yet cover checkboxes or radio or password fields. It adds a 'readonly' type, which is a field that only displays a value and doesn't let the user modify it*/
+
+ $fieldtype = $value[1];
+ $val_string = $this->validation->$key;
+ switch($value[1])
+ {
+/*a simple input line*/
+ case 'input':
+ $data = array(
+ 'name' => $key,
+ 'id' => $key,
+ 'value' => $currentvalue[$key],
+ 'maxlength' => '100',
+ 'size' => '50',
+ 'style' => 'width:50%',
+ );
+ $myform .= "
";
+ break;
+
+ case 'dropdown':
+/*a drop down box. Values are dynamically generated from whichever table was specified in the forms array. This table must have an id field (which is now entered in the form) and a name field (which is displayed in the drop-down box).*/
+ $dropbox = array();
+ if(isset($value[3]))
+ {
+ $temptable = $value[3];
+ $this->db->select('id, name');
+ $query = $this->db->get($temptable);
+ if ($query->num_rows() > 0)
+ {
+ foreach ($query->result() as $row)
+ {
+ $dropbox[$row->id] = $row->name;
+ }
+ }
+ }
+ $myform .= "
";
+
+ break;
+
+ case 'hidden':
+/*generates a hidden field*/
+ $myform .= form_hidden($key, $currentvalue[$key]);
+ break;
+
+ case 'readonly':
+/*generates a field the user can see, but not alter.*/
+
+ $myform .= "
";
+
+ break;
+
+ default:
+ $place = __FILE__.__LINE__;
+ $outcome = "exception: $place: switch can't handle $fieldtype";
+/*test block: what if the switch doesn't recognise the form type?'*/
+ if($test == 'second')
+ {
+ return $outcome;
+ }
+/*test block ends*/
+ else {
+
+ $this->failure($outcome, $controller);
+ }
+ }
+/*end the foreach loop which generates the form*/
+ }
+ $myform .= form_hidden('submit',$time);
+ $myform .= form_close();
+ $myform .= "
";
+
+/*Finally we've built our form and populated it! Now, stuff the form in an array variable and send it to the model which builds up the rest of the view.*/
+ $data['text'] = $myform;
+ $this->display->mainpage($data);
+}
diff --git a/_code/Chapter 13/showall_function.txt b/_code/Chapter 13/showall_function.txt
new file mode 100644
index 0000000..a7231aa
--- /dev/null
+++ b/_code/Chapter 13/showall_function.txt
@@ -0,0 +1,70 @@
+ /*this function lists all the entries in a database table on one page. Note that every db table must have an 'id' field and a 'name' field to display!
+This page is a jumping-off point for the other functions - ie to create, read, update or delete an entry.
+When you've done any of these, you are returned to this page. It has a 'message' parameter, so you can return with a message - either success or failure.*/
+
+function showall($controller='', $message = '', $test ='no')
+ {
+ $result = '';
+ $mysess = $this->session->userdata('session_id');
+ $mystat = $this->session->userdata('status');
+ if(!$this->db->table_exists($controller))
+ {
+ $place = __FILE__.__LINE__;
+ $outcome = "exception:$place:looking for table $controller: it doesn't exist'";
+/*test block: what if there is no controller by that name?*/
+ if($test =='yes')
+ {
+ return $outcome;
+ }
+ else{
+ $this->failure($outcome, 'sites');
+ }
+ }
+/*end test block*/
+ $this->db->select('id, name');
+ $query = $this->db->get($controller);
+ if ($query->num_rows() > 0)
+ {
+ $result .= "
";
+
+ $data['text'] = $result;
+ $this->display->mainpage($data, $this->status);
+ }
+ else
+ {$place = __FILE__.__LINE__;
+ $outcome = "exception: $place: no results from table $controller";
+/*test block: were there results from this table/ controller?*/
+ if($test == 'yes')
+ {$place = __FILE__.__LINE__;
+ return $outcome;
+ }
+/*end test block*/
+ else{
+ $message = "No data in the $controller table";
+/*note: this specific exception must return to another controller which you know does contain data厖 otherwise, it causes an infinite loop! */
+ $this->failure($message, 'sites');
+ }
+ }
+ }
diff --git a/_code/Chapter 13/test_function.txt b/_code/Chapter 13/test_function.txt
new file mode 100644
index 0000000..c1fc975
--- /dev/null
+++ b/_code/Chapter 13/test_function.txt
@@ -0,0 +1,19 @@
+/*now a suite of self-test functions.*/
+
+/*first function just calls all the others and supplies any formatting you want. Also it builds/ destroys temporary data table before/ after tests on the database.*/
+ function test()
+ {
+ $return = "
');
+
+你不一定要改变每个值: 那些你不改变的键保留为默认值。
+
+现在你的表格神奇地以你新设定的格式显示。
+
+缓存网页
+
+到现在为止,我们正在写一些相当复杂的代码。 服务器必须停下来分析每个动态生成的网页。你可以很简单地编写一个函数象上面的dotable(),但是,可怜的老服务器不得不做更多的工作来生成结果。
+
+有时候,这能导致你的页面显示起来比较慢。 对这点可能没有什么好办法。如果你正在写报告,每次写得都不一样,你只能等待。然而,你可能生成将会保持一段时间内容不变的网页。比如说一篇博客,在你提交下一篇之前,你没有什么变化,如果有一千个读者来看你的博客,每个视图都是一样的,而你却需要浪费时间去动态生成同样的页面,一次又一次。
+
+解决之道是缓存网页。你一次生成网页,然后把生成的HTML文件保存在缓存目录中,加上一个时间戳,然后被显示到用户的浏览器上。然后,当一下个读者请求页面时,系统检查离上次生成和保存有多长的间隔,如果还在你设定的时间范围内,它继续返回缓存页面,如果不是,它将更新页面。
+
+听起来这里像需要一些复杂的编程工作,其实如果你使用CI,你只需要做二件事情:
+
+找到你网站中的/system/cache目录,它应该是空的, 除了一个index.html文件。 确定此目录是可读写的-也就是如果在Linux系统上,权限设为666。
+
+插入, 在一个产生 HTML 页的控制器的函数中加上:
+
+$this->output->cache(5);
+
+5是你想要你的缓存持续的分钟数,即在页面被重新生成前会持续多长时间使用静态HTML文件。
+
+搞定。 如果你现在装载函数,你将会像往常一样见到页面被装载。 如果你现在观察你的 /system/cache目录,当然,你将会在那里里面见到一个新的文件,有一个无意义的名称。
+
+把它在一个文本编辑器里打开,你将会见到它包含 HTML 代码, 加上一个时间戳。如果你请求相同的页,在五分钟之内,你将会得到被缓存的页面。超过这个时间段,你的下一次请求将会自动地删除它而且用一个较新的版本替代它。
+
+如果你不想缓冲页面, 从你的控制器中删除this->output->cache(5)这一行,你的页面将每次刷新。 (最后一个被保存的文件将会留在你的 /system/cache目录,直到你手工删除它。) 如果你想要继续缓冲, 但是偶然地删除一个缓存文件, 不要紧张; 当那一页下一次被调用时,系统将会产生新的。
+
+CI让这一切变得快和简单,它正在试着缓存每一页!只是要记住这一点:要缓存内容不频繁改变的页面,其它情况就不要使用缓存功能。
+
+摘要
+
+CI 提供你许多好东西,使你编程容易并且你的网站更专业。 这一个章节介绍是只是它们中的五个:
+
+。 文本和转换助手提供有用的函数操作并且转换字符串。
+
+。 日期助手让你在不同的日期格式之间转换以及应付时区。
+
+。 语言类使你编写多语言网站比较方便,满足使用者语言偏爱比较容易。 唉, 你仍然必须自己做翻译!
+
+。 表格类让你输出适当的HTML表格, 直接地从一个数据库查询中产生的数据可以输出到漂亮的HTML表格中。
+
+。 自动地缓存高负荷动态网页提供一个较快速的响应。
+
+
+下一章学习
+
+使用 CI 处理文件和图像
\ No newline at end of file
diff --git a/_docs/11.doc b/_docs/11.doc
new file mode 100644
index 0000000..5b63158
Binary files /dev/null and b/_docs/11.doc differ
diff --git a/_docs/11.txt b/_docs/11.txt
new file mode 100644
index 0000000..0e2d411
--- /dev/null
+++ b/_docs/11.txt
@@ -0,0 +1,1101 @@
+使用 CI 处理文件和图片
+
+
+
+ 本章看几个有用的 CI 功能和辅助函数。他们中的每一个都是如何用几行 CI 代码无逢存取一系列应用和动作的好例子,如果从头开始编码的话你需要具备许多专业知识。在许多情况下, CI 提供一个对已有类的简单接口,这些类可能是你从 PEAR 或者其他源代码中提取的。但 CI 给你一个标准接口:你只需把它作为本地 CI 代码,并且框架向你提供所有接口的内容。
+
+ 让我们看看本章中的五个例子:
+
+
+
+
+
+ * 文件辅助函数让我们很容易的读写文件。
+
+ * 下载辅助函数让用户直接下载你网站上的文件,而不是显示他们为 HTML。
+
+ * 文件上传类以其他方式工作,他允许用户存储文件到你的网站,并有内建的安全措施以限制用户的行为。
+
+ * 图像处理类允许你给图片做几个有用的处理,我们将看看如何调整图片的大小和给图片加水印。
+
+ * 最后,Zip 类允许你的用户下载文件之前压缩它。
+
+
+
+
+
+ 这的每一个例子都暗藏着很多聪明的代码实现,并且允许你以最小的代价写出实际的应用。在很多例子中,增加了一些额外的代码以使其更健壮。
+
+ 让我们一个接一个的看看:
+
+
+
+
+
+文件辅助函数
+
+
+
+ 第一次学习 PHP 的读写文件的语法是不容易掌握的。CI 的文件辅助函数包含许多有用的函数,他封装了 PHP 自己的文件处理操作。开始一如既往的装载这个辅助函数:
+
+复制内容到剪贴板
+
+PHP 代码:
+
+
+
+ 1.
+
+ $this->load->helper('file');
+
+
+
+ 然后生活得到了很多简化。例如,写文件,你需要知道的是:
+
+
+
+
+
+ * 你的文件的位置。
+
+ * 你想写入的文本。
+
+ * 你想以什么模式打开文件。模式定义在 PHP 手册中(看 'fopen' 这页). 它们包含 'r' 为读, 'w' 为写(写入文件, 覆盖已存在的数据), 'a' 为追加(写入文件, 在已存在的数据后添加). 每种情况下, 添加一个 '+', 例如 'a+', 打开文件以进行读写操作。'a' 和 'w', 而不是 'r' 或 'r+', 如果没有的话,总是创建文件。
+
+
+
+
+
+ 然后你使用这三个信息作为 write_file() 函数的参数:
+
+复制内容到剪贴板
+
+PHP 代码:
+
+
+
+ 1.
+
+ write_file('e:/filetest.txt', 'hello world', 'a+');
+
+
+
+ 这比 PHP 的两步代码更简单、更直观:
+
+复制内容到剪贴板
+
+PHP 代码:
+
+
+
+ 1.
+
+ if ( $fp = fopen('e:/filetest.txt','r+'))
+
+ 2.
+
+ {
+
+ 3.
+
+ fwrite($fp, 'hello world');
+
+ 4.
+
+ }
+
+
+
+ 其次,CI 代码添加了一点额外功能:它在写入之前自动锁定文件,写入后再解锁。如果没有发生文件操作,则辅助函数返回 'FALSE',所以你可以用它来报告成功或失败。你需要为你的文件指定一个文件名,但如果你不指定文件路径,它将保存在你站点的 web 根目录下,就是你的主 index.php 文件所在的位置。
+
+ 当然,在任何文件夹中你创建或写入文件必须具有写入权限。记住,如果你运行于一个 Windows 系统,你必须使用正斜杠 — /— 来描述你的文件路径。
+
+ 在我们的应用程序中,我们可以把数据库工具类与这个辅助函数组合起来。这允许我们创建、备份、修复和优化数据库和表,虽然只适用于 MySQL 和 MySQLi 数据库。与文件辅助函数一起使用,你就可以创建一个不错的备份例程。
+
+复制内容到剪贴板
+
+PHP 代码:
+
+
+
+ 1.
+
+ $this->load->dbutil();
+
+ 2.
+
+ $backup =& $this->dbutil->backup();
+
+ 3.
+
+ $this->load->helper('file');
+
+ 4.
+
+ write_file('e:/mybackup.gz', $backup);
+
+
+
+ 上面的代码将保存服务器上的最新版本的数据库到文件。
+
+ 再次读取文件同样简单:
+
+复制内容到剪贴板
+
+PHP 代码:
+
+
+
+ 1.
+
+ $content = read_file('e:/filetest.txt');
+
+
+
+ 还有个功能是,返回指定目录中的所有文件和文件夹,并以数组形式表示:
+
+复制内容到剪贴板
+
+PHP 代码:
+
+
+
+ 1.
+
+ $filenames = get_filenames('e:/');
+
+
+
+ 不过,如果你在有很多文件的文件夹中使用它的话,你可能会发现 PHP 在把目录读取完毕前就超时了。你可以使用这个简单的代码片段,来检查你想要的文件或文件夹是否存在。首先,使用 CI 的函数查找文件,并定义一个要查找文件的数组,然后使用 array_diff() 比较它们。给出两个数组,array_diff() 告诉你在第一个数组中,但不在任何其它数组中的值,所以,你需要两次调用它,把每个数组都放在前面。
+
+复制内容到剪贴板
+
+PHP 代码:
+
+
+
+ 1.
+
+ //list files actually found
+
+ 2.
+
+ $files_there = get_filenames('e:/rootfolder/system/application/controllers');
+
+ 3.
+
+ // list files we expected
+
+ 4.
+
+ $files_expected = array('start.php', 'index.php');
+
+ 5.
+
+ // any found that we didn't expect?
+
+ 6.
+
+ $difference = array_diff($files_there, $files_expected);
+
+ 7.
+
+ echo " Missing files are:";
+
+ 8.
+
+ print_r($difference);
+
+ 9.
+
+ // any expected that we didn't find?
+
+ 10.
+
+ $difference = array_diff($files_expected, $files_there);
+
+ 11.
+
+ echo " Extra files are:";
+
+ 12.
+
+ print_r($difference);
+
+
+
+ 最后,有个比较“恐怖”的函数 - delete_files()。删除指定目录里所有文件:
+
+复制内容到剪贴板
+
+PHP 代码:
+
+
+
+ 1.
+
+ delete_files('c:/mydirectory/');
+
+
+
+ 将删除“mydirectory”中的所有内容。如果你添加可选参数为“TRUE”:
+
+复制内容到剪贴板
+
+PHP 代码:
+
+
+
+ 1.
+
+ delete_files('c:/mydirectory/', TRUE);
+
+
+
+ 这同时会删除目录下的所有子文件夹,多加小心。想象下面发生什么?
+
+复制内容到剪贴板
+
+PHP 代码:
+
+
+
+ 1.
+
+ delete_files("c:/", TRUE);
+
+
+
+ 要不你试试?!
+
+
+
+
+
+下载辅助函数
+
+
+
+ 下载辅助函数库里只有一个函数,但他是文件辅助函数很好的补充。你可能创建了个文件,而希望将之以文本文件的方式展现给访问者,而非一个网页。
+
+一个很好的例子是一个数据库备份文件,就像我们刚刚创建的那段代码。
+
+ 为了在数据库崩溃的时候重建它,我们需要一个 MySQL 格式的文本文件。屏幕上的东西对我们来说没多大用处:
+
+
+
+pg_0171.jpg (61.6 KB)
+
+2008-2-21 12:14
+
+
+
+
+
+ 我们需要找到一种下载文件的方式。换句话说,在 Windows 系统中,我们想看到这个对话框:
+
+
+
+pg_0172.jpg (15.99 KB)
+
+2008-2-21 12:14
+
+
+
+
+
+ 为了在 Internet 连接下编码这些内容,你必须在 HTTP 头中指定页面类型。CI 的下载辅助函数帮助你在后台完成这些事情。装载辅助函数:
+
+复制内容到剪贴板
+
+PHP 代码:
+
+
+
+ 1.
+
+ $this->load->helper('download');
+
+
+
+ 像下面这样使用其唯一的方法:
+
+复制内容到剪贴板
+
+PHP 代码:
+
+
+
+ 1.
+
+ force_download($name, $data);
+
+
+
+ $name 是被下载文件的名字,$data 是文件的内容。如果你想下载已存在的文件,那么你需要先把它读到字符串中:
+
+复制内容到剪贴板
+
+PHP 代码:
+
+
+
+ 1.
+
+ $data = file_get_contents("e:/mybackup.txt");
+
+ 2.
+
+ $name = 'backup.txt';
+
+ 3.
+
+ force_download($name, $data);
+
+
+
+ 文件的内容可以直接重建 MySQL 数据库。你也可以使用这个辅助函数直接下载报告,无需强迫用户把他们从屏幕上摘抄下来。
+
+ 在幕后,辅助函数帮助识别 MIME 类型并设置 HTTP 头。他依赖于其中的“配置”文件,system/application/config/mimes, 下一节将看到上传类也使用它。这个配置文件存储 MIME 类型和对应 HTTP 扩展名的数组—例如:
+
+复制内容到剪贴板
+
+PHP 代码:
+
+
+
+ 1.
+
+ 'rtf' => 'text/rtf',
+
+ 2.
+
+ 'text' => 'text/plain',
+
+
+
+ 这个方法能节省你去记忆他们的时间!
+
+ 如果你经常使用的文件类型没有包括在 CI 的列表中,那么你可以很容易的将它们添加到“配置”文件中。
+
+
+
+
+
+文件上传类
+
+
+
+ 有时候,你想允许用户在你的网站里上传文件。这些可能是文本、图像或更多其他文件类型,如 MP3 音频或 MPEG 视频。这是一个比我们刚刚讨论过的文件下载更复杂的过程,但 CI 的文件上传类可以帮你完成大多数的工作。它也能处理一些安全问题。不过,你应该总是三思而后行允许任何人上传文件到你的网站,你可能想要保护上传页面,以防止未经授权的用户上传。
+
+ 首先,您需要在服务器上分配空间(文件夹),以存放上传的文件。这个文件夹必须已设置了正确的权限,以允许用户写入。(例如 Unix/Linux 系统上的 777). 让我们假设你新建一个名为“uploads”的文件夹,并将它放在 WEB 根目录文件夹中。
+
+装载 CI 的文件上传类:
+
+复制内容到剪贴板
+
+PHP 代码:
+
+
+
+ 1.
+
+ $this->load->library('upload');
+
+
+
+ 然后你需要做三件事情:
+
+
+
+
+
+ * 设置默认值
+
+ * 创建一个控制器来处理上传。
+
+ * 给你的用户提供一个上传表单和“成功”表单。
+
+
+
+
+
+
+
+ 让我们按这个顺序设置他们。首先,设置一系列默认值。创建一个 $config 数组以完成这件事。
+
+ 让我们将你刚创建的目录设置为上传目录。这样写:
+
+复制内容到剪贴板
+
+PHP 代码:
+
+
+
+ 1.
+
+ $config['upload_path'] = 'uploads';
+
+
+
+ 这行代码可以写在控制器中,也可以在 config 文件夹中创建一个包含这行代码的 upload.php 文件。(system/application/config/upload.php).
+
+复制内容到剪贴板
+
+PHP 代码:
+
+
+
+ 1.
+
+
+
+
+
+ 理解这两种设置默认值方式之间的区别很重要。如果你在 config/upload.php 文件中设置了默认值,你就不需要专门进行初始化文件上传类的工作。只要装载他,他自己就能找到默认值。
+
+ 但是, 你可以在加载类的时候在第二个参数中指定该控制器的默认值,像这样:
+
+复制内容到剪贴板
+
+PHP 代码:
+
+
+
+ 1.
+
+ $this->load->library('upload', $config);
+
+
+
+ $config 是默认值数组的名字。(不要试图在 config/upload.php 文件和控制器里设置默认值!)
+
+ 这种处理方式显得不够优雅, 所以 CI 推荐使用更合理的方式组织配置文件。下面是几个要点:
+
+
+
+
+
+ * 上传文件的位置:CI 不做任何假设,你必须告诉他。
+
+ * 允许用户上传的文件类型。像这样设置:
+
+ 复制内容到剪贴板
+
+ PHP 代码:
+
+
+
+ 1.
+
+ $config['allowed_types'] = 'gif|jpg|png';
+
+
+
+ 使用管道操作符(|)分隔允许的文件类型。上面的设置将允许在你的网站里上传大多数的图像类型的文件,但他将不允许上传音频文件。设置这个参数是一个基本的安全措施:如果你只想让用户上传图片,而不允许上传可执行文件或大型 MP3 文件。
+
+ 注意:你必须在允许上传文件之前设置一个值:默认设置(例如:不设置)不允许上传任何文件。
+
+ * Max_size:如果你不想恶意用户填满你所有的空间,那就明确的设置可上传文件的最大文件大小(KB)。默认值是 0,表示没有限制。
+
+ * 覆盖:如果用户正在上传的文件,和上传文件夹里的文件同名的话,旧文件将被覆盖并永远丢失吗?这取决于网站的实现和允许用户上传的原因。CI 的默认值是 'FALSE', 意思是不覆盖旧文件,并用一个新名字保存文件。如果你想新文件覆盖旧文件,就明确设置这个默认值为 'TRUE'。
+
+ 注意:CI 不会自动通知用户他或她的文件已被重命名,所以可能会让用户产生困惑:阅读下面的内容,来学习如何取得上传过程的报告。
+
+ * 你还可以设置图片尺寸,宽和高的默认值;加密文件;清理标题上的空白。
+
+
+
+
+
+ 现在你已经确定默认设置了,接下来你需要一个上传控制器。这非常简单。他的作用是初始化上传类、从用户表单接收上传,然后确定上传是否成功。如果成功则显示一个报告;如果失败则返回一个带错误信息的上传表单。非常简单,他只包括一个方法:do_upload(),像这样:
+
+复制内容到剪贴板
+
+PHP 代码:
+
+
+
+ 1.
+
+ load->helper(array('form', 'url'));
+
+ 8.
+
+ $this->load->library('upload');
+
+ 9.
+
+ }
+
+ 10.
+
+ /*now the function which does all the work!*/
+
+ 11.
+
+ function do_upload()
+
+ 12.
+
+ {
+
+ 13.
+
+ if ( ! $this->upload->do_upload())
+
+ 14.
+
+ {
+
+ 15.
+
+ $error = array('error' =>
+
+ 16.
+
+ $this->upload->display_errors());
+
+ 17.
+
+
+
+ 18.
+
+ $this->load->view('upload_form', $error);
+
+ 19.
+
+ }
+
+ 20.
+
+ else
+
+ 21.
+
+ {
+
+ 22.
+
+ $data = array('upload_data' =>
+
+ 23.
+
+ $this->upload->data());
+
+ 24.
+
+ $this->load->view('upload_success', $data);
+
+ 25.
+
+ }
+
+ 26.
+
+ }
+
+ 27.
+
+ }
+
+
+
+ 此功能需要一个 upload_form 视图和 upload_success 视图。先使用表单辅助函数创建一个表单,并指向“upload”控制器的 do_upload 方法:不要用下面的函数“打开”:
+
+复制内容到剪贴板
+
+PHP 代码:
+
+
+
+ 1.
+
+ echo form_open('upload/do_upload');
+
+
+
+ (第 5 章里介绍的表单“打开”方式), 而是用表单辅助函数的 multipart 函数“打开”:
+
+复制内容到剪贴板
+
+PHP 代码:
+
+
+
+ 1.
+
+ echo form_open_multipart('upload/do_upload');
+
+
+
+ (记住:我们是在用代码来产生 HTML 标签,所以需要显示到屏幕上。)
+
+ 然后,使用表单辅助函数的 form_upload 函数替换 form_input:
+
+复制内容到剪贴板
+
+PHP 代码:
+
+
+
+ 1.
+
+ echo form_upload($data);
+
+
+
+ 这两行代码可以帮你完成很多乏味的工作。
+
+ 添加一个提交按钮,并“关闭”表单。
+
+复制内容到剪贴板
+
+PHP 代码:
+
+
+
+ 1.
+
+ echo form_submit('mysubmit', 'Submit Post!');
+
+
+
+ 传递 $view 变量到视图并装载他。当 do_upload 函数传递 $error 到视图中时,你的视图也应该显示 $error 变量。
+
+复制内容到剪贴板
+
+PHP 代码:
+
+
+
+ 1.
+
+ echo $error;
+
+
+
+ 你现在看到的页面应该是这样的:
+
+
+
+pg_0176.jpg (3.67 KB)
+
+2008-2-21 12:14
+
+
+
+
+
+ 单击“浏览”查看用户电脑上的文件(本地),而不是服务器上的。一旦他(她)选择了一个文件并单击上传按钮,将调用上传控制器,然后文件将被传输到服务器上的上传文件夹中。
+
+ 当我们尝试上传一个文本文件时,(记住,我们只允许 'gif | jpg | png' 文件类型。) 我们将看到:
+
+
+
+pg_0177.jpg (5.93 KB)
+
+2008-2-21 12:14
+
+
+
+
+
+ CI 返回一个类型错误:这是控制器中的 $this->upload->display_errors() 方法在起作用,他在视图中添加了一个错误消息变量。
+
+ 你也可以让 CI 报告上传已成功。正如你看到的,如果上传成功,控制器就装载一个名为 upload_success 的视图。$this->upload->data 方法的内容将被传递到这个视图中。他返回上传过程的所有信息的数组:可能多于你想要显示的信息。
+
+ 我上传一个名为 waltzer.jpg 的文件:默认报告看起来是这样的:
+
+
+
+pg_0177_1.jpg (22.15 KB)
+
+2008-2-21 12:14
+
+
+
+
+
+ 如果你想创建一个像 Flickr 那样的站点的话,那这些信息可能会影响用户上传他们的图片!然而,你可以很容易的去掉上传控制器中你不需要的任何信息。
+
+ 请注意,顺便说一下,在写这个例子的时候,我设置文件上传类的“overwrite”值为“FALSE”,然后上传了两次 waltzer.jpg 文件。
+
+ 上面的截图显示的是 CI 关于第二次上传成功的报告。你将看到文件已被重命名为 waltzer1.jpg。如果查看上传文件夹,你就会看到两个文件,一个是原来的 waltzer.jpg 文件,另一个是新的 waltzer1.jpg 文件。在你的应用里,你可能想比较 raw_name 和 orig_name 的值,并告诉用户文件名已被更改。
+
+ CI 不比较两个文件的内容,只比较文件名。如果你允许多人上传文件,那么,其中的两个人,将很有可能在无意中,使用相同的文件名来上传不同的文件,并且,你可能不希望丢失第一个文件。另一方面,如果你通过网站上传名字总是相同的文件,你也许宁愿只在网站上保存最新的文件,在这种情况下,覆盖文件是一个简单的节省空间的方法。
+
+ 顺便说一下这个图片,我们将在下一节用到它。
+
+
+
+pg_0178.jpg (16.13 KB)
+
+2008-2-21 12:14
+
+
+
+
+
+
+
+CI 的图像类
+
+
+
+ 如果你允许用户上传图片,你还需要看看 CI 的图像处理类。他支持 PHP 最流行的三个图像类库:GD/GD2、NetPBM 和 ImageMagick。(使用 phpinfo() 查看你的服务器是否支持这些类库。) 虽然图像水印效果只支持 GD/GD2:
+
+ 图像处理类允许你完成图像的 4 个基本功能:
+
+
+
+
+
+ * 缩放:在你的屏幕上,你可能想把它们的大小设置为标准尺寸;或者你想设置成“缩略图”的大小。
+
+ * 裁剪
+
+ * 旋转
+
+ * 水印(只可用在 GD/GD2):它经常用于在图片上放置版权标志,这样可以防止别人从你的网站上下载图片,然后占为己有,当作他们自己的原创作品。
+
+
+
+
+
+ 这些功能中最有用的可能就是缩放了,那么一会儿我们来详细看看吧。裁剪和旋转用处不大,除非你能在屏幕上看到图片。要做到这一点,你需要某种形式的用户界面,使用户可以指定她或他想做的事,并控制 CI 实现这些功能,并且,你需要自己编写这些功能代码!
+
+ 让我们假设你已经使用刚才讨论过的文件上传类,上传了 wltzer.jpg 图片到 /uploads 文件夹中。(上传和处理图片需要设置这个文件夹的权限为 777,因为 CI 需要保存处理结果到这个文件夹中。)
+
+ 首先, 装载类库:
+
+复制内容到剪贴板
+
+PHP 代码:
+
+
+
+ 1.
+
+ $this->load->library('image_lib');
+
+
+
+ 然后,你需要设置几个配置信息。(与文件上传类一样,你可以写在代码里,也可以放到单独的 system/application/config/image_lib.php 文件中。)
+
+
+
+ 这里有一些配置,并且他们已列在《用户指南》里了。或许最重要的是:
+
+
+
+
+
+ * 选择你使用的图像库。默认是 GD2,所以,如果你的 PHP 安装的不是这个库的话,你需要指定一个,例如, $config['image_library'] = 'ImageMagick' (你也应该提供 ImageMagick 库的路径:$config['library_path'] = '/mypath';。)
+
+ * 你要处理的图片。应该提供路径(相对于网站根目录)和文件名。
+
+ * 处理后图片的尺寸—'x' 表示像素数,设置宽度:$config['width'] = x;,设置高度 $config['height'] = x;。
+
+
+
+
+
+ 要调整图片尺寸,只这些还不够,需要用已调整尺寸的图片覆盖旧图片文件。代码如下:
+
+复制内容到剪贴板
+
+PHP 代码:
+
+
+
+ 1.
+
+ function do_image($image_name)
+
+ 2.
+
+ {
+
+ 3.
+
+ $this->load->library('image_lib');
+
+ 4.
+
+ $config['image_library'] = 'GD2';
+
+ 5.
+
+ $config['source_image'] = "$image_name";
+
+ 6.
+
+ $config['width'] = 75;
+
+ 7.
+
+ $config['height'] = 50;
+
+ 8.
+
+ $this->image_lib->initialize($config);
+
+ 9.
+
+ if(!$this->image_lib->resize())
+
+ 10.
+
+ {echo "failed";}
+
+ 11.
+
+ else{echo 'success!';}
+
+ 12.
+
+ }
+
+
+
+ 这个库可以做其他一些聪明的事情。如果你不想覆盖原始图片,可以为新版本指定新名称和文件路径,添加:
+
+复制内容到剪贴板
+
+PHP 代码:
+
+
+
+ 1.
+
+ $config['new_image'] = 'newfolder/newname.png';
+
+
+
+ 或者,如果你想创建一个图片的缩略图,简单地添加:
+
+复制内容到剪贴板
+
+PHP 代码:
+
+
+
+ 1.
+
+ $config['create_thumb'] = TRUE;
+
+
+
+ 执行的效果是:用默认的后缀(_thumb)重命名新调整的文件,把 waltzer.jpg 改成 waltzer_thumb.jpg。(你可以很容易的修改后缀--参考《用户指南》。) 所以,现在有两个文件在同一个文件夹中:原图和缩略图。
+
+ 注意:缩略图设置什么也不做,你还是需要设置你想要的尺寸。
+
+ 这个图片被缩小到 75x50 像素:
+
+
+
+pg_0180.jpg (3.24 KB)
+
+2008-2-21 12:14
+
+
+
+
+
+ 图像类的附加功能可以给你的图片加水印。所以,如果你把自己的精彩照片放到网站上,你也可以添加一个版权声明。
+
+ 虽然添加水印功能有很多选项,在《用户指南》里有详细说明,但基本的代码很简单。初始化类,告诉它想要加水印的图片和水印的内容,然后调用 watermark 方法。
+
+复制内容到剪贴板
+
+PHP 代码:
+
+
+
+ 1.
+
+ function wm_image()
+
+ 2.
+
+ {
+
+ 3.
+
+ $this->load->library('image_lib');
+
+ 4.
+
+ $config['source_image'] = 'uploads/waltzer.jpg';
+
+ 5.
+
+ $config['wm_text'] = 'Copyright 2007 - David Upton';
+
+ 6.
+
+ $config['wm_type'] = 'text';
+
+ 7.
+
+ $this->image_lib->initialize($config);
+
+ 8.
+
+ if(!$this->image_lib->watermark())
+
+ 9.
+
+ {echo 'failure to watermark';}
+
+ 10.
+
+ else {echo 'success';}
+
+ 11.
+
+
+
+ 12.
+
+ }
+
+
+
+ (wm_type 选项设置为 text,则允许你添加文本水印。否则,设置这个选项为 overlay,并提供一个叠加在你原图上的图片。)
+
+ 现在图片看起来像这样。
+
+
+
+pg_0181.jpg (12.39 KB)
+
+2008-2-21 12:14
+
+
+
+
+
+ 我的实际代码比上面的例子复杂的多,以便让我能控制大小和水印的位置,使之更清楚的显示在这个页面上。默认的代码如上所示,将足以应付大多数的用途,但水印太小,打印出来后无法看清。要了解更多关于使用外部字体的信息,请参考 CI 的《用户指南》。
+
+ 这个类使用起来非常的简单。只有当你查看类的代码(在 system/libraries/Image_lib.php 文件中)后,才能体会到 CI 帮你节省了多少时间!
+
+
+
+
+
+用 CI Zip 类压缩文件很容易
+
+
+
+ 如果你要移动像图片这样的大文件的话,你可能需要压缩它们。CI 包含一个实现这个功能的,并且便于使用的类库。
+
+ 让我们拿出刚用过的照片:waltzer.jpg。它在我们的 /uploads 文件夹中。
+
+ 与以往一样,你要先初始化 Zip 类。然后,你要告诉 CI 你想压缩成什么文件,并创建要压缩的文件。接下来,使用 read_file 方法读取这些文件并压缩它,最后用 download 方法下载到你的桌面上。
+
+复制内容到剪贴板
+
+PHP 代码:
+
+
+
+ 1.
+
+ function zip_image()
+
+ 2.
+
+ {
+
+ 3.
+
+ $this->load->library('zip');
+
+ 4.
+
+ $this->zip->archive('my_backup.zip');
+
+ 5.
+
+ $path = 'uploads/waltzer1.jpg';
+
+ 6.
+
+ $this->zip->read_file($path);
+
+ 7.
+
+ $this->zip->download('my_backup.zip');
+
+ 8.
+
+ }
+
+
+
+ CI Zip 编码类比上面说的更复杂,并有数个选项。一如以往,他们都刊载于《用户指南》中。但也应该给了你一个怎样用 CI 简单制作 zip 文件,并从网站上下载它的例子,以便尽量减少带宽消耗并为你的用户节省时间。
+
+
+
+
+
+总结
+
+
+
+ 本章集中说明了一些 CI 辅助函数和类库的功能, 他们可以提供:
+
+
+
+
+
+ * 使用最少的代码进行文件读写操作, 并且可以自动处理锁定和解锁文件。
+
+ * 自动处理 HTTP 头, 而不用关心如何处理是否要显示页面或者下载文件。
+
+ * 上传文件到服务器, 并进行一些安全上的设置, 比如说上传文件的尺寸和类型。
+
+ * 更方便的图片处理, 如缩放和添加水印。
+
+ * 处理下载请求前先对文件进行压缩以节省带宽。
+
+
+
+
+
+ 对我来说框架就是这样, 代替你完成许多乏味的编码工作, 他会给你一个标准且易用的接口, 并且为你考虑细节问题。
\ No newline at end of file
diff --git a/_docs/12.doc b/_docs/12.doc
new file mode 100644
index 0000000..c622ad2
Binary files /dev/null and b/_docs/12.doc differ
diff --git a/_docs/12.now.txt b/_docs/12.now.txt
new file mode 100644
index 0000000..b1573e0
--- /dev/null
+++ b/_docs/12.now.txt
@@ -0,0 +1,596 @@
+> Production Versions,Updates, and Big Decisions
+> 绗 12 绔 浜у搧鐗堟湰锛屽崌绾у拰閲嶅ぇ鍐冲畾
+绗12绔 浜у搧鐗堟湰銆佸崌绾у拰閲嶅ぇ鍐冲畾
+
+> The great day has come. Your development site is running well enough on your local development for you to transfer it to a production site hosted on a remote web server. It should be easy to do this. Copy over all the files, including the whole of the system folder, update the config settings, copy over and link to the database, and away you go. Sometimes, it really is that easy.
+> 鏈夋剰涔夌殑涓澶╂潵涓翠簡銆備綘寮鍙戠殑缃戠珯鍦ㄤ綘鐨勬湰鍦板紑鍙戠幆澧冧腑杩愯寰楄冻澶熷ソ浜嗭紝鏄椂鍊欐妸瀹冧笂浼犲埌杩滅▼鐨刉EB鏈嶅姟鍣ㄤ笂鎴愪负涓涓寮忚繍琛岀殑缃戠珯浜嗐傚疄鐜板畠搴旇鏄鏄撶殑銆備笂浼犳墍鏈夌殑鏂囦欢,鍖呮嫭绯荤粺鏂囦欢澶圭殑鍏ㄩ儴, 鏇存柊 config 璁剧疆, 澶嶅埗瀹屾瘯骞惰繛鎺ュ埌鏁版嵁搴, 鎶婂畠浠紶閫佸嚭鍘汇 鏈夋椂鍊欙紝瀹冪湡鐨勬槸寰堝鏄撱
+浼熷ぇ鐨勪竴澶╃粓浜庡埌鏉ヤ簡锛佷綘鐨勭綉绔欏湪鏈湴鐜涓凡缁忚繍琛屽緱瓒冲濂斤紝鏄椂鍊欐妸瀹冧笂浼犲埌杩滅▼鏈嶅姟鍣ㄤ笂浣夸箣鎴愪负涓涓寮忚繍琛岀殑缃戠珯浜嗐傝繖浠朵簨鎸夎寰堝鏄擄細涓婁紶鎵鏈夌殑鏂囦欢锛屽寘鎷郴缁熸枃浠跺す鐨勫叏閮ㄣ佹洿鏂癱onfig璁剧疆锛屽鍒跺畬鎴愬悗杩炴帴鍒版暟鎹簱锛屼互鍙婂叾浠栥傛湁鏃跺欙紝杩欑殑纭緢瀹规槗銆
+
+> But when it isn't, it's always the night before you are giving an all-important presentation to a venture capitalist or a major prospect. So, in case this happens to you, this chapter covers:
+> 浣嗘槸褰撳畠杩樻病鏈夋垚鍔熺殑鏃跺欙紝瀹冨鍦ㄤ綘鎶婇噸瑕佺殑涓鍒囧憟鐜板湪涓涓闄╄祫鏈鎴栧叕浼楄閲庨潰鍓嶇殑鍓嶅銆 鍥犳, 涓轰簡闃叉鍑虹幇鎰忓, 杩欎竴绔犳秹鍙:
+鍦ㄤ綘鎶婇噸瑕佺殑涓鍒囧憟鐜板湪椋庢姇鎴栧叕浼楄閲庣殑鍓嶅锛屼篃灏辨槸鍦ㄨ繕娌″畬鎴愭椂銆傛鏃讹紝涓轰簡闃叉鍑虹幇鎰忓锛岃繖涓绔犲皢鍛婅瘔浣狅細
+
+> 路What to look for in your config files
+> 璇ュ湪浣犵殑 config 鏂囦欢涓缃粈涔
+浣犺鍦╟onfig鏂囦欢涓缃粈涔
+
+> 路Some diagnostic tools to use if you get stuck
+> 濡傛灉鍑虹幇闂闇瑕佺殑涓浜涜瘖鏂伐鍏
+鍑虹幇闂闇瑕佺殑涓浜涜瘖鏂伐鍏
+
+> 路Some potential differences between servers that may trip you up
+> 鏈湴鏈嶅姟鍣ㄤ笌鍙兘璁╀綘鍑虹幇宸敊鐨勬湇鍔″櫒涓婄殑闅愭у樊寮
+鍙兘鍑虹幇闂鐨勬湇鍔″櫒涓庢湰鍦版湇鍔″櫒鐨勯殣鎬у樊寮
+
+> 路Some notes on security, now that you're out there in the big world
+> 鍑犵偣瀹夊叏鎻愮ず, 鐜板湪浣犲皢澶勮韩浜庡ぇ鍗冧笘鐣
+鍑犵偣瀹夊叏鎻愮ず, 浣犲皢韬浜庡ぇ鍗冧笘鐣
+
+> Next, this chapter covers upgrading, and looks at some of the ways in which CI has changed in the year it's been available. How stable is it? What decisions should you make if you are committing a critical site to it? And what should you do, once your site is up and running, if Rick Ellis brings out a newer version of CI?
+> 涓嬩竴姝, 杩欎竴绔犲寘鎷崌绾, 浠ュ強鐣欐剰閭d簺CI鍦ㄥ勾鍐呭凡缁忔敼鍙樼殑涓浜涙柟寮忋傚畠绋冲畾鍚? 濡傛灉浣犳彁浜や竴涓噸瑕佺綉绔欎綘搴旇濡備綍鍐崇瓥锛熶綘璇ユ庝箞琛屽姩锛熶竴鏃︿綘鐨勭綉绔欏紑濮嬭繍浣滐紝鑰孋I鍙堟帹鍑烘柊鐗堟湰浣犲張濡備綍搴斿锛
+鐒跺悗锛岃繖涓绔犺繕鍖呮嫭浜嗗崌绾т互鍙奀I鍦ㄦ渶杩戝嚑骞寸殑涓浜涙洿鏂般傚畠绋冲畾鍚? 褰撹鎻愪氦涓涓噸瑕佺綉绔欐椂浣犺濡備綍閫夋嫨锛熶綘璇ュ浣曟搷浣滐紵涓鏃︿綘鐨勭綉绔欏紑濮嬭繍浣滐紝鑰孋I鎺ㄥ嚭浜嗘柊鐗堟湰浣犲張璇ュ浣曞簲瀵癸紵
+
+> Lastly, we briefly discuss making your own alterations to the core of CI. It's all there; it's open-source it's possible. Whether it's sensible or not is another matter.
+> 鏈鍚庯紝鎴戜滑绠鐭湴璁ㄨ濡備綍閽堝 CI 鐨勬牳蹇冧綔鍑轰綘鑷繁鐨勪慨鏀广傚畠閮藉湪閭i噷; 瀹冩槸寮鏀炬簮浠g爜鐨勶紝淇敼鏄彲鑳界殑銆 鏄惁鍙鏄彟涓浠朵簨鎯呫
+鏈鍚庯紝鎴戜滑绠鐭湴璁ㄨ濡備綍閽堝CI鐨勬牳蹇冨仛浣犵殑鑷畾涔変慨鏀广傚畠灏卞湪閭i噷锛屽畠鏄紑鏀炬簮浠g爜鐨勶紝鏄彲浠ヤ慨鏀圭殑鈥斺斾絾鏄惁鍙鏄彟涓鐮佷簨銆
+
+> Connections: Check the Config Files
+> 杩炴帴: 妫鏌 Config 鏂囦欢
+杩炴帴锛氭鏌onfig鏂囦欢
+
+> Systems usually fail at the interfaces. That's what your config files are there for: to give you a place to put all those interfaces. If you haven't done so, you've missed one of CI's major strengths.
+> 绯荤粺閫氬父浼氬湪鎺ュ彛澶勫嚭閿欍 閭f槸涓轰粈涔堥渶瑕 config 鏂囦欢鐨勫師鍥: 缁欎綘涓涓湴鏂规斁閭d簺鎺ュ彛銆 濡傛灉浣犺繕娌℃湁杩欎箞鍋氾紝浣犲凡缁忛敊杩 CI 鐨勪富瑕佷紭鐐逛箣涓銆
+绯荤粺閿欒閫氬父鏄湪鎺ュ彛涓婄殑銆傝繖灏辨槸涓轰粈涔堥渶瑕乧onfig鏂囦欢锛氱粰浣犱竴涓湴鏂规斁閭d簺鎺ュ彛銆傚鏋滀綘杩樻病杩欎箞鍋氾紝浣犲凡缁忛敊杩嘋I鏈涓昏鐨勪紭鐐逛箣涓銆
+
+> The major interface problems are likely to be:
+> 涓昏鐨勬帴鍙i棶棰樺彲鑳芥槸:
+涓昏鐨勬帴鍙i棶棰樺彲鑳芥槸锛
+
+> URLs
+> URL
+URL
+
+> CI works by finding files. Your user connects to index.php, and then a whole process of loading starts. At least, it should do. Make sure you have set the web address and server addresses correctly in your config files. The web address is your web root folder; you may have to ask your ISP for the server address, though usually it is clear from their own 'file manager' programme.
+> CI 閫氳繃鏌ユ壘鏂囦欢宸ヤ綔銆 浣犵殑鐢ㄦ埛杩炴帴鍒 index.php, 鐒跺悗寮濮嬭繍琛屾暣涓▼搴忋 鑷冲皯锛屽畠搴旇杩愯銆 纭畾浣犲湪浣犵殑 config鏂囦欢涓凡缁忔纭湴璁剧疆濂絎EB鍦板潃鍜屽叾瀹冩湇鍔″櫒鍦板潃銆 WEB鍦板潃鏄綘鐨勭綉绔欐牴鐩綍;浣犲彲鑳介渶瑕佸挩璇綘鐨処SP浠ヤ究寰楀埌鏈嶅姟鍣ㄥ湴鍧锛屼粬浠湁浠栦滑鑷繁鐨勬枃浠剁鐞嗙郴缁燂紝涓鏌ヤ究鐭ャ
+CI閫氳繃鏌ユ壘鏂囦欢杩涜宸ヤ綔銆傚綋鐢ㄦ埛杩炴帴鍒癷ndex.php锛岀劧鍚庡紑濮嬭繍琛屾暣涓▼搴忋傝嚦灏戯紝瀹冨簲璇ヨ繍琛屻傜‘瀹氫綘鍦╟onfig鏂囦欢涓凡缁忔纭湴璁剧疆濂絎eb鍦板潃鍜屽叾瀹冩湇鍔″櫒鍦板潃銆俉eb鍦板潃鏄綘鐨勭綉绔欐牴鐩綍銆傝屾湇鍔″櫒鍦板潃鍒欏彲鑳借鍘婚棶浣犵殑ISP锛岄氬父鍦ㄤ粬浠彁渚涚殑鈥滄枃浠剁鐞嗏濊彍鍗曚腑涔熸湁銆
+
+> I've had particular issues trying to run sub-domains. Many hosts allow them, but map the domains to folders in ways you don't expect.
+> 鎴戞浘缁忓皾璇曡繍琛屽瓙鍩熴 璁稿涓绘満鍏佽杩欎箞鍋, 浣嗘槸鎶婂煙鏄犲皠鍒颁綘鐨勭洰褰曚篃璁镐笉鏄綘甯屾湜鐨勩
+鎴戝皾璇曡缃瓙鍩熸椂鏇剧粡閬囧埌杩囦竴浜涚壒娈婄殑闂銆傝櫧鐒惰澶氫富鏈哄厑璁歌繖涔堝仛, 浣嗗皢鍩熺洿鎺ユ槧灏勫埌鐩綍涔熻骞朵笉鏄綘鎵甯屾湜鐪嬪埌鐨勩
+
+> Databases
+> 鏁版嵁搴
+鏁版嵁搴
+
+> Locating and connecting to your database is often a major issue. Look at your config file and your config/database file. You need to make sure that you have the correct site and server addresses, and the correct database name, address, user name, and password. Be careful with prefixes鈥攕ometimes these are added automatically. (Your site is called 'fred'. Your database, you think, is called 'mydata' and your user name is 'mylogin'. But the server thinks they are called 'fred_mydata', 'fred_mylogin', etc.)
+> 瀹氫綅骞惰繛鎺ュ埌浣犵殑鏁版嵁搴撶粡甯告槸涓涓富瑕佺殑璇濋銆 鏌ョ湅浣犵殑 config 鏂囦欢鍜屼綘鐨 config/database鏂囦欢銆備綘闇瑕佺‘瀹氫綘鏈夋纭殑缃戠珯鍜屾湇鍔″櫒鍦板潃鍜屾纭殑鏁版嵁搴撳悕绉般佸湴鍧, 鐢ㄦ埛鍚嶅拰瀵嗙爜銆傚皬蹇冨墠缂-鏈夋椂杩欎簺鑷姩鍦拌娣诲姞鐨勩 (浣犵殑缃戠珯鍙 'fred'銆 浣犵殑鏁版嵁搴, 鍙仛 'mydata' 鍜屼綘鐨勭敤鎴峰悕鏄 'mylogin'. 浣嗘槸鍦ㄦ湇鍔″櫒涓婁粬浠彨鍋 'fred_mydata' 锛'fred_mylogin', 绛夌瓑銆)
+璁剧疆骞惰繛鎺ュ埌鏁版嵁搴撴槸涓涓富瑕佽棰樸傛煡鐪嬩綘鐨刢onfig鏂囦欢涓巆onfig/database鏂囦欢銆備綘闇瑕佺‘瀹氭湁姝g‘鐨勭綉绔欏湴鍧涓庢湇鍔″櫒鍦板潃锛屾纭殑鏁版嵁搴撳悕銆佸湴鍧銆佺敤鎴峰悕涓庡瘑鐮併傚皬蹇冨墠缂鈥斺斿畠鏈夋椂浼氳嚜鍔ㄦ坊鍔犵殑锛屾瘮濡備綘鐨勭綉绔欏彨鈥渇red鈥濓紝浣犵殑鏁版嵁搴撳悕涓衡渕ydata鈥濓紝鑰屼綘鐨勭敤鎴峰悕鏄渕ylogin鈥濓紝浣嗘槸鍦ㄦ湇鍔″櫒涓婂畠浠細鍙仛鈥渇red_mydata鈥濄佲渇red_mylogin鈥濈瓑绛夈
+
+> Sometimes, it helps to create a new user on your database, even if you have one already, and set the log-in for this user's name and password. I have no idea why this works, but it does.
+> 鏈夋椂锛屽畠甯姪鍦ㄤ綘鐨勬暟鎹簱涓婂垱寤轰竴涓柊鐨勭敤鎴凤紝鍗充娇浣犲凡缁忔湁浜嗕竴涓, 鑰屼笖璁惧畾涓轰互鏂扮殑鐢ㄦ埛鍚嶅拰瀵嗙爜鐧诲綍銆 鎴戜笉鐭ラ亾涓轰粈涔堜細杩欐牱锛屼絾鏄粡甯镐細杩欐牱銆
+鏈夋椂锛屽畠浼氬湪鏁版嵁搴撲腑鍒涘缓涓涓柊鐨勭敤鎴凤紝骞惰瀹氫簡鐧诲綍鐢ㄧ殑鐢ㄦ埛鍚嶅拰瀵嗙爜锛屽嵆浣夸綘宸茬粡鏈変竴涓簡銆傛垜涔熶笉鐭ラ亾涓轰粈涔堬紝浣嗗氨鏄細杩欐牱銆
+
+> In the config file, you can set CI to accept different types of URI protocols, which determine how the server handles the URI string. The default is:
+> 鍦 config 鏂囦欢涓紝浣犺兘璁惧畾 CI 鎺ュ彈涓嶅悓鐨刄RL鍗忚绫诲瀷,鍐冲畾鏈嶅姟鍣ㄥ浣曞鐞 URI 瀛楃涓层 榛樿鍊兼槸:
+鍦╟onfig鏂囦欢涓紝浣犲彲浠ヨ瀹欳I鎺ュ彈涓嶅悓鐨刄RL鍗忚绫诲瀷锛屼互鍐冲畾鏈嶅姟鍣ㄥ浣曞鐞哢RI瀛楃涓层傞粯璁ゅ兼槸锛
+
+> $config['uri_protocol']="auto";
+
+
+> but there are four other options that you can try if this doesn't work. If the correct option isn't set, you may find your site partly works, but (for example) forms don't call the target page.
+> 浣嗘槸濡傛灉杩欎笉鑳芥甯稿伐浣滐紝鏈夊叾浠栧洓涓夐」鍙互灏濊瘯銆 濡傛灉姝g‘鐨勯夐」娌¤璁惧畾锛屼綘鍙兘鍙戠幇浣犵殑缃戠珯鍙湁閮ㄥ垎宸ヤ綔姝e父(涓句緥鏉ヨ) 琛ㄥ崟涓嶈兘璋冪敤鐩爣椤点
+濡傛灉瀹冧笉鑳芥甯稿伐浣滐紝杩樻湁鍏朵粬鍥涗釜閫夐」鍙互璇曡瘯鐪嬨傚鏋滄病鏈夐夋嫨鍚堥傞夐」鐨勮瘽锛屼綘鍙兘浼氬彂鐜颁綘鐨勭綉绔欎笉鑳藉畬鍏ㄦ甯哥殑杩愪綔锛岋紙渚嬪锛夎〃鍗曚笉鑳借皟鐢ㄧ洰鏍囬〉銆
+
+> Other config Files
+> 鍏朵粬鐨 config 鏂囦欢
+鍏朵粬config鏂囦欢
+
+> The config/routes file sets the default path the application follows, if the user doesn't specify a controller锟絤ethod through the URL (i.e. if they just log on to www.mysite.com). This should normally be set at the default:
+> config/routes鏂囦欢璁惧畾绋嬪簭鎵ц鐨勯粯璁よ矾寰勶紝濡傛灉鐢ㄦ埛娌℃湁鎸囧畾涓涓猚ontroller/method (涔熷氨鏄鏋滀粬浠粎浠呯櫥褰曞埌www.mysite.com). 杩欓氬父搴旇鍦ㄩ粯璁ゅ间腑琚瀹:
+濡傛灉鐢ㄦ埛娌℃湁鎸囧畾鏄庣‘鐨刢ontroller/method锛堟瘮濡備粬浠粎浠呯櫥褰曞埌www.mysite.com锛夛紝config/routes鏂囦欢鍒欎細璁惧畾绋嬪簭鎵ц鐨勯粯璁よ矾寰勩傚叾榛樿鍊煎彲鑳芥槸锛
+
+> $route['default_controller']='index';
+
+
+> If you renamed the system folder, then remember that you also need to alter the index.php file in the site's root directory. The default setting is:
+> 濡傛灉浣犻噸鏂板懡鍚嶇郴缁熸枃浠跺す浜,蹇呴』璁板緱浣犺繕闇瑕佸湪缃戠珯鐨勬牴鐩綍涓敼鍙 index.php 鏂囦欢銆 榛樿鍊艰瀹氫负:
+濡傛灉浣犻噸鏂板懡鍚嶈繃绯荤粺鏂囦欢澶癸紝灏卞繀椤昏寰椾綘杩橀渶瑕佸湪缃戠珯鐨勬牴鐩綍涓慨鏀筰ndex.php鏂囦欢銆傝鍊奸粯璁や负锛
+
+> $system_folder="system";
+
+
+> Look Out for PHP 4/5 and Operating System Differences
+> 鎵惧嚭PHP 4/5 鍜屾搷浣滅郴缁熼棿鐨勫樊寮
+鎵惧埌PHP 4/5鍜屾搷浣滅郴缁熼棿鐨勫樊寮
+
+> CI should be able to live with any version of PHP including and later than 4.3. However, this doesn't mean that any PHP code you write will also be compatible鈥攕o if you wrote it on Xampplite using PHP 5, and are moving to an ISP with a PHP 4 server, look out for problems due to language differences.
+> CI 搴旇鑳藉鍏煎PHP 4.3鍙婁互鍚庣殑浠讳綍鐗堟湰. 鐒惰岋紝杩欎笉鎰忓懗鐫浣犲啓鐨勪换浣曠殑 PHP 缂栫爜閮借兘姝e父宸ヤ綔-鍥犳濡傛灉浣犲湪浣跨敤 PHP 5, 骞朵笖姝e湪鍚戜竴涓 PHP 4 鏈嶅姟鍣ㄨ縼绉, 鐢变簬鐗堟湰涓嶅悓鏈夊彲鑳藉紩鍙戦棶棰樸
+CI搴旇鑳藉鍏煎PHP 4.3鍙婃洿楂樼増鏈備絾杩欎笉鎰忓懗鐫浣犲啓鐨勪换浣曠殑PHP绋嬪簭閮借兘姝e父宸ヤ綔鈥斺斿鏋滀綘鍦ㄤ娇鐢≒HP 5锛屼絾姝e湪鍚戜竴涓狿HP 4鏈嶅姟鍣ㄨ縼绉绘椂锛屽氨鍙兘浼氶亣鍒扮敱浜庣増鏈笉鍚屽紩鍙戠殑闂銆
+
+> PHP (of whatever version) can be set up in different ways. It's worth running phpinfo() on your local and remote sites, and checking for differences.
+> PHP(鏃犺鍝竴涓増鏈) 鍙兘浠ヤ笉鍚岀殑鏂瑰紡琚缓绔嬨傚煎緱涓鍋氱殑鏄湪浣犳湰鍦板拰杩滅▼鏈嶅姟鍣ㄤ笂杩愯phpinfo(), 鎵惧埌瀹冧滑涔嬮棿鐨勫樊寮傘
+锛堟棤璁哄摢涓涓増鏈殑锛塒HP鐜閮藉彲浠ヤ笉鍚岀殑鏂瑰紡鎼缓銆傚煎緱涓鍋氱殑鏄湪浣犳湰鍦板拰杩滅▼鏈嶅姟鍣ㄤ笂閮借繍琛宲hpinfo()锛屼互鎵惧埌瀹冧滑鐨勫樊寮傘
+
+> Case sensitivity is different between Microsoft and Linux servers. So if you've developed your site on a PC, and then uploaded it to a Linux-based server, expect the server to report that it can't find some of the models or libraries you want to load. If you've checked and they have been uploaded, then make sure that the capitalization is correct. Because class definitions and constructors in CI have to begin with a capital, it's easy to begin the file name with a capital too. If you load a model, say, inside a controller, and give it a capitalized name ($this->load->Mymodel), then Windows and Linux may have different views about calls to $this->mymodel.
+> 澶у皬鍐欐晱鎰熷湪寰蒋鍜 Linux 鏈嶅姟鍣ㄤ箣闂存槸涓嶅悓鐨勩 鍥犳濡傛灉浣犲湪涓鍙皐indows鎿嶄綔绯荤粺鐨凱C涓婂紑鍙戜綘鐨勭綉绔, 鐒跺悗涓婁紶鍒颁竴鍙 Linux鏈嶅姟鍣ㄤ笂锛屽彲鑳戒細鏀跺埌鍑洪敊淇℃伅锛屾姤鍛婂畠鎵句笉鍒颁竴浜涗綘瑕佽杞界殑妯″瀷鎴栬呯被搴撱傚鏋滀綘妫鏌ヨ繃纭畾瀹冧滑宸茬粡琚笂浼, 浣犱篃闇瑕佺‘瀹氬ぇ灏忓啓鏄纭殑銆傚洜涓哄湪CI 鐨勭被瀹氫箟鍜屾瀯閫犲嚱鏁板懡鍚嶆槸瑕佹眰浠ヤ竴涓ぇ鍐欏瓧姣嶅紑濮嬬殑锛岃屼笖浠ヤ竴涓ぇ鍐欏瓧姣嶅紑濮嬩竴涓枃浠跺悕瀹炵幇涔熷緢瀹规槗銆傚洜姝ゅ湪涓涓帶鍒跺櫒鍐,浣犺杞戒竴涓ā鍨,鍐犱互涓涓ぇ鍐欏瓧姣嶅紑濮嬶紝姣斿锛($this->load->Mymodel),浣嗘槸windows鍜 Linux 鍙兘浼氶氳繃$this->Model璋冪敤涓嶅悓鐨勮鍥俱
+寰蒋鍜孡inux鏈嶅姟鍣ㄤ笂鐨勫ぇ灏忓啓鏁忔劅鏄笉鍚岀殑銆傛墍浠ュ綋浣犲湪涓鍙癢indows鎿嶄綔绯荤粺鐨凱C涓婂紑鍙戜綘鐨勭綉绔欙紝鐒跺悗涓婁紶鍒颁竴鍙癓inux鏈嶅姟鍣ㄤ笂锛屽彲鑳戒細鏀跺埌鍑洪敊淇℃伅锛屾姤鍛婃壘涓嶅埌涓浜涗綘瑕佽杞界殑妯″瀷鎴栬呯被搴撱傚鏋滀綘妫鏌ヤ簡骞剁‘瀹氬畠浠凡琚笂浼, 浣犱粛闇瑕佺‘瀹氬ぇ灏忓啓鏄纭殑銆傚洜涓哄湪CI鐨勭被瀹氫箟鍜屾瀯閫犲嚱鏁板懡鍚嶆椂锛屾槸瑕佹眰浠ヤ竴涓ぇ鍐欏瓧姣嶅紑澶寸殑锛屼互涓涓ぇ鍐欏瓧姣嶅紑澶寸殑鏂囦欢鍛藉悕涔熷緢瀹规槗銆傚洜姝ゅ湪鎺у埗鍣ㄥ唴瑁呰浇涓涓ā鍨嬶紝搴斿啝浠ヤ竴涓ぇ鍐欏瓧姣嶅紑澶寸殑鍚嶅瓧锛屽锛($this->load->Mymodel)銆傝學indows鍜孡inux鍒欏彲鑳介氳繃$this->mymodel璋冪敤涓嶅悓鐨勮鍥俱
+
+> As an extreme example of server differences, I once began to write a controller, decided to make it a model instead, and so saved it in the model folder without realizing that I had left the first few lines as:
+> 浣滀负涓嶅悓鏈嶅姟鍣ㄧ殑涓涓瀬绔緥瀛, 鎴戞湁涓娆″紑濮嬪啓涓涓帶鍒跺櫒,骞跺喅瀹氫慨鏀逛娇瀹冩垚涓轰竴涓ā鍨, 鎴戞妸瀹冧繚瀛樺湪moel鐩綍涓紝鍗存病鏈夋剰璇嗗埌鎴戝凡缁忓啓浜嗗ご鍑犺浠g爜濡備笅锛
+浣滀负鏈嶅姟鍣ㄥ樊寮傜殑涓涓瀬绔緥瀛愶紝鎴戞湁涓娆″啓涓涓帶鍒跺櫒锛屽悗鍐冲畾灏嗗叾淇敼涓轰竴涓ā鍨嬶紝鎴戞妸瀹冧繚瀛樺湪model鐩綍涓紝鍗存病鏈夋剰璇嗗埌鎴戝湪寮澶村凡缁忓啓浜嗗嚑琛屼唬鐮侊細
+
+> class Myclass extends Controller {
+> function Myclass() {
+> parent::Controller();
+
+
+> instead of changing them to:
+> 鑰屼笉鏄妸瀹冩敼鎴愶細
+鑰屼笉鏄妸瀹冩敼鎴愶細
+
+> class Myclass extends Model {
+> function Myclass() {
+> parent::Model();
+
+
+> Running locally on Xampplite, this did not throw an exception. Transferred to a remote Linux server, it immediately failed (and you can imagine how long it took to track down鈥).
+> 鍦ㄦ湰鍦扮殑Xampplite涓婅繍琛岋紝杩欐病鏈夊嚭閿欍 杩佺Щ鍒颁竴涓繙绋嬬殑Linux鏈嶅姟鍣ㄤ笂, 绔嬪埢鎶ラ敊銆(鑰屼笖浣犺兘鎯冲儚璋冭瘯瀹冭姳璐逛簡澶氶暱鏃堕棿鈥)
+鍦ㄦ湰鍦扮殑Xampplite涓婅繍琛岋紝娌℃湁鍑洪敊銆備絾杩佺Щ鍒颁竴涓繙绋嬬殑Linux鏈嶅姟鍣ㄤ笂鏃讹紝绔嬪埢鎶ラ敊銆傦紙浣犲簲璇ヨ兘鎯冲儚璋冭瘯瀹冭姳璐逛簡澶氶暱鏃堕棿鈥︹︼級
+
+> Some PHP functions also appear to behave differently on different operating systems: for instance, include_once() is case insensitive on Windows but not on other systems. That's not specifically a CI issue, though.
+> 涓浜 PHP 鍑芥暟鍦ㄤ笉鍚岀殑鎿嶄綔绯荤粺涓婁技涔庝篃鏈変笉鍚岀殑琛屼负琛ㄧ幇: 涓句緥鏉ヨ锛 include_once()鍦╳indows涓婃槸澶у皬鍐欐晱鎰熺殑锛屼絾鍦ㄥ叾瀹冪郴缁熶笂涓嶆槸銆傝櫧鐒惰繖涓嶤I鏈韩鏃犲叧銆
+涓浜汸HP鍑芥暟鍦ㄤ笉鍚岀殑鎿嶄綔绯荤粺涓婁篃鏈変笉鍚岀殑琛ㄧ幇锛氫妇渚嬫潵璇达紝include_once()鍦╳indows涓婃槸澶у皬鍐欐晱鎰熺殑锛屼絾鍦ㄥ叾瀹冪郴缁熶笂涓嶆槸銆傝櫧鐒惰繖涓嶤I鏈韩娌′粈涔堝叧绯汇
+
+> Also, your database may be a different version鈥攎any ISPs are very traditional, running MySQL 3.23 for instance! This seems to cause some incompatibilities, which means that uploading a database by a SQL query is less easy than it should be. (For instance, it may not accept comments on db tables.)
+> 鍚屾椂锛屼綘鐨勬暟鎹簱鍙兘鏄竴涓笉鍚岀殑鐗堟湰-璁稿ISP杩樺湪浣跨敤MySQL 3.23! 杩欎技涔庡紩璧蜂竴浜涗笉鍏煎, 杩欐剰鍛崇潃锛岄氳繃SQL鏌ヨ涓婁紶涓涓暟鎹簱涓嶆槸寰堝鏄撳疄鐜般(涓句緥鏉ヨ锛屽畠鍙兘涓嶆帴鍙楁暟鎹〃鐨勬彁浜ゃ)
+鍚屾椂锛屼綘鐨勬暟鎹簱涔熷彲鑳戒笉鏄悓涓涓殑鐗堟湰鈥斺旇澶欼SP杩樺湪浣跨敤MySQL 3.23锛佽繖涔熻寮曡捣涓浜涗笉鍏煎锛岃繖鎰忓懗鐫锛岄氳繃SQL涓婁紶涓涓暟鎹簱涓嶆槸寰堝鏄撳疄鐜扮殑銆傦紙渚嬪锛屽畠鍙兘涓嶆帴鍙楁暟鎹〃鐨勬彁浜ゃ傦級
+
+> Linux has a different system of file permissions to Windows. Make sure that you have the correct permissions on your files and folders. Certain CI file permissions must be set correctly before the system can work.
+> Linux涓巜indows鐩告瘮锛屾湁涓涓笉鍚岀殑鏂囦欢鏉冮檺绠$悊绯荤粺銆 纭畾浣犳湁瀵瑰簲鐨勬枃浠跺拰鐩綍鐨勬潈闄愩侰I鏈夊嚑涓洰褰曟枃浠剁殑鏉冮檺鍦ㄥ畠鑳藉姝e父杩愯鍓嶅繀椤绘纭缃
+Linux涓嶹indows鐩告瘮锛屾湁涓涓笉鍚岀殑鏂囦欢鏉冮檺绠$悊绯荤粺銆傝纭畾浣犳湁瀵瑰簲鐨勬枃浠跺拰鐩綍鐨勬潈闄愩侰I鏈夊嚑涓洰褰曟枃浠剁殑鏉冮檺鍦ㄥ畠鑳藉姝e父杩愯鍓嶅繀椤绘纭缃
+
+> Diagnostic Tools
+> 璇婃柇宸ュ叿
+璇婃柇宸ュ叿
+
+> The first line of the index.php file is:
+> index.php鏂囦欢鐨勭涓琛屾槸:
+index.php鏂囦欢鐨勭涓琛屾槸:
+
+> error_reporting(E_ALL);
+
+
+> which displays any PHP errors on your screen, like this:
+> 浼氬湪浣犵殑灞忓箷涓婃樉绀烘墍鏈夌殑 PHP 閿欒銆
+杩欎細鍦ㄥ睆骞曚笂鏄剧ず鎵鏈夌殑PHP閿欒銆
+
+> Obviously, error reports like this look bad, and may give hackers too much information, so for the production version you alter this to:
+> 寰堟樉鐒讹紝杩欑閿欒鎶ュ憡寰堢碂绯曪紝鍙兘缁欓粦瀹㈠お澶氱殑淇℃伅锛屾墍浠ュ湪浜у搧鏈嶅姟鍣ㄤ笂浣犲簲璇ユ敼鎴愶細
+寰堟樉鐒讹紝杩欑閿欒鎶ュ憡寰堢碂绯曪紝鍙兘浼氱粰榛戝澶淇℃伅锛屾墍浠ュ湪浜у搧鏈嶅姟鍣ㄤ笂浣犲簲璇ユ敼鎴愶細
+
+> error_reporting;(0)
+
+
+> But then, any problems may simply result in a blank screen, with no helpful diagnostic information. You may have to turn error reporting back on, until you have got the site running. One compromise is to set it to an intermediate level, such as:
+> 浣嗘槸鐒跺悗锛屼换浣曠殑闂鍙兘鍙槸閫犳垚涓涓┖鐧界殑灞忓箷锛屾病鏈夊彲鎻愪緵甯姪鐨勮瘖鏂俊鎭 浣犲彲鑳藉繀椤绘妸閿欒鎶ュ憡鍐嶆墦寮鐩村埌浣犲凡缁忎娇缃戠珯姝e父杩愯銆 涓涓姌涓殑鍋氭硶鏄妸瀹冭瀹氫负涓涓腑闂寸姸鎬, 鍍忔槸:
+浣嗘槸锛屽緢澶氶棶棰橀兘鍙兘鍙槸閫犳垚涓涓櫧灞忥紝娌℃湁鍙緵鍙傝冪殑璇婃柇淇℃伅銆傝繖鏃朵綘鍙兘蹇呴』鎶婇敊璇姤鍛婂啀鎵撳紑锛岀洿鍒颁綘宸茬粡浣跨綉绔欏彲浠ユ甯歌繍琛屻備竴涓姌涓殑鍋氭硶鏄妸瀹冭瀹氫负涓涓腑闂寸姸鎬侊紝鍍忔槸锛
+
+> error_reporting(E_ERROR);
+
+
+> This will prevent 'warnings' but will still give you information about serious problems. 'Warnings' are usually conditions that don't stop the programme from executing, but may point to other underlying issues that you hadn't considered.
+> 杩欏皢浼氶伩鍏 'warning' 浣嗘槸浠嶇劧浼氱粰浣犲叧浜庝弗閲嶇殑闂淇℃伅銆 'warning' 閫氬父涓嶄細涓绋嬪簭鐨勬墽琛, 浣嗘槸鍙兘杞悜鍏跺畠浣犳病鏈夎冭檻鍒扮殑闂銆
+杩欏皢浼氶伩鍏嶁渨arnings鈥濓紝浣嗕粛鍙粰浣犲叧浜庝弗閲嶉棶棰樼殑淇℃伅銆傗渨arnings鈥濋氬父涓嶄細涓绋嬪簭鐨勬墽琛岋紝浣嗗彲鑳戒細寮曞彂鍏跺畠浣犳病鏈夎冭檻鍒扮殑闂銆
+
+> The CI Profiler class鈥攕ee Chapter 8鈥攊s also very useful: it shows you what queries you are doing, and what is in the POST array.
+> CI Profiler 绫-瑙佺 8 绔-涔熼潪甯告湁鐢: 瀹冩樉绀轰綘姝e湪鍋氫粈涔堟煡璇, POST鏁扮粍鐨勫唴瀹规槸浠涔堛
+CI鐨凱rofiler绫烩斺旇瑙佺8绔犫斺斾篃闈炲父鏈夌敤锛氬畠鍙互鏄剧ず浣犳鍦ㄥ仛浠涔堟煡璇紝浠ュ強POST鏁扮粍鐨勫唴瀹规槸浠涔堢瓑銆
+
+> Various other tools I have found useful when things don't work:
+> 褰撹繖浜涘伐鍏烽兘涓嶈捣浣滅敤鏃讹紝闇瑕佷娇鐢ㄥ叾瀹冪殑宸ュ叿锛屼笅闈㈠垪鍑轰竴浜涙垜宸茬粡鍙戠幇鐨勫叾瀹冨伐鍏:
+褰撲笂杩板伐鍏烽兘涓嶈捣浣滅敤鏃讹紝灏遍渶瑕佷娇鐢ㄥ叾瀹冪殑宸ュ叿锛屼笅闈㈠垪鍑轰簡鎴戞壘鍒扮殑涓浜涘伐鍏:
+
+> 1. Set CI to print a log file. (Done from the config file鈥攕ee Chapter 8鈥攜ou need to set:
+> 1. 璁惧畾 CI 寮鍚棩蹇楁枃浠躲(閫氳繃淇敼 config 鏂囦欢瀹炵幇-瑙佺 8 绔-浣犻渶瑕佽瀹:
+1. 璁惧畾CI寮鍚棩蹇楁枃浠躲傦紙鍙氳繃淇敼config鏂囦欢瀹炵幇锛岃瑙佺8绔狅級锛
+
+
+> $config['log_threshold']=4;
+
+
+> 4 shows all messages, including just notices and warnings; sometimes theseare pointers to an underlying problem. Then look at the log (printed in/system/logs, filed by date.) This will tell you which parts of the CI system have been called, so you can at least see where the process stopped. (Set thevalue back to 0 to prevent further logging. Remember to do this, and to removethe log files when you've finished: it's amazing how much space they take up.)
+> 4鏄剧ず鎵鏈夌殑淇℃伅, 鍖呮嫭notices鍜寃arnings; 鏈夋椂杩欎簺浼氭樉绀烘綔鍦ㄧ殑闂銆 鐒跺悗鏌ョ湅鏃ュ織鏂囦欢 (淇濆瓨鍦/system/logs涓.) 杩欏皢浼氬憡璇変綘鍝竴涓 CI 閮ㄥ垎宸茬粡琚皟鐢紝鍥犳锛屼綘鑳借嚦灏戣兘鐪嬪埌绋嬪簭涓鐨勫湴鏂广 (鎶婂艰瀹氬洖 0 浼氬仠姝㈡棩蹇楄褰曘 璁板緱杩欐牱鍋, 璋冭瘯瀹屾瘯鍚庤鍒犻櫎鏃ュ織鏂囦欢: 瀹冨昂浜哄湴鍗犵敤绌洪棿銆)
+鈥4鈥濇樉绀烘墍鏈夌殑淇℃伅锛屽寘鎷琻otices鍜寃arnings锛屾湁鏃惰繖浼氭樉绀哄嚭娼滃湪鐨勯棶棰樸傜劧鍚庢煡鐪嬫棩蹇楁枃浠讹紙淇濆瓨鍦/system/logs涓紝渚濇棩鏈熸帓搴忥級锛岃繖灏嗕細鍛婅瘔浣燙I鐨勫摢涓儴鍒嗗凡缁忚璋冪敤锛屽洜姝わ紝浣犺兘鑷冲皯鑳界湅鍒扮▼搴忎腑姝㈢殑鍦版柟銆傦紙鎶婂艰瀹氬洖鈥0鈥濅細鍋滄鏃ュ織璁板綍銆傝璁板緱瑕佽繖鏍峰仛锛岃皟璇曞畬姣曞悗鍒犻櫎鏃ュ織鏂囦欢锛氬畠闈炲父鍗犵敤绌洪棿銆傦級
+
+> 2. If you can access them, print out the PHP server and session variables:
+> 2. 濡傛灉浣犺兘瀛樺彇浠栦滑, 鎵撳嵃鍑 PHP 鏈嶅姟鍣ㄥ拰浼氳瘽鍙橀噺:
+濡傛灉浣犺兘鍙栧緱PHP鐨勬湇鍔″櫒鍜屼細璇濆彉閲忥紝璇疯緭鍑哄畠浠細
+
+> print_r($_SERVER)
+
+> and:
+> 鑰屼笖:
+浠ュ強锛
+
+> print_r($_SESSION)
+
+
+> and use them to check that the document_root and script_filename values are what you expect. If not, you may need to adjust your config file values for base_url and server You can also see if there is an [HTTP_COOKIE]value set, which will show you if your session class is enabled and working.
+> 鑰屼笖浣跨敤浠栦滑妫鏌 document_root 鍜 script_filename 鍊兼槸涓嶆槸浣犳剰鏂欎箣涓殑銆傚鏋滀笉鏄紝浣犲彲鑳介渶瑕佽皟鏁碿onfig鏂囦欢涓殑base_url锛宻erver鐨勫肩殑璁剧疆锛屼綘杩樺彲浠ョ湅涓涓嬫槸鍚︽湁 [HTTP_COOKIE]鐨勮缃紝瀹冧細鏄剧ず鏄惁浣犵殑session绫昏兘澶熷伐浣溿
+骞朵娇鐢ㄤ粬浠鏌ocument_root鍜宻cript_filename鐨勫兼槸涓嶆槸浣犳墍棰勬湡鐨勩傚鏋滀笉鏄紝浣犲彲鑳借璋冩暣config鏂囦欢涓殑base_url銆乻erver鐨勮缃紝浣犺繕鍙互鐪嬩竴涓嬫槸鍚︽湁[HTTP_COOKIE]鐨勮缃紝瀹冧細鏄剧ず鏄惁浣犵殑session绫昏兘澶熷伐浣溿
+
+> 3. Check what CI has loaded into its superobject by using PHP's methods:
+> 3. 鐢≒HP鐨勬柟娉曟鏌 CI 鎶婁粈涔堣杞借繘瀹冪殑鈥滆秴绾у璞♀:
+鐢≒HP鐨勬柟娉曟鏌I鎶婁粈涔堣杞借繘瀹冪殑鈥滆秴绾у璞♀濓細
+
+> get_declared_classes()锛
+
+> and:
+> 鍜:
+浠ュ強锛
+
+> get_class_methods()锛
+
+> 4. CI's own show_error() function is only a means of formatting error reportsthat you generate. So including the following line in your code, say at somebranch that should not be reached:
+> 4. CI 鑷繁鐨 show_error() 鍑芥暟鍙槸鏍煎紡鍖栫敓鎴愮殑閿欒鎶ュ憡. 鍥犳鍦ㄤ綘鐨勪唬鐮佷腑鍔犲叆涓嬮潰鐨勮繖涓琛岋紝鍙互鏄剧ず鍑虹▼搴忚繍鍒板埌鍝竴涓懡浠ゅ垎鏀細
+CI鑷繁鐨剆how_error()鍑芥暟鍙槸鏍煎紡鍖栫敓鎴愮殑閿欒鎶ュ憡銆傚洜姝ゅ湪浠g爜涓姞鍏ヤ笅闈㈢殑杩欎竴琛岋紝鍙互鏄剧ず鍑虹▼搴忔病鏈夎繍琛屽摢涓涓懡浠ゅ垎鏀細
+
+> show_error('test of error function');
+
+
+> would result in your screen showing:
+> 浼氬湪浣犵殑灞忓箷涓婃樉绀:
+浼氬湪灞忓箷涓婃樉绀猴細
+
+> test of error function
+
+
+> I don't find that very useful. What I want is a system that will give me full, helpful error reports, when and where I want them, but won't show them when I don't want them to appear. I wrote my own function, which reads:
+> 鎴戞病鏈夊彂鐜拌繖闈炲父鏈夌敤銆 褰撴垜鎯宠绯荤粺鍦ㄦ垜闇瑕佹椂缁欐垜涓涓畬鏁寸殑鏈夊府鍔╃殑閿欒鎶ュ憡, 鍦ㄤ綍鏃朵互鍙婂摢閲屾壘鍒颁粬浠, 浣嗘槸褰撴垜涓嶆兂瑕佷粬浠嚭鐜扮殑鏃跺欏皢涓嶆樉绀轰粬浠 鎴戦渶瑕佺紪鍐欐垜鑷繁鐨勫嚱鏁帮紝濡備笅:
+鎴戝苟娌℃湁鍙戠幇杩欏鏈夌敤銆傚鏋滃笇鏈涚郴缁熷湪蹇呰鐨勬椂闂村拰浣嶇疆缁欐垜涓涓畬鏁寸殑鏈夊府鍔╃殑閿欒鎶ュ憡锛屼笖褰撴垜涓嶆兂瑕佺殑鏃跺欏氨涓嶆樉绀哄畠浠紝灏卞緱缂栧啓鎴戣嚜宸辩殑鍑芥暟浜嗭紝濡備笅锛
+
+> 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);
+> }
+> }
+
+
+> This lives in a library called errors. I have to remember to load the library, and then, whenever I have a section of code that I'm not confident about, I include the function:
+> 鎶婂畠鏀惧湪涓涓彨鍋歟rrors鐨刲ibrary鏂囦欢涓傛垜闇瑕佽杞借繖涓猯ibrary锛 鐒跺悗, 姣忓綋鎴戝涓娈典唬鐮佹病鏈変俊蹇冪殑鏃跺欙紝鎴慽nclude杩欎釜鍑芥暟锛
+鎶婂畠鏀惧湪涓涓彨鍋歟rrors鐨刲ibrary鏂囦欢涓傛垜闇瑕佽杞借繖涓猯ibrary锛 鐒跺悗, 姣忓綋鎴戝鏌愪竴娈典唬鐮佷笉鏄緢纭畾鏃讹紝鎴戝氨浼歩nclude杩欎釜鍑芥暟锛
+
+> $this->errors->reportme(__FILE__,__LINE__,'if the code has reached here it is becase...');
+
+
+> I can then set the reportme() function to give me a report on the screen, or in my log file.
+> 鐒跺悗鎴戣兘璁惧畾 reportme() 鍑芥暟鍦ㄥ睆骞曚笂鏄剧ず, 鎴栦繚瀛樺湪鏃ュ織鏂囦欢涓
+鎴戣繕鍙互璁惧畾reportme()鍑芥暟鏄惁灞忓箷涓婃樉绀猴紝鎴栨槸鍚︿繚瀛樺湪鏃ュ織鏂囦欢涓
+
+> There are several advantages to a simple method like this. Firstly, I can alter the reportme() function easily, to make it write errors to a file, or to do nothing at all: so I can make all my reports disappear from the screen at once, or come back again, by changing one line of code.
+> 杩欐牱涓涓畝鍗曠殑鏂规硶鐨勬湁鍑犱釜浼樼偣锛氱涓锛屾垜鑳藉鏄撳湴淇敼 reportme() 鍑芥暟, 浣垮畠鍐欓敊璇俊鎭埌涓涓枃浠, 鎴栧叏鐒朵粈涔堜篃涓嶅仛: 鍥犳鎴戣兘绔嬪埢浣挎垜鎵鏈夌殑閿欒淇℃伅浠庡睆骞曟秷澶, 鎴栧啀鍥炴潵, 鍙淇敼涓琛屼唬鐮併
+杩欎釜绠鍗曟柟娉曟湁鍑犱釜浼樼偣锛氱涓锛屾垜鑳藉鏄撳湴淇敼reportme()鍑芥暟, 璁╁畠灏嗛敊璇俊鎭啓鍒颁竴涓枃浠朵腑锛屾垨浠涔堜篃涓嶅仛锛氬洜姝ゆ垜鍙淇敼涓琛屼唬鐮侊紝灏卞彲浠ョ珛鍒讳娇鎵鏈夌殑閿欒淇℃伅浠庡睆骞曚笂娑堝け锛屾垨鍐嶆樉绀哄嚭鏉ャ
+
+> Secondly, let's say I am expecting a variable to have a particular value. (An ID number to be an integer, say.) I make the message as complete and helpful as possible. I try to say what I expected to find (an integer), as well as including the value I actually got. The function call also uses PHP's __FILE__ and __LINE__ 'magic constants' to tell me exactly where it happened.
+> 绗簩锛屾垜鐢熸垚涓涓寘鍚壒鍒殑鍊肩殑鍙橀噺銆 (涓涓狪D锛岀被鍨嬩负鏁村瀷,姣旀柟璇淬) 鎴戠敓鎴愪竴涓敖鍙兘瀹屾暣鍜屾湁甯姪鐨勪俊鎭傛垜璇曠潃鎵惧埌涓涓暣鏁, 杩炲悓鎴戝疄闄呬笂寰楀埌鐨勫笺 鍑芥暟涔熺敤PHP__FILE__ 鍜 __LINE__榄旀湳甯告暟瀹屾暣鍦板憡璇夋垜瀹冨彂鐢熺殑鍦版柟銆
+绗簩锛屾瘮濡傛垜鐢熸垚浜嗕竴涓寘鍚壒瀹氬肩殑鍙橀噺銆傦紙濡備竴涓狪D锛岀被鍨嬩负鏁村瀷銆傦級鐒跺悗鐢熸垚涓涓敖鍙兘瀹屾暣涓旀湁甯姪鐨勪俊鎭傛垜棰勬湡浼氭壘鍒颁竴涓暣鏁帮紝涔熷寘鍚簡杩欎釜鍊硷紝鑰屾垜纭疄鎵惧埌浜嗐傝鍑芥暟浼氱敤鈥滈瓟鏈父鏁扳漃HP__FILE__鍜宊_LINE__瀹屾暣鍦板憡璇夋垜瀹冨彂鐢熺殑鍦版柟銆
+
+> So, if this particular piece of code becomes a problem when I transfer it to another server, possibly some time after I wrote it, I can immediately find the code, and the text helps me to remember why it is a problem. Six months after you write code, you can't just pick it up straight away, especially if it is late at night and a client is on the phone asking for an explanation! The more helpful the error text, the easier it is to respond sensibly.
+> 鍥犳,濡傛灉褰撴垜鎶婄▼搴忚縼绉诲埌鍙﹀鐨勪竴涓湇鍔″櫒鐨勬椂鍊欙紝杩欏潡鐗瑰埆鐨勪唬鐮佸湪鎴戝啓浜嗕竴娈垫椂闂翠箣鍚庡鏋滃嚭鐜颁竴涓棶棰橈紝鎴戣兘绔嬪埢鎵惧埌杩欐浠g爜锛岃屼笖鏂囧瓧淇℃伅甯姪鎴戣璧峰畠涓轰粈涔堟槸涓涓棶棰樸 鍦ㄤ綘缂栫▼瀹屾垚涔嬪悗鍏釜鏈堬紝浣犱笉鍙兘椹笂寮勬槑鐧, 灏ゅ叾濡傛灉瀹冨彂鐢熷湪娣卞锛屼竴浣嶅鎴峰湪鐢佃瘽涓姹備綘缁欎粬瑙i噴浠涔堢殑!鏇存湁甯姪鐨勯敊璇湰鏂,灏嗕娇浣犲鏄撳湴浣滃嚭鍙嶅簲銆
+鍥犳锛屽鏋滄垜鎶婄▼搴忚縼绉诲埌鍙︿竴涓湇鍔″櫒鍚庡嚭鐜颁簡涓涓棶棰橈紝鎴戣兘绔嬪埢閫氳繃杩欐浠g爜鎵惧埌闂鎵鍦紝涓旀枃瀛椾俊鎭彲浠ュ府鍔╂垜璁拌捣瀹冩槸涓涓粈涔堥棶棰樸傚湪浣犵紪绋嬪畬鎴愬叚涓湀鍚庯紝浣犱笉鍙兘椹笂寮勬槑鐧斤紝灏ゅ叾鏄綋娣卞涓浣嶅鎴峰湪鐢佃瘽涓姹備綘缁欎粬鍋氭儏鍐佃В閲婃椂锛佹洿鏈夊府鍔╃殑閿欒鏂囨湰锛屽皢甯綘鏇村鏄撳湴浣滃嚭鍙嶅簲銆
+
+> Thirdly, if the integrity of the site was really critical, I could set the function to email me with error reports. That might result in a very full mailbox during the development phase, but once the site is stable and in use, it might be very useful to have an immediate warning if the site is experiencing problems. You will know about them before your users tell you.
+> 绗笁锛屽鏋滅綉绔欑殑瀹屾暣鎬х湡鐨勮嚦鍏崇揣瑕侊紝鎴戝彲浠ヨ瀹氬嚱鏁扮敤鐢甸偖鎶婇敊璇姤鍛婂彂閫佺粰鎴戙傚湪寮鍙戦樁娈靛彲鑳介犳垚閭鐖嗘弧锛屼絾鏄竴鏃︾綉绔欒繍琛岀ǔ瀹氾紝濡傛灉缃戠珯閬囧埌闂锛屾湁涓涓嵆鏃剁殑璀﹀憡閭欢鍙戠粰鎴戜細鏄潪甯告湁鐢ㄧ殑銆備綘灏嗕細鍦ㄤ綘鐨勭敤鎴风煡閬撲箣鍓嶅氨鍙戠幇闂銆
+绗笁锛屽鏋滅綉绔欑殑瀹屾暣鎬х湡鐨勮嚦鍏抽噸瑕侊紝鍙互璁惧畾涓涓嚱鏁颁互鐢靛瓙閭欢褰㈠紡灏嗛敊璇姤鍛婂彂閫佺粰鎴戙傚湪寮鍙戦樁娈佃繖鍙兘浼氶犳垚閭鐖嗘弧锛屼絾涓鏃︾綉绔欐寮忚繍琛屽悗锛屽綋閬囧埌闂鏃讹紝鏈変竴涓嵆鏃剁殑璀﹀憡閭欢鍙戠粰鎴戞槸闈炲父鏈夌敤鐨勩備綘灏嗕細鍦ㄤ綘鐨勭敤鎴风煡閬撲箣鍓嶅氨鍙戠幇闂銆
+
+> Coping with Change in New CI Versions
+> 搴斿鏂扮殑CI鐗堟湰甯︽潵鐨勫彉鍖
+鏂扮増CI甯︽潵鐨勫彉鍖
+
+> Between 28 February 2006 and 30 October 2006, CI went from its first beta to version 1.5. That's a pretty impressive rate of development.
+> 鍦2006骞2鏈28鏃ュ埌2006骞10鏈30鏃ヤ箣闂达紝 CI 浠庡畠鐨勭涓涓狟eta鐗堝崌绾у埌1.5鐗堛 閭f槸鐩稿綋浠や汉鍗拌薄娣卞埢鐨勫崌绾с
+鍦2006骞2鏈28鏃ュ埌2006骞10鏈30鏃ヤ箣闂达紝CI浠庡畠鐨勭涓涓狟eta鐗堝崌绾у埌浜1.5鐗堛傝繖涓崌绾ц繃绋嬩护浜哄嵃璞$浉褰撴繁鍒汇
+
+> During that time Rick Ellis made several fairly radical changes, particularly to the structure of the site. For the most part, he has been careful to make them backwardly compatible鈥攂ut not all of them are. If you are new to CI and have downloaded the latest version, you can skip this section. But if you wrote programmes using earlier versions, you may need to check these changes. You may also need to check if you are using CI libraries or plug-ins written by other people.
+> 鍦ㄩ偅鏈熼棿锛孯ick Ellis鍋氫簡涓浜涢潪甯告縺杩涚殑鍙樺寲, 鐗瑰埆鍦板湪缃戠珯鐨勭粨鏋勪笂銆傚ぇ鑷翠笂锛屼粬宸茬粡灏忓績浣夸粬浠悜鍚庡吋瀹-浣嗘槸骞舵病鏈夊畬鍏ㄥ仛鍒般傚鏋滀綘鍒氫娇鐢 CI 骞朵笅杞戒簡鏈鏂扮増鏈紝浣犺兘澶熻烦瓒婅繖涓涓樁娈点備絾鏄鏋滀綘浠啓浜嗕娇鐢ㄨ緝鏃╃殑鐗堟湰鐨勭▼搴忋傚鏋滀綘姝e湪浣跨敤 CIlibraries鎴栬呮槸鍏朵粬浜哄啓鐨刾lug-ins銆
+鍦ㄩ偅鏈熼棿锛孯ick Ellis鍋氫簡涓浜涢潪甯告縺杩涚殑鏇存柊锛岀壒鍒湴鍦ㄧ綉绔欑殑缁撴瀯涓娿傚ぇ鑷翠笂锛屼粬宸茬粡娉ㄦ剰鍒伴渶瑕佸悜鍚庡吋瀹光斺斾絾骞舵病鏈夊畬鍏ㄥ仛鍒般傚鏋滀綘鍒氫娇鐢–I骞朵笅杞戒簡鏈鏂扮増鏈紝浣犲彲浠ヨ烦杩囪繖涓娈点備絾濡傛灉浣犱篃浣跨敤浜嗚緝鏃╃増鏈殑绋嬪簭锛屾垨鍦ㄧ敤鍏朵粬浜哄啓鐨凜I libraries鎴杙lug-ins锛屼綘灏遍渶瑕佺‘璁や竴浜涘彉鍖栥
+
+> Rick has grappled with two main problems:
+> 鐟炲厠宸茬粡鍔姏瑙e喅浜屼釜涓昏鐨勯棶棰:
+鐟炲厠宸茬粡鍔姏澶勭悊浜嗕袱涓富瑕侀棶棰橈細
+
+> How to Load Models, and What to Call Them
+> 璇ュ浣曡杞芥ā鍨嬨佸拰濡備綍璋冪敤浠栦滑
+搴斿浣曡杞芥ā鍨嬶紝浠ュ強濡備綍璋冪敤浠栦滑
+
+> At first, there were no models, just folders for scripts and libraries. There was no provision to initialize them automatically as part of the CI 'super-object'. As a result, you had an MVC system without 'model' files, which seems confusing.
+> 璧峰厛锛屾病鏈夋ā鍨嬶紝鍙槸浣跨敤鐩綍鏉ョ鐞嗚剼鏈拰libraries銆傛病鏈夊噯澶囪鑷姩鍦板垵濮嬪寲瀹冧滑浣滀负CI瓒呯骇瀵硅薄鐨勪竴閮ㄤ唤銆傜粨鏋滄槸, 浣犳湁浜嗕竴涓 MVC 绯荤粺娌℃湁 'M' 鏂囦欢,浼间箮浠や汉鍥版儜銆
+璧峰厛锛屾病鏈夋ā鍨嬶紝鍙槸閫氳繃鐩綍鏉ョ鐞嗚剼鏈拰libraries銆傚苟娌″噯澶囪嚜鍔ㄥ湴鍒濆鍖栧畠浠綔涓篊I瓒呯骇瀵硅薄鐨勪竴閮ㄥ垎銆傜粨鏋滄湁浜嗕竴涓己灏戔滄ā鍨嬧濈殑MVC绯荤粺锛岃繖寰堣浜鸿糠鎯戙
+
+> As well as this, there are two libraries folders: /system/application/libraries holds any files you write for yourself, while /system/libraries holds the system's own operating files. This may have confused a few people: the two are quite different! You ought to be adding to or altering the former; you probably don't need to alter the latter. (And if you do, you run serious risks of incompatibility if you upgrade to a later CI version: see below.)
+> 涓嶄粎杩欐牱锛岃繕鏈変簩涓猯ibraries鐩綍: /system/application/libraries淇濆瓨浣犱负鑷繁缂栧啓鐨勪竴浜涙枃浠, 鑰/system/libraries淇濆瓨绯荤粺鑷繁鐨勬搷浣滄枃浠躲 杩欏彲鑳借涓浜涗汉鎰熷埌娣蜂贡: 杩欎簩鑰呬箣闂村瓨鍦ㄥ緢澶х殑宸紓!浣犲簲璇ュ鍔犲埌鎴栨敼鍙樺墠涓涓洰褰曚腑鐨勬枃浠讹紝浣嗕綘鎴栬涓嶉渶瑕佹敼鍙樺悗鑰咃紝褰撶劧杩欏緢瀹规槗鎼為敊銆 (鑰屼笖濡傛灉浣犺繖鏍峰仛浜嗭紝濡傛灉浣犲 CI鐗堟湰鍗囩骇锛屼綘浼氶潰涓存棤娉曞吋瀹圭殑涓ラ噸鍗遍櫓: 鍦ㄤ笅闈㈠彲浠ョ湅鍒般)
+涓嶄粎杩欐牱锛岃繕鏈変袱涓猯ibraries鐩綍锛/system/application/libraries淇濆瓨浣犱负鑷繁缂栧啓鐨勪竴浜涙枃浠讹紝鑰/system/libraries鍒欎繚瀛樼郴缁熻嚜宸辩殑鎿嶄綔鏂囦欢銆傝繖鍙兘浼氳浜虹硦娑傦細杩欎簩鑰呬箣闂村畬鍏ㄤ笉鍚岋紒浣犲簲璇ュ鍔犳垨鏀瑰彉鍓嶄竴鐩綍涓殑鏂囦欢锛屼絾浣犱篃璁镐笉鐢ㄦ敼鍙樺悗鑰咃紝杩欏緢瀹规槗鎼為敊銆傦紙鑰屼笖濡傛灉浣犺繖鏍峰仛浜嗭紝褰撳崌绾I鐗堟湰鏃讹紝浣犲皢浼氶潰涓翠笉鍏煎鐨勫嵄闄╋細鍦ㄤ笅闈㈠彲浠ョ湅鍒般傦級
+
+> With version 1.3 came a new 'model' class. The User Guide defines models as, "PHP classes that are designed to work with information in your database". When first introduced, CI models connected automatically to the database. However, since Version 1.3.3, you must specifically load the database from inside the model or the controller that calls it.
+> 1.3 鐗堝甫鏉ヤ簡涓涓柊鐨 'Model'绫汇 鐢ㄦ埛鎵嬪唽瀹氫箟妯″瀷涓" 琚璁$敤鏉ヤ笌浣犵殑鏁版嵁搴撲腑鐨勬暟鎹悎浣滅殑PHP绫 ". 褰撶涓娆′娇鐢ㄦ椂, CI 妯″瀷鑷姩鍦颁笌鏁版嵁搴撹繛鎺ャ 鐒惰, 浠1.3.3 鐗堣捣, 浣犱竴瀹氳鍦ㄦā鍨嬫垨鎺у埗鍣ㄤ腑鏄惧紡鍦拌繛鎺ユ暟鎹簱銆
+1.3鐗堝甫鏉ヤ簡涓涓柊鐨勨淢odel鈥濈被銆傜敤鎴锋墜鍐屼腑灏嗘ā鍨嬪畾涔変负鈥滆璁$敤鏉ヤ笌浣犵殑鏁版嵁搴撲腑鐨勬暟鎹悎浣滅殑PHP绫烩濄傜涓娆′娇鐢ㄦ椂锛孋I妯″瀷鑷姩鍦颁笌鏁版嵁搴撹繛鎺ャ備絾浠1.3.3鐗堣捣锛屼綘灏卞繀椤昏鍦ㄦā鍨嬫垨鎺у埗鍣ㄤ腑鏄惧紡鍦拌繛鎺ユ暟鎹簱銆
+
+> Or, when you call the model from the controller, you can do so in this format:
+> 鎴栬, 褰撲綘浠庢帶鍒跺櫒璋冪敤妯″瀷鐨勬椂鍊欙紝浣犲彲浠ヤ互濡備笅鏍煎紡瀹炵幇:
+鎴栬咃紝褰撲綘浠庢帶鍒跺櫒璋冪敤妯″瀷鏃朵互濡備笅鏍煎紡瀹炵幇锛
+
+> $this->load->model('Mymodel', '', TRUE);
+
+
+> and then the 'TRUE' loads the model with the default database connection made, as defined in your config file. (The second parameter, left blank here, is an optional alias for the model.)
+> 鐒跺悗 'TRUE' 鎸囧畾褰撲笌榛樿鐨勬暟鎹簱杩炴帴鏃舵墠瑁呰浇妯″瀷,姝h薄浣犵殑 config 鏂囦欢鎵瀹氫箟鐨勯偅鏍枫 (绗簩涓弫鏁, 鍦ㄨ繖閲岀殑绌烘牸,鏄ā鍨嬬殑涓涓彲閫夋嫨鐨勫埆鍚.)
+鐒跺悗鈥淭RUE鈥濇寚瀹氬綋涓庨粯璁ょ殑鏁版嵁搴撹繛鎺ユ椂鎵嶈杞芥ā鍨嬶紝姝h薄浣犲湪config鏂囦欢涓墍閰嶇疆鐨勯偅鏍枫傦紙绗簩涓┖鐧藉弬鏁版槸妯″瀷涓竴涓彲閫夋嫨鐨勫埆鍚嶃傦級
+
+> CI will probably still work if you put the functionality of a 'model' (in the MVC sense) into a 'library' or a (deprecated) 'script', as you had to in the early days when there were no 'models' folders: but you'll have to access the CI resources differently鈥攕ee the next section!
+> 濡傛灉浣犳妸'妯″瀷 '鍔熻兘(鍦 MVC 鎰忎箟涓婄殑) 鏀惧埌涓涓 'library' 鎴栬呬竴涓 (澹版槑锛氫笉璧炴垚杩欎箞鍋)鑴氭湰涔嬪唴,CI 鎴栬杩樺彲浠ュ伐浣溿 杈冩棭鐨勭増鏈苟娌℃湁'model'鐩綍: 浣犱笉寰椾笉鐢ㄥ叾瀹冩柟寮忓瓨鍙朇I璧勬簮-瑙佸埌涓嬩竴涓尯娈!
+濡傛灉浣犳妸锛圡VC鎰忎箟涓婄殑锛夆滄ā鍨嬧濆姛鑳芥斁鍒颁竴涓渓ibrary鈥濇垨鑰呬竴涓 (澹版槑锛氫笉璧炴垚杩欒繖鏍峰仛)鑴氭湰涓紝CI鎴栬杩樺彲浠ュ伐浣溿傛棭鏈熺増鏈病鏈夆渕odel鈥濈洰褰曪細浣犲繀椤荤敤鍏跺畠鏂瑰紡瀛樺彇CI璧勬簮鈥斺旇瑙佷笅涓娈碉紒
+
+> How to Initialize Your Own 'library' Classes
+> 濡備綍鍒濆鍖栦綘鑷繁鐨 'library' 绫
+濡備綍鍒濆鍖栦綘鑷繁鐨勨渓ibrary鈥濈被
+
+> Originally, you couldn't make your own classes part of the CI 'super-object'. This was a problem, because it meant that your library code couldn't, for instance, access the database through Active Record, or use other CI libraries, and that became pretty limiting.
+> 鏈潵锛屼綘鏃犳硶浣夸綘鑷繁鐨勭被鎴愪负 CI 瓒呯骇瀵硅薄鐨勪竴閮ㄤ唤銆傝繖鏄竴涓棶棰橈紝鍥犱负瀹冩剰鍛崇潃浣犵殑library浠g爜涓嶈兘閫氳繃AR璇诲啓鏁版嵁搴擄紝鎴栦娇鐢ㄥ叾浠栫殑 CI library锛岃繖鏍风殑闄愬埗澶ぇ浜嗐
+鏈潵锛屼綘涓嶈兘璁╀綘鑷繁鐨勭被鎴愪负CI瓒呯骇瀵硅薄鐨勪竴閮ㄥ垎銆傝繖閲屾湁涓涓棶棰橈紝鍥犱负瀹冩剰鍛崇潃浣犵殑library浠g爜涓嶈兘閫氳繃AR璇诲啓鏁版嵁搴擄紝鎴栬呬娇鐢ㄥ叾浠栫殑CI library锛岃繖鏍峰氨杩囦簬鍙楅檺浜嗐
+
+> Version 1.2 added the get_instance() function that allows you to access the 'super-object'. (See Chapter 7.) You could include it in your 'library' or 'script' and then use the CI resources. (Unless your new file was a functional script rather than an OO class, of course. However, script files are probably best used for simple low-level functions.)
+> 1.2 鐗堟湰澧炲姞浜嗚瀵圭被鐨 get_instance() 鍑芥暟锛屽厑璁镐綘璇诲啓CI鐨勮秴绾у璞★紙瑙佺涓冪珷锛夛紝浣犲彲浠ュ湪浣犵殑 'library'鎴栬呰剼鏈腑include瀹冿紝鐒跺悗浣跨敤 CI 璧勬簮銆(闄ら潪浣犵殑鏂版枃浠舵槸涓涓嚱鏁拌剼鏈屼笉鏄竴涓 OO 绫汇傚綋鐒讹紝鑴氭湰鏂囦欢鍙兘鎬х敤鏉ョ紪鍐欑畝鍗曠殑鍩烘湰鍑芥暟鐨勯夋嫨銆)
+1.2鐗堟湰澧炲姞浜唃et_instance()鍑芥暟锛屽厑璁镐綘璇诲啓CI鐨勮秴绾у璞★紙璇﹁绗7绔狅級锛屼綘鍙互鍦ㄢ渓ibrary鈥濇垨鑰呰剼鏈腑include瀹冿紝鐒跺悗浣跨敤CI璧勬簮銆傦紙闄ら潪浣犵殑鏂版枃浠舵槸涓涓嚱鏁拌剼鏈屼笉鏄竴涓狾O绫汇傚綋鐒讹紝鑴氭湰褰㈠紡鍙兘鏄敤鏉ョ紪鍐欑畝鍗曠殑搴曞眰鍑芥暟鐨勬渶浣抽夋嫨銆)
+
+> Version 1.4 introduced a new system. You had to create two files for each 'library' class. The first was the class itself, say Newclass.php, stored in the application/ libraries folder, and the second, stored in an application/init folder, had to be called init_newclass.php and contained a few standard lines of code that initialized it as part of the 'super-object'. However, you still had to use the get_instance() function to access CI resources.
+> 1.4 鐗堝紩杩涗簡涓涓柊鐨勭郴缁熴備綘蹇呴』涓烘瘡涓'library'绫诲垱寤轰簩涓枃浠躲傜涓涓槸绫绘湰韬紝姣斿Newclass.php,淇濆瓨鍦 application/libraries鐩綍涓紝绗簩涓紝淇濆瓨鍦╝pplication/init鐩綍涓, 蹇呴』鍙仛init_newclass.php 蹇呴』鍖呭惈鍑犱釜鏍囧噯浠g爜琛岋紝鐢ㄦ潵鍒濆鍖栵紝璁╁畠鎴愪负CI瓒呯骇瀵硅薄鐨勪竴閮ㄥ垎锛屼綘浠嶇劧闇瑕佷娇鐢 get_instance()鍑芥暟瀛樺彇 CI 璧勬簮銆
+1.4鐗堝紩杩涗簡涓涓柊鐨勭郴缁熴備綘蹇呴』涓烘瘡涓渓ibrary鈥濈被鍒涘缓涓や釜鏂囦欢銆傜涓涓槸绫绘湰韬紝姣斿Newclass.php锛屼繚瀛樺湪application/libraries鐩綍涓紱绗簩涓垯淇濆瓨鍦╝pplication/init鐩綍涓紝蹇呴』鍙仛init_newclass.php锛屽畠蹇呴』鍖呭惈鍑犺鏍囧噯浠g爜锛屼互杩涜鍒濆鍖栵紝浣垮叾鎴愪负CI瓒呯骇瀵硅薄鐨勪竴閮ㄥ垎锛屼絾浣犱粛瑕佷娇鐢╣et_instance()鍑芥暟瀛樺彇CI璧勬簮銆
+
+> In version 1.5, the init folder has been deprecated, and initialization happens automatically. You now only need the one file for each 'library' class.
+> 鍦1.5鐗堜腑锛 init 鏂囦欢澶瑰凡缁忚涓嶉紦鍔变娇鐢ㄤ簡锛屽垵濮嬪寲灏嗚嚜鍔ㄥ湴杩涜銆 姣忎釜'library'鍙渶瑕佷竴涓枃浠躲
+鍦1.5鐗堜腑锛屽凡缁忎笉榧撳姳浣跨敤init鏂囦欢澶逛簡锛屽垵濮嬪寲灏嗚嚜鍔ㄥ湴杩涜銆傛瘡涓渓ibrary鈥濆彧闇瑕佷竴涓枃浠躲
+
+> The old scripts folder has also been deprecated. 'Deprecate' in this context, usually means that the thing concerned is till recognized and should still work, but that the developer offers no guarantee that it will do so in all future versions. In other words, don't panic if you still have scripts in a system/application/scripts folder鈥攂ut don't write any more.
+> 鏃х殑鑴氭湰鐩綍涔熷凡缁忎笉榧撳姳浣跨敤浜嗐'涓嶉紦鍔变娇鐢'閫氬父鎰忓懗鐫鐩稿叧鐨勫疄鐜版柟娉曡兘澶熷伐浣滐紝浣嗘槸寮鍙戣呭簲璇ュ敖閲忎笉瑕佽繖涔堝仛锛屽洜涓篊I涓嶈兘淇濊瘉鍦ㄦ湭鏉ョ殑鐗堟湰涓繕鏀寔瀹冦 濡傛灉浣犱粛鐒舵湁涓涓猻ystem/application/scripts鐩綍锛屼笉闇瑕佺揣寮-浣嗘槸涔熶笉瑕佸啀浣跨敤瀹冦
+鏃х殑鑴氭湰鐩綍涔熶笉榧撳姳浣跨敤浜嗐傗滀笉榧撳姳浣跨敤鈥濋氬父鎰忓懗鐫鐩稿叧鐨勫疄鐜版柟娉曡兘澶熷伐浣滐紝浣嗘槸璇峰紑鍙戣呭敖閲忎笉瑕佽繖涔堝仛锛屽洜涓篊I涓嶈兘淇濊瘉鍦ㄦ湭鏉ョ殑鐗堟湰涓繕鏀寔瀹冦傚鏋滀綘杩樻湁涓涓猻ystem/application/scripts鐩綍锛屾棤闇绱у紶鈥斺斾絾鏄涓嶈鍐嶇敤瀹冧簡銆
+
+> If you are planning to use libraries or plug-ins written by the CI community, please check first that they are up to date with the latest CI version. There are quite a few around still that were written for 1.4.1 and have separate 'init' files. Updating them isn't difficult, but it does take some care to get it right.
+> 濡傛灉浣犳鍦ㄨ鍒掍娇鐢ㄧ敱 CI 绀惧尯缂栧啓鐨刲ibraries鎴栬卲lug-ins锛岃棣栧厛妫鏌ヨ繖浜涜祫婧愭槸鍚︽槸涓篊I鐨勬渶鏂扮増鏈紑鍙戠殑銆 鏈夌浉褰撳鐨勬槸涓1.4.1鐗堟湰寮鍙戠殑锛岃繕鏈夊彟涓涓'init'鏂囦欢銆 鏇存柊瀹冧滑涓嶅洶闅撅紝浣嗘槸闇瑕佸皬蹇冭浜嬶紝纭繚瀹冧滑姝e父宸ヤ綔銆
+濡傛灉浣犳鍦ㄨ鍒掍娇鐢ㄧ敱CI绀惧尯缂栧啓鐨刲ibraries鎴栬卲lug-ins锛岃棣栧厛妫鏌ヨ繖浜涜祫婧愭槸鍚︽槸涓篊I鐨勬渶鏂扮増鏈紑鍙戠殑銆傛湁鐩稿綋澶氱殑鏄笓涓1.4.1鐗堟湰寮鍙戠殑锛屼粛鍖呭惈鐙珛鐨勨渋nit鈥濇枃浠躲傛洿鏂板畠浠笉鍥伴毦锛屼絾闇灏忓績琛屼簨锛屼互纭繚瀹冧滑姝e父宸ヤ綔銆
+
+> So Should I Update If a New CI Version Comes Out?
+> 濡傛灉鏂扮殑 CI 鐗堟湰鍑烘潵锛屾垜搴旇鏇存柊鍚?
+濡傛灉鏈変簡鏂扮増CI锛屾垜闇瑕佹洿鏂板悧锛
+
+> New versions of CI come out from time to time. They come with comprehensive instructions for updating. Usually, this involves copying a new set of files to your system folder. Sometimes, you need to change config files, or your index.php file, as well, but none of these are major changes and none of them are rocket science. Because the folder structure keeps your application files in their own place, it's usually easy to update the system without touching the applications.
+> CI 鐨勬柊鐗堟湰鏃朵笉鏃朵細鎺ㄥ嚭銆 瀹冧滑甯︽湁濡備綍鏇存柊鐨勬寚鍗椼 閫氬父锛岃繖鍖呮嫭鎷疯礉涓缁勬柊鐨勬枃浠跺埌浣犵殑system鐩綍銆 鏈夋椂锛屼綘闇瑕佹敼鍙 config鏂囦欢, 鎴栬呬綘鐨 index.php鏂囦欢銆備笉浼氭湁宸ㄥぇ鐨勬敼鍙樸傚洜涓虹洰褰曠粨鏋勬妸浣犵殑搴旂敤淇濆瓨鍦ㄤ粬浠嚜宸辩殑浣嶇疆锛岃繖鏍峰彲浠ュ湪鍗囩骇绯荤粺鏃朵笉鐢ㄦ秹鍙婂埌浣犵殑搴旂敤绯荤粺銆
+鏂扮増CI浼氫笉鏃舵帹鍑猴紝瀹冧滑浼氬甫鏈夋洿鏂版寚鍗椼傞氬父锛岃繖鍖呮嫭涓缁勬柊鐨勬枃浠堕渶瑕佹嫹璐濆埌浣犵殑system鐩綍涓傛湁鏃讹紝浣犺繕闇瑕佹洿鏂癱onfig鏂囦欢锛屾垨index.php鏂囦欢銆傝繖涓嶄細甯︽潵宸ㄥぇ鏀瑰彉锛屽洜涓虹洰褰曠粨鏋勫凡缁忓皢浣犵殑搴旂敤淇濆瓨鍦ㄤ粬浠嚜宸辩殑浣嶇疆锛岃繖鏍峰湪鍗囩骇绯荤粺鏃跺苟涓嶄細娑夊強鍒板簲鐢ㄧ▼搴忋
+
+> But, say you've written your killer app in version 1.5. It's uploaded to your production system and working fine. Then, Rick Ellis brings out CI version 1.6 (or 2.8 or whatever鈥). It has interesting new features, and some bug fixes. Do you upgrade to it?
+> 浣嗘槸, 鍋囧璇翠綘宸茬粡鍦 1.5 鐗堜腑鍐欎簡涓涓綘鐨勪紭绉浣滃搧. 瀹冭涓婁紶鍒颁綘鐨勭敓浜х郴缁熷苟涓旇繍琛屽緱寰堝ソ銆 濡傛灉Rick鎺ㄥ嚭浜咰I 1.6 鐗.(鎴 2.8 鎴栦换浣曞叾瀹冪殑鈥) 瀹冩湁涓浜涙湁瓒g殑鏂板姛鑳姐佽繕鏈変竴浜汢ug淇銆備綘鏄惁闇瑕佸瀹冭繘琛屽崌绾?
+鍋囧璇翠綘宸茬粡鍩轰簬1.5鐗堝畬鎴愪簡涓涓紭绉浣滃搧锛屽畠琚笂浼犲埌浜у搧绯荤粺涓笖杩愯寰楀緢濂姐傛鏃跺鏋淩ick鎺ㄥ嚭浜咰I 1.6鐗堬紙鎴2.8锛屾垨鍏跺畠鐗堟湰鈥︹︼級锛屽畠鏈変竴浜涙湁瓒g殑鏂板姛鑳姐佽繕鏈変竴浜汢ug淇銆備綘鏄惁闇瑕佸瀹冭繘琛屽崌绾у憿锛
+
+> I would say, 'Yes', if it's a minor upgrade, say between 1.5.2 and 1.5.3. But if it's a major version change, and your existing system is working, leave well alone. You can tell the difference partly from the numbering, but also from the 'change log' published with each upgrade when it comes out. The sort of changes that have been made in CI over the last year fall into three categories:
+> 鎴戜細璇, '鏄殑', 濡傛灉瀹冩槸涓涓緝灏忕殑鍗囩骇, 姣斿鍦 1.5.2 鍜 1.5.3涔嬮棿锛屼綘搴旇鍗囩骇. 浣嗘槸濡傛灉瀹冩槸涓涓富瑕佺殑鐗堟湰鍙樺寲,鑰屼綘鐨勭幇鏈夌郴缁熸鍦ㄥ伐浣, 鏆傜紦鍗囩骇鏄釜姣旇緝鏄庢櫤鐨勯夋嫨銆 浣犲彲鑳戒粠鏁板瓧閮ㄥ垎鍒嗚鲸鍑虹増鏈崌绾у彉鍖栧ぇ灏忕殑绋嬪害, 浣嗘槸涔熷彲浠ヤ粠鏂扮増鏈檮甯︾殑'鍙樺寲娓呭崟'鍒楄〃寰楀嚭缁撹銆備粠鍘诲勾寮濮 CI 鍒掑垎浜嗕笁涓洰褰曟潵鍒楀嚭涓夌被涓嶅悓鐨勫彉鍖:
+鎴戜細璇达細鈥滄槸鐨勩傗濆鏋滃畠鍙槸涓涓緝灏忕殑鍗囩骇锛屾瘮濡傚湪1.5.2鍜1.5.3涔嬮棿锛屼綘搴旇鍗囩骇銆備絾濡傛灉瀹冩槸涓涓富瑕佺殑鐗堟湰鍙樺寲锛岃屼綘鐜版湁鐨勭郴缁熸鍦ㄥ伐浣滐紝鏆傜紦鍗囩骇鏄釜姣旇緝鏄庢櫤鐨勯夋嫨銆備綘鍙兘浠庢暟瀛楅儴鍒嗗垎杈ㄥ嚭鐗堟湰鍗囩骇鍙樺寲澶у皬鐨勭▼搴︼紝涔熷彲浠ヤ粠鏂扮増鏈檮甯︾殑鈥滄洿鏂板垪琛ㄢ濅腑寰楀嚭缁撹銆備粠鍘诲勾寮濮嬶紝CI鍒掑垎浜嗕笁绉嶇被鍨嬫潵琛ㄧず涓嶅悓鐨勫彉鍖栵細
+
+> Bug fixes: There are surprisingly few of these鈥擟I is excellent code, and most of the base classes have been well tested by hundreds if not thousands of users.
+> Bug淇: 浠や汉鎯婅鍦板皯锛孋I鏈変紭鑹殑浠g爜, 鑰屼笖澶ч儴浠界殑鍩虹绫诲凡缁忚鏁颁互鍗冭鐨勪娇鐢ㄨ呯簿蹇冨湴娴嬭瘯浜嗕笂鐧鹃亶銆
+Bug淇锛氫护浜烘儕璁剁殑灏戯紝CI鏈変紭鑹殑浠g爜锛屽ぇ澶氭暟鍩虹绫诲凡缁忚鏁颁互鍗冭鐨勪娇鐢ㄨ呯簿蹇冨湴娴嬭瘯浜嗕笂鐧鹃亶銆
+
+> New features:. These appear regularly, but if you managed to build your application without them, will they really be helpful now?
+> 鏂扮殑鍔熻兘:. 杩欎簺缁忓父鍦板嚭鐜帮紝浣嗘槸濡傛灉浣犱笉浣跨敤杩欎簺鏂板姛鑳芥潵寮鍙戜綘鐨勫簲鐢紝瀹冧滑骞朵笉浼氬浣犳湁浠涔堝府鍔┿
+鏂板姛鑳斤細缁忓父鍑虹幇锛屼絾濡傛灉浣犱笉浣跨敤杩欎簺鏂板姛鑳芥潵寮鍙戜綘鐨勫簲鐢ㄧ▼搴忥紝瀹冧滑骞朵笉浼氬浣犳湁浠涔堝府鍔┿
+
+> Subtle changes: As I've described, CI has gone through a process of internal evolution, and it may well continue to do so. As you can see from the following table, some of these might be backwardly compatible, or they might require fairly major re-writes of your code.
+> 鏁忔劅鐨勫彉鍖: 灏辫薄鎴戝凡缁忔弿杩拌繃鐨勶紝 CI 宸茬粡缁忚繃涓涓唴瀛樼殑鍗囩骇鐨勮繃绋嬶紝鑰屼笖瀹冪悊鎵褰撶劧浼氱户缁繖涔堝仛銆備綘鑳戒粠涓嬮潰鐨勫垪琛ㄧ湅鍒帮紝鍏朵腑鐨勪竴浜涘悜鍚庡吋瀹, 鍚﹀垯瀹冧滑鍙兘瀵艰嚧浣犻噸鍐欎綘鐨勯儴鍒嗕唬鐮併
+鏁忔劅鏇存柊锛氬氨璞℃垜璇磋繃鐨勶紝CI缁忚繃浜嗕竴涓唴閮ㄥ崌绾х殑杩囩▼锛岃屼笖瀹冪悊鎵搴斿綋鍦颁細缁х画杩欎箞鍋氥備綘鍙互閫氳繃涓嬮潰鐨勫垪琛ㄧ湅鍑烘潵锛屽叾涓竴浜涙洿鏂颁細鍚戝悗鍏煎锛屽惁鍒欏畠浠彲瀵艰嚧浣犲皢閲嶅啓閮ㄥ垎浠g爜銆
+
+> Some changes between versions of CI:
+> 鍦 CI 鐨勭増鏈箣闂寸殑涓浜涘彉鍖:
+CI鐗堟湰鏇存柊杩囩▼涓殑涓浜涘彉鍖栵細
+
+> VersionChange Log
+> 鐗堟湰 鍙樺寲璁板綍
+鐗堟湰 鍙樺寲璁板綍
+
+> 1.2Added a global function named get_instance() allowing the main CodeIgniter object to be accessible throughout your own classes.
+> 1.2 澧炲姞涓涓叏灞鍑芥暟锛屽悕涓篻et_instance()锛屽厑璁镐綘鑷畾涔夌殑璇诲啓CI鐨勪富瀵硅薄銆
+1.2 澧炲姞浜嗕竴涓悕涓篻et_instance()鐨勫叏灞鍑芥暟锛屽厑璁镐綘鐨勮嚜瀹氫箟绫诲緢瀹规槗鍦拌鍐機I鐨勪富瀵硅薄銆
+
+> 1.3Added support for Models.
+> 1.3 澧炲姞瀵规ā鍨嬬殑鏀寔銆
+1.3 澧炲姞浜嗗妯″瀷鐨勬敮鎸併
+
+> 1.3Added the ability to pass your own initialization parameters to your custom core libraries when using $this->load->library().
+> 1.3 澧炲姞浼犻掍綘鑷繁鐨勫垵濮嬪寲鍙傛暟鍒板鎴疯嚜瀹氫箟library鑳藉姏锛屼綘鍙互杩欐牱鍋氾細$this->load->library()
+1.3 澧炲姞浜嗕紶閫掕嚜瀹氫箟鍒濆鍖栧弬鏁板埌甯歌鏍稿績library鐨勫姛鑳斤紝浣犲彲浠ヨ繖鏍峰仛锛$this->load->library()
+
+> 1.3Added better class and function name-spacing to avoid collisions with user developed classes. All CodeIgniter classes are now prefixed with CI_ and all controller methods are prefixed with _ci to avoid controller collisions.
+> 1.3 澧炲姞杈冨ソ鐨勭被鍜屽嚱鏁扮殑鍛藉悕绌洪棿-閬垮厤鍐茬獊銆傛墍鏈夌殑CodeIgniter绫诲紑濮嬪啝浠I_鍓嶇紑鍜屾墍鏈夌殑鎺у埗鍣ㄦ柟娉曚互_ci涓哄墠缂閬垮厤鎺у埗鍣ㄥ懡鍚嶅啿绐併
+1.3 澧炲姞浜嗚緝濂界殑绫讳笌鍑芥暟鐨勫懡鍚嶇┖闂粹斺旈伩鍏嶅啿绐併傛墍鏈夌殑CodeIgniter绫诲紑濮嬪啝浠I_鍓嶇紑锛屾墍鏈夌殑鎺у埗鍣ㄦ柟娉曞潎浠ci涓哄墠缂锛屼粠鑰岄伩鍏嶄簡鎺у埗鍣ㄥ懡鍚嶅啿绐併
+
+> 1.3.3Models do not connect automatically to the database as of this version.
+> 1.3.3 妯″瀷涓嶈嚜鍔ㄨ繛鎺ユ暟鎹簱銆
+1.3.3 璇ョ増鏈ā鍨嬩笉鑷姩杩炴帴鏁版嵁搴撱
+
+> 1.4Added the ability to replace core system classes with your own classes.
+> 1.4 澧炲姞浜嗙敤浣犺嚜瀹氫箟鐨勭被鏇挎崲鏍稿績绯荤粺绫荤殑鑳藉姏銆
+1.4 澧炲姞浜嗙敤浣犺嚜瀹氫箟鐨勭被鏇挎崲鏍稿績绯荤粺绫荤殑鍔熻兘銆
+
+> 1.4Updated the Models loader function to allow multiple loads of the same model.
+> 1.4 鍗囩骇妯″瀷鐨勮杞藉嚱鏁帮紝鍏佽瀵瑰悓涓妯″瀷澶氭瑁呰浇銆
+1.4 鍗囩骇浜嗘ā鍨嬬殑瑁呰浇鍑芥暟锛屽厑璁稿鍚屼竴妯″瀷澶氭瑁呰浇銆
+
+> 1.4.1Updated plugins, helpers, and language classes to allow your application folder to contain its own plugins, helpers, and language folders. Previously, they were always treated as global for your entire installation. If your application folder contains any of these resources they will be used instead the global ones.
+> 1.4.1 鏇存柊浜唒lugins, helpers鍜宭anguage绫伙紝鍏佽浣犵殑搴旂敤绋嬪簭鐩綍鍖呭惈浣犺嚜宸辩殑plugins, helpers鍜宭anguage绫汇傚厛鍓嶏紝鍦ㄥ畨瑁呮椂瀹冧滑鎬绘槸琚綋浣滃叏灞鐨勶紝濡傛灉浣犵殑搴旂敤鐩綍鍖呭惈浜嗗畠浠腑鐨勪换涓涓紝浣犺嚜瀹氫箟鐨勫皢浼氳鐩栫郴缁熶腑鐨勮繖浜涜祫婧愩
+1.4.1 鏇存柊浜唒lugins銆乭elpers鍜宭anguage绫伙紝鍏佽浣犵殑搴旂敤绋嬪簭鐩綍鍖呭惈浣犺嚜宸辩殑plugins, helpers鍜宭anguage绫汇備箣鍓嶅畠浠湪瀹夎鏃舵绘槸琚綋浣滃叏灞鐨勩傝岀幇鍦ㄤ綘鐨勫簲鐢ㄧ洰褰曞鏋滃寘鍚簡瀹冧滑涓殑浠讳竴涓紝鑷畾涔夌殑灏嗕細鏇夸唬绯荤粺涓殑杩欎簺璧勬簮銆
+
+> 1.4.1Deprecated the application/scripts folder. It will continue towork for legacy users, but it is recommended that you create your own libraries or models instead. It was originally added before CI had userlibraries or models, but it's not needed anymore.
+> 1.4.1 澹版槑涓嶉紦鍔变娇鐢╝pplication/script鐩綍銆 浣嗗畠灏嗕細缁х画涓轰互鍓嶇殑浣跨敤鑰呮彁渚涙甯镐娇鐢ㄧ殑鍙兘鎬э紝浣嗘槸寤鸿浣犳敼涓哄垱寤轰綘鑷繁鐨刲ibrary鎴杕odel銆傚湪CI鏀寔鐢ㄦ埛鑷畾涔塴ibrary鎴栬卪odel涔嬪墠锛屽畠鏈潵灏卞瓨鍦紝浣嗘槸瀹冧笉鍐嶆槸蹇呬笉鍙皯鐨勩
+1.4.1 澹版槑浜嗕笉榧撳姳浣跨敤application/script鐩綍銆備絾瀹冨皢浼氫负浠ュ墠鐨勪娇鐢ㄨ呯户缁繚鐣欙紝浣嗗缓璁綘鏀逛负鍒涘缓浣犺嚜宸辩殑library鎴杕odel銆傚湪CI鏀寔鐢ㄦ埛鑷畾涔塴ibrary鎴栬卪odel涔嬪墠锛屽畠鏈潵灏卞瓨鍦紝浣嗘槸瀹冨凡涓嶅啀鏄繀椤荤殑浜嗐
+
+> 1.5Added the ability to extend libraries and extend core classes, in additionto being able to replace them.
+> 1.5 澧炲姞鑳藉姏鎵╁睍library鍜屾牳蹇冪被鐨勫姛鑳斤紝鑰屼笖涔熻兘鏇挎崲瀹冧滑銆
+1.5 澧炲姞浜嗘墿灞昹ibrary鍜屾墿灞曟牳蹇冪被鐨勫姛鑳斤紝鑰屼笖涔熻兘鏇挎崲瀹冧滑銆
+
+> 1.5Deprecated init folder. Initialization happens automatically now.
+> 1.5 澹版槑涓嶉紦鍔变娇鐢╥nit 鏂囦欢澶广傚垵濮嬪寲鐜板湪鑷姩杩涜銆
+1.5 澹版槑浜嗕笉榧撳姳浣跨敤init鏂囦欢澶广傜幇鍦ㄧ殑鍒濆鍖栧紑濮嬭嚜鍔ㄨ繘琛屼簡銆
+
+> Don't misunderstand me. All of these are sensible changes and all of them are improvements. If you are starting a new project, start with the latest CI version. But if you wrote code using version 1.3, say, you will find that your scripts folder is deprecated and your models don't automatically connect to the database any more. Personally, I would leave that code running on version 1.3 of CI, rather than try to upgrade it. Life's too short.
+> 涓嶈璇В鎴戙 杩欎簺鍏ㄩ儴鏄湁鎰忎箟鐨勬敼鍙橈紝骞朵笖浠栦滑鍏ㄩ儴鏄CI鐨勫崌绾с 濡傛灉浣犲紑濮嬩竴涓柊鐨勯」鐩, 璇蜂娇鐢ㄦ渶鏂扮殑 CI 鐗堟湰銆備絾鏄鏋滀綘浣跨敤1.3鐗堝紑鍙戝簲鐢紝浣犱細鍙戠幇浣犵殑scripts鐩綍琚0鏄庝笉榧撳姳浣跨敤锛岃屼笖浣犵殑妯″瀷涔熶笉浼氳嚜鍔ㄨ繛鎺ユ暟鎹簱銆傛垜涓汉鍦ㄤ娇鐢–I鐨1.3 鐗堬紝鑰屼笉灏濊瘯鍗囩骇瀹冦 鐢熷懡鑻︾煭銆
+涓嶈瀚屾垜鍟板棪锛岃繖浜涢兘鏄噸瑕佺殑鏇存柊涓庢敼鑹傚鏋滀綘鍚姩浜嗕竴涓柊椤圭洰锛岃浣跨敤鏈鏂扮殑CI鐗堟湰銆備絾鏄鏋滀綘姝e湪浣跨敤1.3鐗堝紑鍙戝簲鐢紝浣犱細鍙戠幇scripts鐩綍琚0鏄庝笉榧撳姳浣跨敤锛岃屼笖妯″瀷涔熶笉浼氳嚜鍔ㄨ繛鎺ユ暟鎹簱銆傚氨涓汉鑰岃锛屾垜鏇剧粡鍧氭寔鍦–I鐨1.3鐗堣屼笉鍘诲崌绾у畠锛屾氮璐逛簡寰堝鏃堕棿銆
+
+> How to Add On to CI's Basic Classes
+> 濡備綍淇敼CI鐨勫熀纭绫
+濡備綍淇敼CI鐨勫熀纭绫
+
+> Normal users are unlikely to need to alter the base CI classes. It's a pretty good framework, it does a lot of things, and after all, the point of a framework is to make thing easy, right? However, if you must 鈥.
+> 涓鑸娇鐢ㄨ呬笉鍙兘闇瑕佹敼鍙樺熀鏈殑CI绫汇 瀹冩槸鐩稿綋濂界殑妗嗘灦锛屽畠鍋氳澶氫簨鐗, 鑰屼笖姣曠珶锛屼娇鐢ㄦ鏋朵細浣垮仛浜嬫洿瀹规槗, 瀵逛笉瀵? 鐒惰, 濡傛灉浣犱竴瀹氣.
+涓鑸娇鐢ㄨ呭彲鑳芥棤闇鏀瑰彉CI鍩虹绫汇傚畠鍏峰鐩稿綋濂界殑妗嗘灦锛岃兘鍋氬嚭璁稿涓滆タ鏉ワ紝浣跨敤妗嗘灦涓嶆槸浼氳浜嬫儏鏇村鏄撳悧锛熷綋鐒讹紝濡傛灉浣犱竴瀹氳鍋氱殑璇濃︹
+
+> CI is open source, and you can see all the code as soon as you download it. This includes the basic libraries that make CI work (stored in system/libraries) as well as the ones you wrote in system/application/libraries.) So it has always been possible to change CI any way you like.
+> CI 鏄紑婧愪骇鍝, 涓鏃︿笅杞戒綘鑳界湅鍒版墍鏈夌殑浠g爜銆 杩欏寘鎷娇 CI 宸ヤ綔 (鍦╯ystem/libraries淇濆瓨)鐨勫熀鏈琹ibrary浠ュ強浣犱竴鏃︾紪鍐欏悗淇濆瓨system/application/libraries鐨勪綘鑷繁鐨刲ibraries锛屽洜姝や綘鍙兘鏀瑰彉CI璁╁畠浠ヤ綘鍠滄鐨勬柟寮忓伐浣溿
+CI鏄紑婧愪骇鍝侊紝涓嬭浇鍚庝綘灏辫兘鐪嬪埌鎵鏈夌殑浠g爜銆傝繖鍖呮嫭浣緾I宸ヤ綔鐨勫熀鏈琹ibrary锛堜繚瀛樺湪system/libraries涓級涓庝綘鑷繁鐨刲ibraries锛堜繚瀛樺湪system/application/libraries涓級锛屾墍浠ヤ綘鍙互鏀瑰彉CI锛屼娇鍏朵互浣犲枩娆㈢殑鏂瑰紡宸ヤ綔銆
+
+> Changing system library files has two problems, however:
+> 淇敼绯荤粺libraries鏂囦欢鏈変簩涓棶棰橈紝灏辨槸:
+淇敼绯荤粺libraries鏂囦欢瀛樺湪涓や釜闂锛屽氨鏄細
+
+> There's no guarantee that your new code will be compatible with the rest of CI, or with updated versions. This may lead to subtle or strange errors that won't be easy to track down. If you later update your CI version, the system folder is likely to be changed.
+> 銆 娌℃湁淇濊瘉锛屼綘鐨勬柊瀵嗙爜鏄惁浼氫笌 CI 鐨勫叾浣欓儴鍒嗗吋瀹癸紝鎴栦笌鏇存柊鐨勭増鏈吋瀹广傝繖鍙兘瀵艰嚧涓嶅鏄撹窡韪晱鎰熸垨鑰呭鎬殑閿欒銆傚鏋滀綘绋嶅悗鍗囩骇浣犵殑 CI 鐗堟湰锛岀郴缁熺洰褰曞彲鑳借鏀瑰彉銆
+璋佷篃涓嶈兘淇濊瘉浣犵殑鏂颁唬鐮佷笌CI鐨勫叾浠栭儴鍒嗘垨鏇存柊鐨勭増鏈吋瀹广傝繖鍙兘浼氬鑷翠笉鏄撹窡韪殑銆佹晱鎰熺殑鎴栧鎬殑閿欒銆傚鏋滀綘绋嶅悗鍗囩骇浣犵殑CI鐗堟湰锛岀郴缁熺洰褰曚篃鍙兘浼氶殢涔嬫敼鍙樸
+
+> 路The library file you altered may well be over-written and updated, so you'd have to go through your changes and transfer them to the updated version.
+> 銆 浣犳敼鍙樿繃鍘荤殑libraries灏嗚鏂扮殑鏂囦欢瑕嗙洊鎴栧崌绾э紝 鍥犳浣犻渶瑕佺淮鎶や綘鑷繁鐨勪慨鏀瑰苟鎶婂畠浠縼绉诲埌鍗囩骇鐨勭増鏈
+浣犱慨鏀硅繃鐨刲ibrary灏嗚鏂扮殑鏂囦欢瑕嗙洊鎴栨洿鏂帮紝鍥犳浣犻渶瑕佽嚜琛屼慨鏀瑰苟灏嗗叾鏇存柊鍒板崌绾х殑鐗堟湰涓
+
+> However, since version 1.5, there are now two sensible 'work-arounds' for tinkering with the CI library classes (except for the underlying 'database' and 'controller' classes, which you touch at your peril.)
+> 涓嶈繃锛屼粠 1.5 鐗堣捣, 鐜板湪閽堝淇敼CI绫诲簱鏈変簩涓湁鎰忎箟鐨勬満鍒(闄や簡閽堝'鏁版嵁搴'鍜'鎺у埗鍣'绫伙紝瑙﹀強杩欎袱涓兘寰堝嵄闄.)
+涓嶈繃锛屽鍙兘鏈変汉浼氳儭涔变慨鏀笴I绫诲簱鐨勬儏鍐碉紝浠1.5鐗堣捣澧炲姞浜嗕袱涓槑纭殑鈥滃伐浣滃尯鈥濄傦紙涓嶅寘鎷熀鏈殑鈥滄暟鎹簱鈥濆拰鈥滄帶鍒跺櫒鈥濈被锛岃繖涓や釜閮藉緢鍗遍櫓锛岃涓嶈鑷淇敼銆傦級
+
+> 路 Firstly, you can create a file with the same name as any of the system baseclasses in your /system/application folder. The system then uses this one,in preference to the standard one in the /system folder. This requires exactnaming conventions鈥攕ee the online User Guide. It also requires you to copyall the functionality in the existing class as well as your own additionsor changes.
+> 绗竴锛屾棤璁哄湪鍝釜鎿嶄綔绯荤粺涓紝浣犺兘鍦ㄤ綘鐨 /system/application鐩綍涓垱寤轰竴涓笌绯荤粺鍩虹绫诲悓鍚嶇殑鏂囦欢銆傜郴缁熶細浼樺厛浣跨敤杩欎竴涓,濡傛灉杩欎釜鏂囦欢涓嶅瓨鍦ㄦ垨涓嶅彲鐢ㄥ垯浣跨敤/system鐩綍涓殑閭d釜銆傝繖闇瑕佹湁绮剧‘鐨勫懡鍚嶉檺鍒-璇﹁鐢ㄦ埛鎵嬪唽銆傚畠涔熼渶瑕佷綘澶嶅埗鎵鏈夊湪鐜版湁鐨勭被涓瓨鍦ㄧ殑鍔熻兘锛岃瀹冧滑鍜屼綘鑷繁淇敼杩囩殑閭d簺涓璧峰伐浣溿
+绗竴锛屽綋浣犲湪/system/application鐩綍涓垱寤轰竴涓笌绯荤粺鍩虹绫诲悓鍚嶇殑鏂囦欢锛岀郴缁熶細浼樺厛浣跨敤杩欎竴涓紱濡傛灉璇ユ枃浠朵笉瀛樺湪鎴栦笉鍙敤鏃讹紝鍒欎娇鐢/system鐩綍涓殑閭d釜銆傝鎿嶄綔鏈夋槑纭殑鍛藉悕闄愬埗鈥斺旇瑙佺敤鎴锋墜鍐屻傚畠杩橀渶瑕佷綘澶嶅埗鎵鏈夊湪鐜版湁鐨勭被涓瓨鍦ㄧ殑鍔熻兘锛岃瀹冧滑鍜屼綘鏀硅繃鐨勯偅浜涚被涓璧峰伐浣溿
+
+> 路 Secondly, and more conveniently, you can create a new class that extends the system class. (So it's perhaps best referred to as a 'sub-class'.) Again, there arenaming conventions鈥攕ee the online User Guide. Extending the underlyingsystem class means that your new sub-class inherits all the resources of theunderlying CI class, but adds a few extra methods of your own. This shouldmean that, if you update your CI version, the underlying CI class will bereplaced, but your new sub-class (which you should put in the system/application folder) will be left untouched.
+> 绗簩, 鏇存柟渚垮湴锛屼綘鑳戒粠鐜版湁绯荤粺绫绘淳鐢熷嚭涓涓柊绫汇(鍥犳鍙兘娲剧敓涓涓瓙绫绘槸鏇村ソ鐨勫仛娉)锛屽綋鐒朵篃鏈夊懡鍚嶉檺鍒讹紝璇﹁鐢ㄦ埛鎵嬪唽銆傜户鎵夸竴涓郴缁熺被鎰忓懗鐫浣犵殑鏂板瓙绫绘綔鍦ㄧ户鎵夸簡CI绫讳腑鐨勬墍鏈夎祫婧愶紝骞跺鍔犲嚑涓綘鑷繁鐨勯澶栨柟娉曘傝繖搴旇鎰忓懗鐫锛屽鏋滀綘鍗囩骇浣犵殑 CI鐗堟湰锛岀鍏堢被灏嗕細琚浛鎹紝浣嗘槸浣犵殑鏂板瓙绫(浣犲簲璇ユ妸瀹冩斁鍦╯ystem/application鐩綍)灏嗗畨鐒舵棤鎭欍
+绗簩锛屾洿鏂逛究鍦帮紝浣犲彲浠ヤ粠鐜版湁绯荤粺绫讳腑娲剧敓鍑轰竴涓柊绫汇傦紙娲剧敓涓涓瓙绫诲彲鑳芥槸鏇村ソ鐨勫仛娉曘傦級杩欏綋鐒朵篃鏈夊懡鍚嶉檺鍒讹紝璇﹁鐢ㄦ埛鎵嬪唽銆傜户鎵夸竴涓郴缁熺被鎰忓懗鐫浣犵殑鏂板瓙绫绘綔鍦ㄥ湴缁ф壙浜咰I绫讳腑鐨勬墍鏈夎祫婧愶紝骞跺鍔犱簡鍑犱釜浣犵殑棰濆鏂规硶銆傝繖涔熻鎰忓懗鐫锛屽鏋滀綘鍗囩骇浜咰I鐗堟湰锛岀鍏堢被灏嗕細琚浛鎹紝浣嗘槸浣犵殑鏂板瓙绫伙紙搴旇灏嗗叾鏀惧湪system/application鐩綍涓級灏嗗畨鐒舵棤鎭欍
+
+> However, neither of these methods will guarantee that your code is (or remains) compatible with the rest of CI.
+> 鐒惰, 涓よ呴兘涓嶈兘淇濊瘉浣犵殑浠g爜涓嶤I鐨勫叾瀹冮儴鍒嗗畬鍏ㄥ吋瀹广
+鐒惰岋紝涓婅堪涓ょ鏂瑰紡閮戒笉鑳戒繚璇佷綘鐨勪唬鐮佷笌CI鐨勫叾瀹冮儴鍒嗗畬鍏ㄥ吋瀹广
+
+> Looking through the CI online forums, there are various suggestions for extending the Validation, Unit Testing, and Session classes. Unit Testing, for example, only has two functions and a limited number of comparisons. Perhaps you want a function to show up errors in red, so they stand out when the test results are returned?
+> 閫氳繃CI鍦ㄧ嚎璁哄潧锛屾湁鎵╁厖鏍¢獙绫汇佸崟鍏冩祴璇曞拰浼氳瘽绫荤殑鍚勭涓嶅悓鐨勫缓璁 鍗曞厓娴嬭瘯, 涓句緥鏉ヨ锛屽彧鏈変簩涓嚱鏁板拰姣旇緝鏈夐檺鐨勫瓧绗︽暟銆 涔熻浣犳兂瑕佷竴涓嚱鏁帮紝鐢ㄧ孩鑹叉樉绀洪敊璇俊鎭紝鍥犳锛屽綋娴嬮獙缁撴灉琚繑鍥炴椂锛屼粬浠細鏄惧緱姣旇緝閱掔洰 ?
+鍘荤湅鐪婥I绀惧尯锛屼笂闈㈡湁瀵瑰悇绉嶆墿鍏呮牎楠岀被銆佸崟鍏冩祴璇曞拰浼氳瘽绫荤殑涓嶅悓寤鸿銆備互鍗曞厓娴嬭瘯涓轰緥锛屽彧鏈変簩涓嚱鏁板拰姣旇緝鏈夐檺鐨勫瓧绗︽暟鍋氭瘮杈冦傚鏋滀綘甯屾湜鏈変竴涓嚱鏁帮紝褰撴祴璇曠粨鏋滆繑鍥炴椂锛屼互姣旇緝閱掔洰鐨勭孩鑹叉爣璇嗛敊璇俊鎭紵
+
+> If you wanted to make extensive use of some other testing function, it would be simpler to add it in via a sub-class, extending Unit Testing, than to write it out in the controller each time you called Unit Testing.
+> 濡傛灉浣犳兂瑕佹墿鍏呬竴浜涘叾浠栫殑娴嬭瘯鍑芥暟锛岄氳繃涓涓瓙绫绘妸瀹冨姞鍏ヤ細鏄瘮杈冪畝鍗曠殑锛屾瘡娆″湪鎺у埗鍣ㄤ腑缂栧啓浠g爜璋冪敤鍗曞厓娴嬭瘯銆
+鎴栬呬綘甯屾湜鎵╁厖涓浜涘叾浠栫殑娴嬭瘯鍑芥暟锛屾瘮杈冪畝鍗曠殑鍋氭硶鏄氳繃涓涓瓙绫诲皢鍏跺姞鍏ャ傛墿灞曞埌鍗曞厓娴嬭瘯涓婏紝鍗冲綋浣犳瘡娆¤皟鐢ㄥ崟鍏冩祴璇曟椂锛屽皢鍏跺啓鍏ユ帶鍒跺櫒涓
+
+> If you wanted to do this, you'd start your new sub-class this way:
+> 濡傛灉浣犳兂瑕佽繖鏍峰仛锛屼綘鍙互杩欐牱寮濮嬩綘鐨勬柊瀛愮被:
+濡傛灉浣犳兂杩欐牱鍋氾紝鍙互杩欐牱寮濮嬫柊瀛愮被:
+
+> class MY_Unit_test extends CI_Unit-test {
+> function My_Unit_test() {
+> parent::CI_Unit_test();
+> }
+
+
+> function newfunction() {
+> //new code here!
+> }
+> }
+
+
+> Notice three things here:
+> 鍦ㄨ繖閲屾敞鎰忎笁浠朵簨:
+杩欓噷闇瑕佹敞鎰忎笁浠朵簨锛
+
+> 路The name of the underlying unit testing class is CI_Unit_test, even though the filename of the class code is system/libraries.unit_test.
+> 銆 鍗曞厓娴嬭瘯绫荤殑鍚嶇О鏄疌I_Unit_test锛屽嵆浣跨被鐨勪唬鐮佹枃浠跺悕鏄痵ystem/libraries.unit_test銆
+鍗曞厓娴嬭瘯绫荤殑鍚嶇О鏄疌I_Unit_test锛屽嵆浣跨被浠g爜鐨勬枃浠跺悕涓簊ystem/libraries.unit_test銆
+
+> 路If you need to use a constructor in your sub-class, make sure you extend the parent constructor first, as here.
+> 銆 濡傛灉浣犻渶瑕佸湪浣犵殑瀛愮被涓娇鐢ㄤ竴涓瀯閫犲嚱鏁帮紝纭畾浣犻鍏堝湪閲岄潰璋冪敤鐖剁被鐨勬瀯閫犲嚱鏁般
+濡傛灉浣犻渶瑕佸湪浣犵殑瀛愮被涓娇鐢ㄤ竴涓瀯閫犲嚱鏁帮紝璇峰厛纭畾鍦ㄩ噷闈㈣皟鐢ㄧ埗绫荤殑鏋勯犲嚱鏁般
+
+> 路Your new sub-class name should be prefixed with MY_, and saved as application/libraries/MY_unit_test.php. (Unlike the main classes, where the CI_ prefix is part of the class name but not of the filename, here the MY_ prefix is part of both.)
+> 銆 浣犵殑鏂板瓙绫诲悕绉板簲璇ヤ笌 MY_ 鍓嶇紑骞朵繚瀛樹负application/libraries/MY_unit_test.php銆(涓嶅儚绯荤粺涓偅浜涗富瑕佺殑绫, 瀹冧滑鏄互CI_涓哄墠缂锛屼絾鏄笉鏄枃浠跺悕, 鍦ㄨ繖閲孧Y_鍓嶇紑鏄袱鑰呯殑涓閮ㄤ唤.)
+璇ユ柊瀛愮被鍚嶇О搴旇涓篗Y_鍓嶇紑骞朵繚瀛樹负application/libraries/MY_unit_test.php銆傦紙涓嶅儚绯荤粺涓殑涓昏绫伙紝鏄互CI_涓哄墠缂锛岃屼笉鏄枃浠跺悕锛屽湪杩欓噷MY_鍓嶇紑鍏跺疄鏄簩鑰呯殑涓閮ㄥ垎銆傦級
+
+> Once you've created your sub-class, you load it like this:
+> 涓鏃︿綘宸茬粡鍒涘缓浣犵殑瀛愮被锛屼綘鍍忚繖鏍疯杞藉畠:
+濡傛灉浣犲凡缁忓垱寤轰簡浣犵殑瀛愮被锛屼綘鍙互杩欐牱瑁呰浇瀹冿細
+
+> $this->load->library('unit_test');
+
+
+> In other words, exactly the same as before you wrote the sub-class; and you call a function in the same way too, except that this time you can call not only the existing unit test functions, but also any new ones you've written yourself:
+> 鎹㈠彞璇濊, 瀹屽叏鍦颁笌浣犱互鍓嶇紪鍐欑殑浜嗙被鐩稿悓; 鑰屼笖浣犱互鍚屾牱鐨勬柟寮忚皟鐢ㄤ竴涓嚱鏁帮紝浣嗘槸杩欐浣犱笉浣嗚兘璋冪敤鍘熸潵鐨勫崟鍏冩祴璇曞嚱鏁帮紝杩樿兘璋冪敤浣犺嚜宸辩紪鍐欑殑鏂板嚱鏁:
+鎹㈠彞璇濊锛屾柊瀛愮被涓庝綘浠ュ墠缂栧啓鐨勫瓙绫诲畬鍏ㄧ浉鍚岋紝鑰屼笖浼氫互鍚屾牱鐨勬柟寮忚皟鐢ㄥ嚱鏁帮紝涓嶈繃姝ゆ椂浣犱笉浣嗚兘璋冪敤鍘熸潵鐨勫崟鍏冩祴璇曞嚱鏁帮紝杩樿兘璋冪敤浣犺嚜宸辩紪鍐欑殑鏂板嚱鏁帮細
+
+> $this->unit_test->newfunction();
+
+
+> When you next update your CI installation, the unit test library in the system folder will be overwritten, but the one in the application folder won't, so your code will still be there. Of course, you'll need to check that the updated system library is still compatible with your own code.
+> 褰撲綘浠ュ悗鍗囩骇浣犵殑 CI 鐨勬椂鍊欙紝鍦ㄧ郴缁熸枃浠跺す涓殑鍗曞厓娴嬭瘯绫诲簱灏嗕細瑕嗙洊锛屼絾鏄偅涓湪application鐩綍涓殑灏嗕笉浼氾紝鍥犳浣犵殑浠g爜灏嗕細浠嶇劧鍦ㄩ偅閲屻 褰撶劧锛屼綘灏嗕細闇瑕佹鏌ヨ鏇存柊鐨勭郴缁熺被鏄惁浠嶇劧涓庝綘鑷繁鐨勪唬鐮佸吋瀹广
+鍦ㄤ綘浠ュ悗鍗囩骇CI鏃讹紝鍦ㄧ郴缁熸枃浠跺す涓殑鍗曞厓娴嬭瘯绫诲簱灏嗕細瑕嗙洊锛屼絾鍦╝pplication鐩綍涓殑閭d釜涓嶄細琚鐩栵紝鎵浠ヤ綘鐨勪唬鐮佽繕浼氬湪閭i噷銆傚綋鐒讹紝浣犺繕闇瑕佹鏌ユ洿鏂扮殑绯荤粺绫绘槸鍚︿緷鐒朵笌浣犺嚜宸辩殑浠g爜鍏煎銆
+
+> Summary
+> 鎽樿
+鎽樿
+
+> In this chapter, we've seen some of things that can go wrong when you try to transfer your system from a local server to a remote one. This may involve:
+> 鍦ㄨ繖涓绔犻噷锛屽綋浣犲皾璇曡縼绉讳竴涓湰鍦板簲鐢ㄥ埌杩滅▼鏈嶅姟鍣ㄧ殑鏃跺欙紝鏈変簺涓滆タ鍙兘浼氬嚭閿欍 杩欏彲鑳藉寘鎷:
+鍦ㄨ繖涓绔犻噷锛屽綋浣犲皾璇曡縼绉讳竴涓湰鍦板簲鐢ㄥ埌杩滅▼鏈嶅姟鍣ㄦ椂锛屽彲鑳戒細鍑虹幇涓浜涢棶棰樸傝繖鍖呮嫭锛
+
+> 路 A different version of PHP or MySQL
+> 銆 PHP 鎴 MySQL 鐨勭増鏈樊寮
+PHP鎴朚ySQL鐨勭増鏈樊寮
+
+> 路 A different operating system
+> 銆 涓嶅悓鐨勬搷浣滅郴缁
+鎿嶄綔绯荤粺鐨勫樊寮
+
+> In particular, we've looked at case sensitivity, PHP differences, and MySQL issues. We've also looked at diagnostic tools.
+> 鐗瑰埆鍦帮紝鎴戜滑鍒嗘瀽浜嗗ぇ灏忓啓鏁忔劅闂, PHP 宸紓銆佸拰 MySQL 闂銆 鎴戜滑涔熻皥鍙婂嚑涓瘖鏂伐鍏枫
+鐗瑰埆鍦帮紝鎴戜滑鍒嗘瀽浜嗗ぇ灏忓啓鏁忔劅闂銆丳HP宸紓涓嶮ySQL闂銆傛垜浠繕璁ㄨ浜嗗嚑涓瘖鏂伐鍏枫
+
+> Then we looked at the CI's updates. These have all been major improvements, but my advice is, if you have a system working on the current CI version and a new one comes out, think carefully before you upgrade.
+> 鐒跺悗鎴戜滑鍒嗘瀽浜咰I 鐨勫崌绾с 杩欎簺閮藉甫鏉ヤ簡閲嶈鐨勮繘姝, 浣嗘槸鎴戠殑蹇犲憡鏄, 濡傛灉浣犲湪鐜板湪鐨 CI 鐗堟湰涓婂伐浣滃緱寰堝ソ锛屽鏋淐I鏈夋柊鐨勭増鏈帹鍑猴紝浠旂粏璇勪及鏄惁闇瑕佸崌绾у拰濡備綍鍗囩骇銆
+鐒跺悗鎴戜滑鍒嗘瀽浜咰I鐨勫崌绾с傚杩欎簺閲嶈鐨勮繘姝ワ紝鎴戠殑寤鸿鏄紝濡傛灉浣犲湪鐜板湪鐨凜I鐗堟湰涓婂伐浣滃緱寰堝ソ锛屽綋CI鏈夋柊鐨勭増鏈帹鍑烘椂锛岃浠旂粏璇勪及鏄惁闇瑕佸崌绾т互鍙婂浣曞崌绾с
+
+> Lastly, we looked at the pros and cons of adding to CI's basic classes. Most users won't need to do this, but if you want to, I strongly suggest that the best way to do it is to sub-class an existing library class.
+> 鏈鍚庯紝鎴戜滑鍒嗘瀽淇敼 CI 鍩虹绫荤殑姝e弽涓ゆ柟闈€ 澶у鏁扮殑浣跨敤鑰呭皢涓嶉渶瑕佽繖涔堝仛銆備絾鏄鏋滀綘纭俊闇瑕佽繖涔堝仛锛屾垜寮虹儓寤鸿锛氬疄鐜板畠鐨勬渶濂芥柟寮忔槸浠庝竴涓幇瀛樼殑library绫讳腑娲剧敓涓涓瓙绫汇
+鏈鍚庯紝鎴戜滑鍒嗘瀽浜嗕慨鏀笴I鍩虹绫荤殑姝e弽闈㈠洜绱犮傚叾瀹炲ぇ澶氭暟鐨勪娇鐢ㄨ呮棤闇杩欐牱鍋氥備絾濡傛灉浣犵‘淇$殑纭渶瑕佽繖鏍峰仛锛屾垜寮虹儓寤鸿锛氬疄鐜板畠鐨勬渶濂芥柟寮忔槸浠庝竴涓幇瀛樼殑library绫讳腑娲剧敓涓涓瓙绫汇
\ No newline at end of file
diff --git a/_docs/12.txt b/_docs/12.txt
new file mode 100644
index 0000000..b2ef9a0
--- /dev/null
+++ b/_docs/12.txt
@@ -0,0 +1,382 @@
+第 12 章 产品版本,升级和重大决定
+
+
+伟大的一天终于到来了!你的网站在本地环境中已经运行得足够好,是时候把它上传到远程服务器上使之成为一个正式运行的网站了。这件事按说很容易:上传所有的文件,包括系统文件夹的全部、更新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文件
+
+如果用户没有指定明确的controller/method(比如他们仅仅登录到www.mysite.com),config/routes文件则会设定程序执行的默认路径。其默认值可能是:
+
+$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->mymodel调用不同的视图。
+
+作为服务器差异的一个极端例子,我有一次写一个控制器,后决定将其修改为一个模型,我把它保存在model目录中,却没有意识到我在开头已经写了几行代码:
+
+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);
+
+这将会避免“warnings”,但仍可给你关于严重问题的信息。“warnings”通常不会中止程序的执行,但可能会引发其它你没有考虑到的问题。
+
+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');
+
+会在屏幕上显示:
+
+
+
+我并没有发现这多有用。如果希望系统在必要的时间和位置给我一个完整的有帮助的错误报告,且当我不想要的时候就不显示它们,就得编写我自己的函数了,如下:
+
+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 because......');
+
+我还可以设定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系统,这很让人迷惑。
+
+不仅这样,还有两个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的超级对象(详见第7章),你可以在“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版本。但是如果你正在使用1.3版开发应用,你会发现scripts目录被声明不鼓励使用,而且模型也不会自动连接数据库。就个人而论,我曾经坚持在CI的1.3版而不去升级它,浪费了很多时间。
+
+如何修改CI的基础类
+
+一般使用者可能无需改变CI基础类。它具备相当好的框架,能做出许多东西来,使用框架不是会让事情更容易吗?当然,如果你一定要做的话……
+
+CI是开源产品,下载后你就能看到所有的代码。这包括使CI工作的基本library(保存在system/libraries中)与你自己的libraries(保存在system/application/libraries中),所以你可以改变CI,使其以你喜欢的方式工作。
+
+修改系统libraries文件存在两个问题,就是:
+
+ 谁也不能保证你的新代码与CI的其他部分或更新的版本兼容。这可能会导致不易跟踪的、敏感的或奇怪的错误。
+
+ 如果你稍后升级你的CI版本,系统目录也可能会随之改变。你修改过的library将被新的文件覆盖或更新,因此你需要自行修改并将其更新到升级的版本中。
+
+不过,对可能有人会胡乱修改CI类库的情况,从1.5版起增加了两个明确的“工作区”。(不包括基本的“数据库”和“控制器”类,这两个都很危险,请不要自行修改。)
+
+ 第一,当你在/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类中派生一个子类。
\ No newline at end of file
diff --git a/_docs/13.txt b/_docs/13.txt
new file mode 100644
index 0000000..6160cbf
--- /dev/null
+++ b/_docs/13.txt
@@ -0,0 +1,3859 @@
+快捷的 CRUD 及其配合使用
+
+
+
+ 编写任何一个动态网页时,最基本的,也是最令人厌烦的部分是 CRUD。你有一个或多个数据表时,你需要去对它们每一个实体进行建立、读取、更新与删除。后来,你将对数据处理会变得灵活,但在此之前也有一些界面友好的方式来处理它,并保留下来,但对你的网站是不能用的。
+
+ 不过,这牵涉到写各种各样的 CRUD 方法,虽然在理念上相当容易,但这些是相当复杂且费时的工作。所以对于我们的网站,我写了一个通用的 CRUD 模型,使用 CI 的类及辅助函数,使之更加容易。在这一章中,你会看到这个模型如何工作,如何将它集成到我们的应用程序中来。
+
+ CRUD 模型不只是 CRUD。它验证用户输入的数据,并用多种方式检查它。如:当你在特定行做“delete”操作时,或者通过返回进入到表单并且在你的浏览器中重新加载它,你不小心重复“create”操作时。
+
+ 最后,CRUD 模型包含了它自己的自检框架,因此你能够把它作为你的架构或适应你的代码,进行开发测试。
+
+ CRUD 除此之外,还能做更全面,更优秀的代码(参见第 15 章)。然而,我们所要做的是对我们以前所学的课程进行一个好的总结与提高。
+
+ 此章对模型所要展示的代码如下:
+
+
+
+
+
+ * 设计原理。
+
+ * 一个标准的控制器与模型配合使用。
+
+ * 数据库表必须是有条理的。
+
+ * 模型本身: 保存数据库信息的数组、分离函数。
+
+ * 自检函数。
+
+
+
+
+
+
+
+
+
+CRUD 模型: 设计原理
+
+
+
+ 在这个 CRUD 模型背后的想法是它能被任何表的任何控制器所调用。对于数据表的数据,在一个数组中,如何从过去显示的数据去更新它,所有都是标准:控制器恰好识别它自己(并且在表中起作用)并且需要对每条记录给一个 ID 号。因此我们不得不写一些简单的控制器,连接到数据库去取数据,并展现它到表单中去。
+
+ 记得我们不能直接使用一个模型,因此每次我们不得不需要通过一个控制器不使用它。你可以放一些代码到控制器中,但你不得不复制它到其它新的控制器中去。有一种方法,只通过在模型中的一套 CRUD 代码,我们就能去更新,去维护了。代价是你必须去保持控制器与模型之间来去通畅,如何使代码更简洁,更多的困难在后面。
+
+ 为了简单,我们使用在代码中没有定义的两个扩展函数:
+
+
+
+
+
+ * failure(), 当报告有错误时,我们仍然想继续执行。
+
+ * 被调用的模型显示-建立菜单与设置基准URL等。因而CRUD函数建立了一大堆数据,放在 $data 变量中,可以简单的调用:
+
+ 复制内容到剪贴板
+
+ PHP 代码:
+
+
+
+ 1.
+
+ $this->display->mainpage($data);
+
+
+
+
+
+ 我想让 CRUD 模型能够自检,因此它包含了一个自检组件。如果我们愿意的话,可以在设计处理期间允许我们去调用它。(编写测试套件会让你发现很多错误,起初不经意的地方会出现错误让你很是意外,但是现在发现错误,总比以后你的客户访问网站发现错误要好。)
+
+ 请以一个折衷的方法记住每一个模型。你要求它的越多,它要求你的也越多。例如,除非你的数据库表以某种特殊方式下,这个模型它不工作。它以完全的高级方式显示在表格中,但它不能灵活处理。它没有使用 Javascript 来更好的适应用户的习惯。它不能用它自己的规则来处理异常。在其它方面,如果你只想实现一系列标准事件(它是很通用的),它是很简单达到的。
+
+
+
+
+
+标准的控制器格式
+
+
+
+ 首先,对每一个数据表,你需要有一个标准的控制器。它是怎样让用户与你的数据表接口,如:新增记录,更新记录等。对于新增一个新的人员,用户将与人员数据表对接,因此需要一个不同的控制器:但它与其它的控制器大多是相同的。
+
+ 下面是我们一个 Sites 数据表的控制器:
+
+复制内容到剪贴板
+
+PHP 代码:
+
+
+
+ 1.
+
+ load->model('crud');
+
+ 15.
+
+ }
+
+ 16.
+
+
+
+ 17.
+
+ /*function to update an entry (if an ID is sent) or to insert a new
+
+ 18.
+
+ one. Also includes validation, courtesy of CI */
+
+ 19.
+
+ function insert($id)
+
+ 20.
+
+ {
+
+ 21.
+
+ $this->crud->insert($this->controller, $id);
+
+ 22.
+
+ }
+
+ 23.
+
+
+
+ 24.
+
+ /*interim function to pass post data from an update or insert through
+
+ 25.
+
+ to Crud model, which can't receive it directly*/
+
+ 26.
+
+ function interim()
+
+ 27.
+
+ {
+
+ 28.
+
+ $this->crud->insert2($this->controller, $_POST);
+
+ 29.
+
+ }
+
+ 30.
+
+
+
+ 31.
+
+ /*function to delete an entry, needs table name and id. If called
+
+ 32.
+
+ directly, needs parameters passed to function; if not, from Post
+
+ 33.
+
+ array*/
+
+ 34.
+
+ function delete($idno=0, $state='no')
+
+ 35.
+
+ {
+
+ 36.
+
+ if(isset($_POST['id'])&& $_POST['id'] > 0)
+
+ 37.
+
+ {$idno = $_POST['id'];}
+
+ 38.
+
+ if(isset($_POST['submit']))
+
+ 39.
+
+ {$state = $_POST['submit'];}Instant CRUD—or Putting it All Together
+
+ 40.
+
+
+
+ 41.
+
+ $this->crud->delete($this->controller, $idno, $state);
+
+ 42.
+
+ }
+
+ 43.
+
+
+
+ 44.
+
+ /*function to show all entries for a table*/
+
+ 45.
+
+ function showall()
+
+ 46.
+
+ {
+
+ 47.
+
+ $this->crud->showall($this->controller, $message);
+
+ 48.
+
+ }
+
+ 49.
+
+
+
+ 50.
+
+ /*function to show all data in a table, but doesn't allow any
+
+ 51.
+
+ alterations*/
+
+ 52.
+
+ function read()
+
+ 53.
+
+ {
+
+ 54.
+
+ $this->crud->read($this->controller);
+
+ 55.
+
+ }
+
+ 56.
+
+
+
+ 57.
+
+ /*function to set off the test suite on the 'crud' model. This
+
+ 58.
+
+ function need only appear in one controller, as these tests are made
+
+ 59.
+
+ on a temporary test table so that your real data is not affected*/
+
+ 60.
+
+ function test()
+
+ 61.
+
+ {
+
+ 62.
+
+ $this->crud->test();
+
+ 63.
+
+ }
+
+ 64.
+
+ }
+
+ 65.
+
+ ?>
+
+
+
+ 你领会之后,你会发现它是优美苗条并且十分通用的。如果你想让 people 控制器去代替 Sites 控制器--换句话说,允许你在 people 表中去建立,读取,更新或删除记录,等等,你需要做如下事情:
+
+
+
+
+
+ * 更改 Sites 为 People (首字母大写!)。
+
+ * 更改 $controller 变量 sites 为 people (小写)。
+
+ * 更改构造函数名Sites 为 People (首字母大写)。
+
+ * 保存新的控制器为:system/application/controllers/people.php.
+
+
+
+
+
+ 控制器名必须严格的与数据表名一样,如对于 people 表,它必须是 people。在类定义行与构造函数中,名字的首字母必须是大写,但是其他地方不一定要这样。
+
+
+
+
+
+数据库表
+
+
+
+ 对于你的数据表有三个简单的规则:
+
+
+
+
+
+ * 最主要是第个表有 ID 字段为主键并且为自增字段 (这是一个标准的 MySQL 字段类型。新增记录时,自动建立一个新的不重复的数字)。
+
+ * 如果你想使用它在一个动态的下拉列表,你有一个 NAME 字段。
+
+ * 你也必须有一个 SUBMIT 字段来存储状态,殊如等等。
+
+
+
+
+
+ 除此之外,你可拥有任何你想要的字段,并随意对其命名,其他所有都由 CRUD 模块处理,适用于设计针对与这些行一起配合成对的任何控制器/数据表。
+
+
+
+
+
+模型的心脏:数组
+
+
+
+ 准备工作完成后。让我们开始创建 CRUD 模型吧。
+
+ 首先你需要定义 CRUD 模型与一个构造函数。标准写法如下:
+
+复制内容到剪贴板
+
+PHP 代码:
+
+
+
+ 1.
+
+ load->helper('form');
+
+ 15.
+
+ $this->load->helper('url');
+
+ 16.
+
+ $this->load->library('errors');
+
+ 17.
+
+ $this->load->library('validation');
+
+ 18.
+
+ $this->load->database();
+
+ 19.
+
+ $this->load->model('display');
+
+
+
+ 保存它到 system/application/models/crud.php。
+
+ 继续钻研,但你仅需要做一次下面的事。你要写一个多维数组(我刚开始的时候从一本书上学的PHP――它是非常好的――上面说“多维数组不会经常遇到,因此我们以后都没有必要深入的学习它”。看来现在开始就要用它了)。
+
+ 我们数组的一维是数据表列表(sites,pepple,等)。二维是每个表的字段列表。对于 sites 表,就是 id, name, url 等。三维是描述每个字段与提供将在插入/更新表单中处理控制的一组变量。如下:
+
+
+
+
+
+ * 在输入框显示你想希望被用户看的文本: 这个字段是如何向人们描述的.(所以它的第一个字段是网站 ID 而不仅仅是 ID). 这个会让你的表单更加用户友好。
+
+ * 你可以在你的插入/更新表单上显示表单类型的字段: 这可以是一个输入框、一个文本域或者一个下拉框。(CRUD 模型定制了一些设置但不是全部。)
+
+ * 当用户填写表单的时候,你可以加入 CI 的验证规则。这个可以留空。
+
+ * 如果你希望动态下拉框显示这个字段, 它的表名将会显示出来.看下面的这个解释,它也同样可以留空。
+
+
+
+
+
+ 我们已经声明了类里面的一个变量 $form 为数组,所以以后任何时候,我们必须这样用它 $this->form。它被定义在构造函数里, 它直接跟随前面的代码。
+
+复制内容到剪贴板
+
+PHP 代码:
+
+
+
+ 1.
+
+ $this->form =
+
+ 2.
+
+ array
+
+ 3.
+
+ ('sites' => array
+
+ 4.
+
+ (
+
+ 5.
+
+ 'id' => array('ID number of this site',
+
+ 6.
+
+ 'readonly', 'numeric'),
+
+ 7.
+
+ 'name' => array('Name of site', 'textarea',
+
+ 8.
+
+ 'alpha_numeric'),
+
+ 9.
+
+ 'url' => array('Qualified URL,
+
+ 10.
+
+ eg http://www.example.com', 'input', ''),
+
+ 11.
+
+ 'un' => array('username to log in to site',
+
+ 12.
+
+ 'input', 'numeric|xss_clean'),
+
+ 13.
+
+ 'pw' => array('password for site', 'input',
+
+ 14.
+
+ 'xss_clean'),
+
+ 15.
+
+ 'client1' => array('Main client',
+
+ 16.
+
+ 'dropdown', '', 'people' ),
+
+ 17.
+
+ 'client2' => array('Second client', 'dropdown',
+
+ 18.
+
+ '', 'people'),
+
+ 19.
+
+ 'admin1' => array('First admin', 'dropdown',
+
+ 20.
+
+ '', 'people'),
+
+ 21.
+
+ 'admin2' => array( 'Second Admin', 'dropdown',
+
+ 22.
+
+ '', 'people'),
+
+ 23.
+
+ 'domainid' => array('Domain name', 'dropdown',
+
+ 24.
+
+ 'numeric', 'domains'),
+
+ 25.
+
+ 'hostid' => array( 'Host', 'dropdown',
+
+ 26.
+
+ 'numeric', 'hosts'),
+
+ 27.
+
+ 'submit' => array( 'Enter details', 'submit', 'mumeric')
+
+ 28.
+
+ ),
+
+ 29.
+
+ 'domains' => array
+
+ 30.
+
+ (
+
+ 31.
+
+ 'id' => array('ID number of this domain',
+
+ 32.
+
+ 'hidden', 'numeric'),
+
+ 33.
+
+ //etc etc etc!!
+
+
+
+ 你会发现 $from 数组里每一个表(这里指站点和域名,虽然由于空间的原因我只开通了后者)都有一个二级数组,每个二级数组都包含了他们自有的三级数组,每一个字段(“id”、“name”等等)对应一个。每个三级数组都是依次排列在数组中,包含了三个或者四个在前面描述的值。
+
+ 你并不能很容易的理解这种数组,但是从概念上看很简单。
+
+ 为完成我们应用程序中的表的设置,创建该数组约需 120 行。但是,你只需要去做一次!这是你的模型的心脏。用括号'}'关闭这个构造函数,并继续创建 CRUD 模型的其他方法。
+
+ 如果你需要改变你的数据库表(例如添加一个新的字段),或者你要改变你的验证规则,那么你只需要改变数组里的数值。他们将会自动更改:举例来说,当您下次尝试增加新的条目时,你应该看到在表上反映的变化。
+
+
+
+
+
+CRUD 模型
+
+
+
+ 以下各个方法组成了 CRUD 模型:
+
+
+
+
+
+Showall 方法
+
+
+
+ 这是一个用户最常用的方法。它可以作为一个切入点,对输入所有其他的操作,添加,更新,或删除。它显示你表里已有内容。网站表上的一些测试数据,它看起来是这样的:
+
+1.gif (10.74 KB)
+
+2008-3-5 18:26
+
+
+
+ 如同你看到的,在这个页面中你可以更新或删除整个网站。你可以添加新的内容,或者从表中读取所有数据。
+
+ 顺便说一句,请大家不要忘记,该模型并不包括任何安全规定。在一个真实的网站中,你也许要微调用户的选项-例如,允许更新而不能删除。你要确保黑客无法通过输入 URL(例如:www.example.com/index.php/sites/delete/18) 访问 CRUD 模型的功能。CI 的基于 URL 的结构使得它比较容易推断出系统如何访问这些命令,所以你可能希望在 CRUD 模型激活以前,确保用户已登录到网站中。
+
+ 回到 CRUD 机制。记住,用户不能直接调用模型。每一个操作(删除,更新等)都是通过控制器来调用的。控制器用下面这行代码调用 showall 方法:
+
+复制内容到剪贴板
+
+PHP 代码:
+
+
+
+ 1.
+
+ $this->crud->showall($this->controller);
+
+
+
+ 换句话说,就是用 showall 方法取代网站的 $this->controller,并传递一个参数到 CRUD 方法中,就是要告诉它,它在取代哪个控制器的功能。
+
+ 我们现在来看看 showall 方法。我们已经把第一个参数传递给它了。我们把 $message 留到后面。集中看标为高亮的行。
+
+复制内容到剪贴板
+
+PHP 代码:
+
+
+
+ 1.
+
+ /*this function lists all the entries in a database table on one
+
+ 2.
+
+ page. Note that every db table must have an 'id' field and a 'name'
+
+ 3.
+
+ field to display!
+
+ 4.
+
+ This page is a jumping-off point for the other functions - ie to
+
+ 5.
+
+ create, read, update or delete an entry.
+
+ 6.
+
+ When you've done any of these, you are returned to this page. It has a
+
+ 7.
+
+ 'message' parameter, so you can return with a message - either success
+
+ 8.
+
+ or failure.*/
+
+ 9.
+
+
+
+ 10.
+
+ function showall($controller='', $message = '', $test ='no')
+
+ 11.
+
+ {
+
+ 12.
+
+ $result = '';
+
+ 13.
+
+ $mysess = $this->session->userdata('session_id');
+
+ 14.
+
+ $mystat = $this->session->userdata('status');
+
+ 15.
+
+ if(!$this->db->table_exists($controller))
+
+ 16.
+
+ {
+
+ 17.
+
+ $place = __FILE__.__LINE__;
+
+ 18.
+
+ $outcome = "exception:$place:looking for table
+
+ 19.
+
+ $controller: it doesn't exist'";
+
+ 20.
+
+ /*test block: what if there is no controller by that name?*/
+
+ 21.
+
+ if($test =='yes')
+
+ 22.
+
+ {
+
+ 23.
+
+ return $outcome;
+
+ 24.
+
+ }
+
+ 25.
+
+ else{
+
+ 26.
+
+ $this->failure($outcome, 'sites');
+
+ 27.
+
+ }
+
+ 28.
+
+ }
+
+ 29.
+
+ /*end test block*/
+
+ 30.
+
+ $this->db->select('id, name');
+
+ 31.
+
+ $query = $this->db->get($controller);
+
+ 32.
+
+ if ($query->num_rows() > 0)
+
+ 33.
+
+ {
+
+ 34.
+
+ $result .= "
";Instant CRUD—or Putting it All Together
+
+ 35.
+
+
+
+ 36.
+
+ $result .= "
";
+
+ 18.
+
+ $result .= anchor("$controller/showall",
+
+ 19.
+
+ "No, don't delete");
+
+ 20.
+
+ $data['text'] = $result;
+
+ 21.
+
+ $this->display->mainpage($data);
+
+ 22.
+
+ }
+
+ 23.
+
+ }
+
+
+
+ 只是为了清楚起见,这里有一张如何在删除操作中传递控制行为的图表。
+
+5.jpg (33.36 KB)
+
+2008-3-5 18:26
+
+
+
+ 你可以看到,这比我们先前的例子复杂的多。模型会处理所有的工作,但用户只能调用控制器,所以如果你需要后退并向用户重新显示问题,你需要再次调用控制器。
+
+ 不过,一旦你整理出来,它运作良好,而且还具有高度的逻辑性。CI 强加这个框架给你,但长远来说,这就是一个优势。你的代码是一致的,模块化的。注意模型和视图每次被调用时的相同处:他们向用户展示什么依赖于调用他们的 CRUD 模型里的方法。
+
+
+
+
+
+Insert 方法
+
+
+
+ 这是最复杂的方法,因为它生成一个让用户填写的表单。(和人相关的接口总是最复杂的东西……)
+
+ 与其写两个独立的方法,一个用来插入、一个更新,而且需要建立两次表单,我写一个方法来完成这两个工作。如果你提供了一个合法的 ID 号,就更新相应的记录;如果未提供,则插入一条新记录。
+
+ 简便起见,我没有加入我们在 delete 方法中的测试模块。
+
+ 以下就是我们使用本章开头定义的那个数组的地方。这个方法生成了一个表格,通过使用 CI 的表单辅助函数,基于数组中特定的表单元素(下拉,文本域等等。)。方法的核心是一个状态切换来实现如下工作。
+
+ 这段代码使用 CI 的验证类来帮助我们检查输入的数据:记住我们在初始数组中设置了验证规则。
+
+复制内容到剪贴板
+
+PHP 代码:
+
+
+
+ 1.
+
+ /*the most complex function. This creates an HTML form, based on the
+
+ 2.
+
+ description of the fields in the form array. This is sent to our
+
+ 3.
+
+ display model, which sets up a view and shows it to the user.
+
+ 4.
+
+ The view then sends a POST array back to the controller. The form
+
+ 5.
+
+ can't call this model directly, so it has to call the controller,
+
+ 6.
+
+ which refers it back to the model.
+
+ 7.
+
+ Note the function parameters:
+
+ 8.
+
+ 1. The controller parameter is whichever controller/ table has called
+
+ 9.
+
+ the model - eg the 'sites' controller, or the 'domains' controller.
+
+ 10.
+
+ The controller has the same name as the table it manipulates.
+
+ 11.
+
+ 2. The optional id parameter is the id of an individual entry in that
+
+ 12.
+
+ table.
+
+ 13.
+
+ 3. The optional 'test' parameter is so you can set the form up to make
+
+ 14.
+
+ usable responses to self-test functions.
+
+ 15.
+
+ */
+
+ 16.
+
+
+
+ 17.
+
+ function insert($controller='', $id=0, $test='no')
+
+ 18.
+
+ {
+
+ 19.
+
+ $myform = '';
+
+ 20.
+
+ $myid = 0;Instant CRUD—or Putting it All Together
+
+ 21.
+
+
+
+ 22.
+
+ $currentvalue = array();
+
+ 23.
+
+
+
+ 24.
+
+
+
+ 25.
+
+ /*test if the table exists*/
+
+ 26.
+
+ if(!$this->db->table_exists($controller))
+
+ 27.
+
+ {
+
+ 28.
+
+
+
+ 29.
+
+ $place = __FILE__.__LINE__;
+
+ 30.
+
+ $outcome = "exception: $place:looking for table
+
+ 31.
+
+ $controller: it doesn't exist'";
+
+ 32.
+
+ if($test =='yes')
+
+ 33.
+
+ {
+
+ 34.
+
+ return $outcome;
+
+ 35.
+
+ }
+
+ 36.
+
+ else{
+
+ 37.
+
+ $this->failure($outcome, $controller);
+
+ 38.
+
+ }
+
+ 39.
+
+ }
+
+ 40.
+
+ else
+
+ 41.
+
+ {
+
+ 42.
+
+ if($test =='yes')
+
+ 43.
+
+ {
+
+ 44.
+
+ return 'OK';
+
+ 45.
+
+ }
+
+ 46.
+
+ }
+
+ 47.
+
+ /*end test block*/
+
+ 48.
+
+
+
+ 49.
+
+ /*next check if there is an id number. If there is, we need to get the
+
+ 50.
+
+ values to populate the table fields*/
+
+ 51.
+
+ if(isset($id) && $id > 0)
+
+ 52.
+
+ {$myid = $id;
+
+ 53.
+
+ $this->db->where('id', $id);
+
+ 54.
+
+ $query = $this->db->get($controller);
+
+ 55.
+
+ if ($query->num_rows() > 0)
+
+ 56.
+
+ {
+
+ 57.
+
+ $row = $query->row();
+
+ 58.
+
+ //--------------work out the values we want!
+
+ 59.
+
+ foreach($row as $key =>$value)
+
+ 60.
+
+ /*
+
+ 61.
+
+ first of all work out what value you want to show as the existing
+
+ 62.
+
+ value in each line of the form. In priority order these are:
+
+ 63.
+
+ 1. the last value the user entered, from the post array
+
+ 64.
+
+ 2. the value from the database
+
+ 65.
+
+ 3. nothing, if neither of these is set.
+
+ 66.
+
+ if we got here, the id does exist and is returning values, so get the
+
+ 67.
+
+ existing values into a value array. Or, if there is something in the
+
+ 68.
+
+ validation array, use that instead*/
+
+ 69.
+
+ {
+
+ 70.
+
+ $_POST[$key] = $this->validation->$key;
+
+ 71.
+
+
+
+ 72.
+
+ if(isset($_POST[$key]))
+
+ 73.
+
+ {$currentvalue[$key] = $_POST[$key];}
+
+ 74.
+
+ else
+
+ 75.
+
+ {$currentvalue[$key] = $value;}
+
+ 76.
+
+ }
+
+ 77.
+
+
+
+ 78.
+
+
+
+ 79.
+
+ /*test block: there was an id number, so has the program gone for an
+
+ 80.
+
+ update? if this is not a test, of course, just do the update*/
+
+ 81.
+
+ if($test == 'yes')
+
+ 82.
+
+ {
+
+ 83.
+
+ $place = __FILE__.__LINE__;
+
+ 84.
+
+ $outcome = "exception: $place: id of $id
+
+ 85.
+
+ returned results from $controller table so have gone for update";
+
+ 86.
+
+ return $outcome;
+
+ 87.
+
+ }
+
+ 88.
+
+
+
+ 89.
+
+ /*end test block*/
+
+ 90.
+
+ $myform .= "
Update
+
+ 91.
+
+ existing entry number $id
";
+
+ 92.
+
+ }
+
+ 93.
+
+ /*now catch situation where this query isn't returning results. We
+
+ 94.
+
+ could only have got here with an integer set as our ID number, so
+
+ 95.
+
+ this probably means we are trying to delete an entry that doesn't
+
+ 96.
+
+ exist.*/
+
+ 97.
+
+ else{
+
+ 98.
+
+ $place = __FILE__.__LINE__;
+
+ 99.
+
+ $outcome = "exception: $place: despite
+
+ 100.
+
+ id of $id cant get any results from $controller table";
+
+ 101.
+
+
+
+ 102.
+
+
+
+ 103.
+
+ if($test == 'yes')
+
+ 104.
+
+ /*test block: there was and ID but there were no results*/
+
+ 105.
+
+ {
+
+ 106.
+
+ return $outcome;
+
+ 107.
+
+ }
+
+ 108.
+
+ /*end test block*/
+
+ 109.
+
+ else
+
+ 110.
+
+ {$this->failure($outcome, $controller);}
+
+ 111.
+
+
+
+ 112.
+
+
+
+ 113.
+
+ }
+
+ 114.
+
+ }
+
+ 115.
+
+
+
+ 116.
+
+
+
+ 117.
+
+ /*there was no ID number, so this is a new entry*/
+
+ 118.
+
+ else{
+
+ 119.
+
+ /*If the user has filled in values, and has returned here because some
+
+ 120.
+
+ of them didn't validate, we still need to repopulate the form with
+
+ 121.
+
+ what he entered, so he only has to alter the one that didn't validate.
+
+ 122.
+
+ Get these from the post array*/
+
+ 123.
+
+
+
+ 124.
+
+
+
+ 125.
+
+ if(isset($_POST))
+
+ 126.
+
+ {
+
+ 127.
+
+ foreach($_POST as $key => $value)Instant CRUD—or Putting it All Together
+
+ 128.
+
+
+
+ 129.
+
+ {
+
+ 130.
+
+ if(isset($_POST[$key]))
+
+ 131.
+
+ {$currentvalue[$key] = $_POST[$key];}
+
+ 132.
+
+ }
+
+ 133.
+
+
+
+ 134.
+
+ }
+
+ 135.
+
+ $myform .= "
New entry
";
+
+ 136.
+
+
+
+ 137.
+
+ /*test block: there was no ID, so this is a new entry*/
+
+ 138.
+
+ if($test == 'yes')
+
+ 139.
+
+ {
+
+ 140.
+
+ $place = __FILE__.__LINE__;
+
+ 141.
+
+ $outcome = "exception: $place: id of $id
+
+ 142.
+
+ treated as no id, so going for new entry";
+
+ 143.
+
+ return $outcome;
+
+ 144.
+
+ }
+
+ 145.
+
+ /*end test block*/
+
+ 146.
+
+ }
+
+ 147.
+
+
+
+ 148.
+
+
+
+ 149.
+
+
+
+ 150.
+
+ /*the table exists, whether this is an update or new entry, so start
+
+ 151.
+
+ to build the form*/
+
+ 152.
+
+ $myform .= "
";
+ } else {
+ $report = "no tests for this site yet.";
+ }
+ $report .="
Other options
";
+ $report .= "
";
+ $report .= anchor("tests/getwrittenreport/$row->siteid/604800", 'Get a written report for last week');
+ $report .= anchor("tests/getwrittenreport/$row->siteid/2592000", 'Get a written report for last month');
+ $report .= "
";
+ $report .= anchor("tests/getbaseremotefiles/$row->siteid", 'Update remote file list for this site');
+ $report .= "
";
+ $response['mytext'] = $report;
+ $this->display->mainpage($response);
+}
+
+你能看到它已经查询了数据库以找出针对我们选择的网站要做哪些测试。 只有一个测试, 一个简单的 'ping', 这个测试被建议需要每 15 分钟做一次。
+
+因为在最近的 15 分钟内还没有这样做,因此,它已经显示为过期,而且一个超链接让我们可以做这一个测试。 如果我点击超链接,我调用了tests控制器的runtest函数,提供它我想要的测试的ID值。
+
+
+测试完成后,系统会告诉我什么时候进行的测试,有没有问题, 和花了多长时间。
+
+如果我现在返回到report函数,你可以重新调用针对我选择的这个网站的上一周,上一月的测试报告。我只是把这个网站作为一个例子,因此,它没有真正的测试历史: 但是如果我执行了连续的一系列的测试,报告中将会增这些测试的结果。
+
+正如你看到的一样,我们正在建立一项报告,它能在不同的时间以不同的测试方式得到某位客户的网站正在运行着并且能作出回应的结果。测试的任一次都不会比另外一次来得更有趣, 但是可以帮助我们得到一个比较长的时间段的图表。
+
+实际上进行测试的函数被构造成一个swith语句。 它有二个叁数:
+
+。 测试的ID值, 它给我们提供了我们需要运行的网站的基本数据: URL,域名/地址,测试完成后需要的文字信息
+
+。 用户类型。 (如果用户真正的人,程序会以对人更友好的格式返回更多的信息-换句话说,如果你想要在屏幕上显示结果,调用时把用户参数设置为人。 如果你想要系统处理结果, 将这一个叁数设定为其它。)
+
+在这个代码片段中,我们已经定义了测试的一些类型。 二个例子是:
+
+。 'ping'测试,只是简单地调用URL。 如果他们得到一个结果,他们或是用一个预设的格式来分析它(在数据库中被称为 '正则表达式') 或是在没有设置正则表达式时作为一个一般性的 HTML 术语。
+
+。 'ete' 测试,用一些我们开发的代码来对一个被保护的网页进行一个'全面' 的测试, 登录后查找一个预期的片段。 这段代码不在这一本书中进行解释,因为它属于CI的函数。
+
+每个测试返回一个$result变量和一个$timetaken变量,这些作为一个记录被保存在数据库的'events'表中,连同一些来自数据库的其它的信息。下面列出代码,这些代码很大程度上依靠CI的AR模型读写数据库,并且使用benchmark类统计每次测试需要的时间。
+
+ /*function to run an individual test*/
+ function runtest($testid, $user='human') {
+ /*first, look up the test details */
+ $this->db->where('id', $testid);
+ $query = $this->db->get('tests');
+ if ($query->num_rows() > 0) {
+ foreach ($query->result() as $row) {
+ $type = $row->type;
+ /*then work out which type it is and forward it accordingly*/
+ switch ($type){
+ case 'ping':
+ $this->benchmark->mark('code_start');
+ $result =$this->pingtest($testid);
+ $this->benchmark->mark('code_end');
+ $timetaken = $this->benchmark->elapsed_time('code_start', 'code_end');
+ break;
+ case 'ete' :
+ $this->benchmark->mark('code_start');
+ $result = $this->httppost($testid);
+ $this->benchmark->mark('code_end');
+ $timetaken = $this->benchmark->elapsed_time('code_start', 'code_end');
+ break;
+
+ default:
+ $result = 'noid';
+ }
+ /*work out which site the test belongs to*/
+ $this->db->select('tests.siteid AS id');
+ $this->db->where('id', $testid);
+ $query = $this->db->get('tests');
+ if ($query->num_rows() > 0) {
+ $srow = $query->row();
+ $mysiteid = $srow->id;
+ } else {
+ $mysiteid = 0;
+ }
+ /*build the rest of the result set and enter it into the database*/
+ $time = now();
+ if($result == 'OK') {
+ $isalert = 'n';
+ } else {
+ $isalert = 'y';
+ }
+ $this->db->set('name', $type);
+ $this->db->set('type', 'test');
+ $this->db->set('timetaken', $timetaken);
+ if($result != '') {
+ $this->db->set('result', $result);
+ }
+ $this->db->set('testid', $testid);
+ $this->db->set('userid', 0);
+ $this->db->set('siteid', $mysiteid);
+ $this->db->set('time', $time);
+ $this->db->set('isalert', $isalert);
+ $this->db->insert('events');
+ $mydata = array(
+ 'lastdone' => $time,
+ 'notes' => $result,
+ 'isalert' => $isalert
+ );
+ $this->db->where('id', $testid);
+ $this->db->update('tests', $mydata);
+
+ /*only return this info to screen if user is human. Otherwise, no need to do anything more; you've updated the database.*/
+
+ if($user == 'human') {
+ $mytext = "
";
+ $mytext .= "
Test took $timetaken.
";
+ $mytext .= "
Result was ".$result.
+ '
';
+ $mytext .= $this->testhistory($testid);
+ $response['mytext'] = $mytext;
+ $this->display->mainpage($response);
+ } else {
+ return $response;
+ }
+ }
+ }
+ }
+
+
+
+我们如何实际上打印出测试报告是一个 CI 能提供较大帮助的有趣例子。你可以有多种的选择。你能用硬编码的方式打印出你的报告,在你的代码中使用 HTML 就象我们早些时候所做的,象这样:
+
+ /*do database query here!*/
+ /*now format the results the hard way……*/
+
+ $report .= "
Test history:
";
+ $report .= "
Time
Name
Time taken
Status
";
+
+ if ($query->num_rows() > 0) {
+ foreach ($query->result() as $row) {
+ $report .= "
";
+
+
+另一方面,你可以使用 HTML 的表格助手来使生活变得更容易:
+
+ /*do db query here */
+ /*format the results using the CI HTML table library*/
+ $report .= "Test history:";
+ /*redefine our CI table layout if we want to, using our css file*/
+ $tmpl = array ('table_open' => '
之类的,甚至更糟糕的是,生成.rtf格式或一些类似的富文本格式。
+
+另一个选择是可以使用其它可能的 PHP 语法或模板语法分析器类,把变量或占位符号, 放在视图中。 (针对这些内容在第 5 章有些简要的论述。) 然后,你把真正的,未格式化的数据传递给视图。 这可能使 MVC 纯粹论者满意,我的观点是这种什么是把一个复杂的额外的层加入到代码中。但是这仅是我个人的观点,而且多数人会不同意这一点。
+
+重要的一点是 CI 提供多种选择: 它让你自由选择你感到愉快的那一种。没有绝对对或者错的方法: 确实有一些方法比另一些表现更好,适合你本人的方法的是最好的方法。
+
+一个平衡表
+
+让我们回顾我们已经在这一本书中涉及的内容。 CI 有帮助吗?
+
+CI 能提供帮助的地方: 结构
+
+即使只观察我们网站一部分比如说一个模型,很明显发现一些有实用价值的应用正在变得很复杂。CI, 通过建议或者一定程度上的强迫,构建了一个 MVC 结构, 来帮助你使复杂的东西变得更有条理。 虽然你还可能忘记你把一段代码放到哪里去了, 或者会在不同的控制器或模型中重复编写一个相似的函数: 但是它更多的是让你的代码更合乎逻辑。
+
+CI 的URL机制帮助你快速从一个代码文件连接到别一个。
+
+CI'超级对象' 没有 namespace 冲突,能够使代码互相调用并互相传递数据。 因为每一个变量都有自己的作用域,因此即使同名也都相安无事,不会引起混乱。同时,你能容易地在从你的代码中上存取所有的 CI 资源。
+
+CI 的 'config' 文件鼓励你们建立针对你网站参数的集中设置。
+
+所有的这些好处使你在开发网站时更容易,维护更方便,也更有利于别的程序员看懂代码。
+
+CI 能提供帮助的地方: 简化
+
+CI 在许多方面帮助你简化代码。 也许最好的例子来自AR类, 但是还有很多其它的方面。 CI 把复杂的代码隐藏在函数或一些类库中,做得很优秀,允许你使用一个简单的函数来调用它们。
+
+CI 能提供帮助的地方: 额外功能
+
+当你使用CI的时候,许多 CI 函数带给你许多额外的好处。象URL类和AR类能够自动读取config文件的设置参数,因此你不需要重复数据,因此,针对网站的修改只需要在一个地方进行就可以在整个程序得到应用。
+
+有许多小的例子-容易的方法使你能屏蔽机器人来抓取你的电子邮件住址, 举例来说 (见第 3 章),又或者AR类也能为你准备数据。
+
+事实上,CI主要的学习曲线 (至少对我来说) 就是去发现可以走什么捷径,并且请记住,使用它们来代替艰苦漫长的PHP编程。 如果这一本书能指导你更方便地找到捷径,它就达到了目的。
+
+CI 的问题
+
+CI 并不十全直美。 这意味着它本身是一个平衡体:轻巧便捷,而不是复杂和全面。正如有人曾经说过:'轻量级'意味着"我想要的东西都包含,我不想要的都不包含"。
+
+全面
+
+CI函数几乎包含了你在开发一般网站时所需要的全部内容。但也有一些CI主要函数库中没有包含的例外,而它们之中的绝大多数被CI社区中的用户提供的类库所覆盖。(详见下一章),或者你可以通过PEAR得到。 最明显的省略包括:
+
+。 AJAX类
+
+。 编写网络机器人的类
+
+。 一个创建加密网站的类, 处理登入,网页保护,以及其它的基本会话维护
+
+。 一个改进的 '脚手架' 类, 能提供面向外部网络功能,而不仅仅面向开发都。
+
+CI 也从Rails书中摘了一页,制作了一个代码生成器,能够帮助开发者建立客制化类。
+
+容易使用
+
+CI 提供你期待的学习效果。 但是, 假如你已经了解一些 PHP知识, 会变得相当容易。 事实上, 我发现CI主要的'学习曲线'是,如果你知道如何用标准 PHP 方法实现某个功能,你就会很容易地用CI实现。只有稍后,当你从用户手册中找到更多的内容时,你才会了解,做同一件事,使用CI类和助手会变得多么快捷。
+
+私下里讲,我发现有两个 CI 类较难理解和掌握。 一个是XMLRPC 类, 另一个是Validation类。主要是它们都需要不同网页或不同网站之间的接口。为了使它们工作,正确地设置它们有时候会很困难(分别参见第 8 和第 9 章)
+
+我也发现使用CI的 '超级对象'一开始也比较困难-见第 7 章. 可能是最陡峭的 CI 学习曲线, 错误的做法会让你迷惑,有挫败感,直到你掌握它。
+
+其余的: 都挺容易的。 如果有任何疑问,你总是可以去研究源代码。
+
+摘要
+
+这一章我们看到一些编码的例子, 还有许多我们已经在以前各章中一点一点地讨论过的函数。
+
+我们还看到CI可以:
+
+。组织你的网站
+
+。简化编程
+
+。增加功能
+
+我希望这一本书已经说服你在使用PHP开发动态网站时选择CI。
+
+它还是对开源运动和支持开源的人的贡献,源代码如此丰富,可以容易地,自由地和广泛地得到源代码。 谢谢,Rick!
+
+更为慷慨的一个主题,在这本书的最后一章,会提供一些让你更好地开发CI应用的资源-CI用户社区提供进一步的帮助,支持和其它的代码资源。
+
+
+下一章:资源和扩展
\ No newline at end of file
diff --git a/_docs/15.txt b/_docs/15.txt
new file mode 100644
index 0000000..730cd42
--- /dev/null
+++ b/_docs/15.txt
@@ -0,0 +1,724 @@
+资源和扩展
+
+翻译: 郝银龙
+
+
+
+ 好了,我们已经相当完整的讲解了 CI,并且,我希望能给你留下深刻的印象。在这个过程中,我们自己也编写了一些代码。我敢肯定,当你看过我的一些代码后,你会开始思考:“我可以写这么好...”。每个人都有自己的风格,并且 CI 给你很多的自由。
+
+ 在 CI 社区里有很多可以写出好代码的人,幸好他们中的许多人正准备利用业余时间做这件事情。所以,有很多代码能为您节省大量的工作。拿一个例子来说,你要创建一个数据取自数据库的动态图形,你可以坐下来好好写自己的代码,但实际上,至少有 3 个人已经解决了这个问题,并且他们都把其代码提供给了你。
+
+ 最后一章介绍一些你可以借鉴的资源,使你的编码快捷又方便。CI 有一个繁荣和活跃的社区,并且可用的资源一直都在发生变化,所以,我也没有试图制作一个完整的清单,只是让你知道这里有什么,并且到哪里可以看到。
+
+ 这是一个警告提示,太多的混乱代码很容易让人头晕。一些人写项目的时候,只注重代码的漂亮。大多数人都喜欢写代码,而没有写评论和注释的习惯。正因为这样,很多人就不容易理解一些类库和插件的使用方法。
+
+ 接下来,让我们看看这本书的最后一章所能给我们提供的帮助吧。
+
+1. 首先,让我们看看源代码。
+
+2. 然后,让我们看几个主题,并比较可用的代码。
+
+3. 最后,让我们看看更普遍的帮助源:在 PHP、MySQL 和 Apache 上。
+
+
+
+
+
+CI 的用户论坛
+
+
+
+ CI 有两个主要的资源:
+
+The user forums, at http://www.codeigniter.com/forums/ offer a lively and pretty well continuous discussion of most CI issues. Comments and suggestions made are not always helpful (or accurate), but there are a number of 'senior members' who usually make a lot of sense. It's quite a kind forum, too; people ask very obviously 'newb' questions, but get patient and helpful replies. Occasionally, Rick Ellis himself chips in, but he quite rightly doesn't try to field every issue himself.
+
+ 用户论坛的地址是 http://www.codeigniter.com/forums/ ,这里对大多数 CI 问题进行活跃的、几乎连续不断的讨论。评论和建议并不一定总有用(或准确),但也有一些“资深会员”,他们经常贡献很多智慧。它同样是一个非常友好的论坛;人们问一些非常明显的“菜鸟”问题,也会得到耐心并有价值的回复。有时 Rick Ellis 自己也会被一些东西所吸引,但他并没有去涉及所有领域确实是明智之举。
+
+
+
+
+
+The wiki, at http://www.codeigniter.com/wiki/ .This is intended as a "repository for tips, tricks, hacks, plugins, and enhancements." It contains a lot of useful code, although coverage is not systematic.
+
+ Wiki 的地址是 http://www.codeigniter.com/wiki/ 。这是一个集提示、技巧、Hack、插件和功能强化为一体的知识库,它包含很多有用的代码,尽管涉及的范围还不很系统化。
+
+
+
+Using the forum or the wiki is easy: you just create a membership for yourself (free) and then log on and do your thing.
+
+If you are seriously using CI, it's worth setting your RSS reader to subscribe to the 'recent changes' feed on the Wiki.
+
+ 论坛和 Wiki 使用起来都很简单:你只需为自己(免费)建立会员账号,然后登陆就可以做自己的事情了。
+
+ 如果你认真的使用 CI,那么有必要设置你的 RSS 阅读器来订阅 Wiki 的“近期更改” Feed。
+
+
+
+Remember, though, that:
+
+Not all plug-in writers are as technically competent as Rick Ellis. Their products may have bugs or issues.
+
+Some of the older plugins written before CI version 1.5 came out may need altering, because the way that libraries were initialized was changed (see Chapter 12). This should not be too difficult to do, but it does mean these library files won't work straight out of the box.
+
+ 然而,请记住:
+
+1. 并不是所有的插件编写者象 Rick Ellis 那样技术精湛,他们的作品可能会有一些 BUG 或问题。
+
+2. 一些在 CI 1.5 版本出现之前编写的早期插件可能需要修改,因为类库的初始化方式已经改变(见第 12 章)。这并不是很难修改,但是也证实了这些类库超出一定范围之后就难以稳定的运行。
+
+
+
+Video Tutorials
+
+If you want to be literally talked through your first CI application, there are three
+
+excellent video tutorials on the CI site.
+
+
+
+
+
+视频教程
+
+
+
+ 如果你想有人手把手的教你完成第一个 CI 应用程序的话,那么,在 CI 的网站上有 3 个优秀的视频教程。
+
+1. An introduction to CI.
+
+2. Create a blog in 20 minutes. Derek Jones builds basic blog pages, showing you how to set out the site, make database queries, and present the results in views.
+
+3.A link to an external video by Derek Allard (see http://video.derekallard.com/), which describes, among other things, how to use the Scriptaculous library to integrate AJAX and JavaScript effects. Using the view below, this shows you how to build an auto-complete text entry dropdown, using Ajax to update it.
+
+
+
+1. 对 CI 的介绍。
+
+2. 20 分钟建立一个 Blog。Derek Jones 建立了基本的 Blog 页面,向你展示怎样去设置这个网站,编写数据库查询,以及在视图中展示结果。
+
+3. 由 Derek Allard 制作的视频(详见 http://video.derekallard.com/),它描述了除其他事项外,如何利用 Scriptaculous 类库去集成 AJAX 和 JavaScript 特效。在下面的图片中,它向你展示如何去构建一个用 Ajax 更新的,有自动完成功能的文本输入下拉框。
+
+
+
+
+
+
+
+Available Plug-ins and Libraries
+
+
+
+Rick Ellis's intention and hope was that CI users would contribute 'plug-ins' or libraries to help other CI users. The framework has only been available for about a year, but already there is a lot of interesting code available.
+
+
+
+
+
+可用的插件和类库
+
+
+
+ Rick Ellis 的目的和希望是:CI 的用户能够把“插件”或类库贡献出来用于帮助其他的 CI 用户。这个框架刚出现大约一年,但已经有了很多有趣的代码。
+
+
+
+The number of plug-ins and libraries is growing steadily, and those already there are being changed. So the next section is not a systematic account of what's there: just a few notes on some of the things you might find useful. I'm sorry that I've had to miss out a lot of good stuff: please do look at the wiki yourself.
+
+ 插件和类库的数量在稳步的增长,已有的插件和类库也在不断的更新。因此下一节并不是一个全面的介绍:只有一部分相关的说明是你觉得有用的。我很抱歉,一直以来我漏掉了很多好资料:请务必亲自查看 Wiki。
+
+
+
+
+
+AJAX/JavaScript
+
+The wiki contains two AJAX packages: one using XAJAX, and the other the prototype.js/scriptaculous.js libraries.
+
+
+
+
+
+AJAX/JavaScript
+
+
+
+ 在 Wiki 里包含了 2 个 AJAX 包:其中一个使用 XAJAX,另外一个使用 prototype.js/scriptaculous.js 类库。
+
+
+
+Name Ajax for CI 1.5.1
+
+URL
+
+http://www.codeigniter.com/wiki/AJAX_for_CodeIgniter/
+
+Uses the prototype.js and scriptaculous.js libraries
+
+Download includes .js files as well as .php and a full User Guide. (This is not easy to
+
+understand if you don't already have a good grasp of AJAX and the DOM, and it could
+
+usefully have had some longer examples.) Simple to install: place the .php file in your
+
+application/libraries folder and the .js files in your root directory
+
+Newly released, so very few comments on the CI Forums.
+
+Author siric
+
+
+
+名称 Ajax for CI 1.5.1
+
+URL http://www.codeigniter.com/wiki/AJAX_for_CodeIgniter/
+
+ 使用 prototype.js 和 scriptaculous.js 类库
+
+ 下载包括了 .js 文件和 .php 文件,以及一个完整的用户指南。(如果你没有很好的掌握 AJAX 和 DOM 的话,这是不容易理解的,不过一些例子有助于你理解它。)简单的安装:把 .php 文件放到你的 application/libraries 文件夹下,把 .js 文件放到你的根目录下。因为刚刚正式发布,所以在 CI 的论坛上只有非常少的评论。
+
+作者 siric
+
+
+
+
+
+Name XAJAX
+
+URL http://www.codeigniter.com/wiki/XAJAX/
+
+A CI 'front end' for the XAJAX library. Includes its own JavaScript 'include' file,xajax.js
+
+Author Greg McLellan—based on the xajax php library (see http://www.xajaxproject.org/ )
+
+名称 XAJAX
+
+URL http://www.codeigniter.com/wiki/XAJAX/
+
+ XAJAX 类库的一个 CI“前端”。包括其自己的 Javascript 文件,xajax.js
+
+作者 Greg McLellan—基于 xajax 的 php 类库(详见 http://www.xajaxproject.org/ )
+
+
+
+
+
+Authentication
+
+
+
+Wiki users have also wrestled with security: these three packages look at authenticating your users and avoiding the possible pitfalls of storing session data in cookies.
+
+
+
+身份验证
+
+
+
+ Wiki 上的用户也困于安全问题:这 3 个包用于验证你的用户,并避免保存 Session 数据在 Cookie 中所可能出现的陷阱。
+
+
+
+
+
+Name FreakAuth_light
+
+URL
+
+http://www.4webby.com/freakauth/
+
+This includes a library to perform
+
+user loginlogout
+
+user registration
+
+remember password
+
+change password
+
+website reserved areas locking
+
+ a backend administration application to:
+
+manage users
+
+manage administrators
+
+
+
+名称 FreakAuth_light
+
+URL http://www.4webby.com/freakauth/
+
+ 它包括一个类库
+
+1. 用户登录/退出
+
+2. 用户注册
+
+3. 记住密码
+
+4. 修改密码
+
+5. 锁定网站的保留区域
+
+ 一个后台管理程序:
+
+1. 管理用户
+
+2. 管理管理员
+
+
+
+It allows you to set four levels of access (from superadmin down to guest) and then to set
+
+a 'check' method in controllers. This can be set either in the controller constructor or in
+
+individual functions. If a user invokes the controller (or the individual function) the code
+
+checks that he/she is logged in, consulting
+
+There's an extensive discussion of this code going on in the CI forums at the time of
+
+writing. Some errors have been identified, but the code is now on its third release and it
+
+looks as if the problems are being resolved.
+
+Author danfreak
+
+ 它允许你设置 4 个访问级别(从超级管理员到来宾),然后在控制器中设置一个“check”方法。它既能被设置在控制器的构造函数中,也能被设置在某个指定的函数中。如果用户调用控制器(或指定的函数),这段代码将检查他(她)是否登陆。
+
+ 在编写这段代码的时候,CI 论坛上正在对其进行广泛的讨论。一些错误已被发现,但是这段代码已发布到第三版了,这些错误看起来已经解决了。
+
+作者 danfreak
+
+
+
+
+
+Name Auth
+
+URL http://www.codeigniter.com/wiki/auth
+
+This package offers login/logout functionality, registration, with activation, and even a
+
+forgotten password reset. It's quite complex to set up: you have to set up a database table,
+
+include some new core libraries and helpers, and also do some configs.
+
+Works with CI 1.5.
+
+Author Anonymous
+
+
+
+名称 Auth
+
+URL http://www.codeigniter.com/wiki/auth
+
+ 这个包提供了登录/退出功能,带激活的注册,甚至忘记密码功能。它搭建起来非常复杂:你必须建立一个数据库表,引入一些核心的库文件和帮助文件,然后还要作一些配置。
+
+ 在 CI 1.5 下运行。
+
+作者 (匿名/未知)
+
+
+
+Name DB Session
+
+URL
+
+http://www.codeigniter.com/wiki/DB_Session/
+
+http://dready.jexiste.fr/dotclear/index.
+
+php?2006/09/13/19-reworked-session-handler-for-code-
+
+igniter
+
+Alters the CI session class (see Chapter 6) which stores session data in cookies. (Which can
+
+be encrypted, of course.) This class only stores a session identifiers: you add an extra table
+
+to your database, and it looks up all the rest of the session information there.
+
+Works with CI 1.5.
+
+Author dready
+
+
+
+名称 DB Session
+
+URL http://www.codeigniter.com/wiki/DB_Session/
+
+ http://dready.jexiste.fr/dotclear/index.php?2006/09/13/19-reworked-session-handler-for-code-igniter
+
+ 修改 CI 的 Session 类(见第 6 章),它把 Session 数据存储在 Cookie 里。(当然 Cookie 可加密。)这个类只存储 Session 标识符:你在数据库中添加一个额外的表,它将在那里查找所有的 Session 信息。
+
+ 在 CI 1.5 下运行。
+
+作者 dready
+
+
+
+
+
+External Sites
+
+
+
+There are some 'power users' of CI who contribute code of their own. One good example is Glossopteris, a site run by a US web design company. This makes available some of their own libraries, for instance (at http://www.glossopteris.com/journal/post/table-relationships-in-ci) another CRUD library, which they claim "will allow for complex table inter-relationships to be assigned and simple CRUD actions to be completed." This follows the Rails precedent quite closely: you can define relationships between tables such as 'has one' and 'has many' links. The code is available, but could do with more comments or a user guide.
+
+Another development is CI_Forge (http://www.ciforge.com/), which is intended
+
+as, "A place for projects designed to enhance or extend the lightweight PHP
+
+framework CodeIgniter." It provides Subversion and Trac hosting, a wiki, a
+
+bugissue tracker, and change log support. This is a new application, but (as at July
+
+2007) already hosts 20 projects.
+
+
+
+
+
+外部站点
+
+
+
+ 有一些 CI 的“强大用户”,他们贡献他们自己的代码。一个很好的例子是 Glossopteris,它是一个美国网页设计公司经营的网站。他们提供了一些自己的类库,例如(地址是 http://www.glossopteris.com/journal/post/table-relationships-in-ci)另外一个 CRUD 类库,他们声称“将允许指定复杂的表内关系,并完成简单的 CRUD 操作。”它遵循 Rails 的惯例:你可以在表之间定义象“一对一”和“一对多”的关系。虽然代码是现成的,但还应该多写一些注释,或者制作一个用户指南。
+
+ 另外一个网站是 CI_Forge(http://www.ciforge.com/),它是“一个用于项目设计,为了增强和扩展轻量级 PHP 框架 CodeIgniter 的地方。”它提供 Subversion 和 Trac 主机、一个 Wiki 和一个问题跟踪程序并且支持变更记录。这虽然是一个新的应用,但是(在 2007 年 7 月)已经托管了 20 个项目。
+
+
+
+
+
+Comparisons: Which Charting Library to Use?
+
+
+
+That's quite a range of options. Sometimes, there can be almost too much choice.
+
+
+
+To demonstrate this, let's look at three options for doing the same thing, and see how they differ. Making dynamic charts of data is not an easy thing to code on your own. But it does make your site look good.
+
+
+
+Let's look at three add-ons available for CI that do just this, and try to compare their strengths and weaknesses, as well as look at the results they produce.
+
+
+
+对比:使用哪个图表类库?
+
+
+
+ 图表类库可选择的范围很大。有时会有非常多的选择。
+
+ 为了演示它,让我们看一下做同一件事情的三种选择,并看看他们有什么不同。自己制作动态数据图表不是一件简单的事情,但它确实使你的网站变漂亮了。
+
+ 让我们来看 3 个 CI 的插件,然后像看他们生成的结果一样尝试去比较他们的强项和弱点。
+
+
+
+
+
+Name 3d-pie-chart
+
+URL
+
+http://codeigniter.com/wiki/3d-pie-chart/
+
+Generates a pie chart from two arrays of data (labels and values) and saves it on your site. Looks great, but this is all it does.
+
+
+
+Simple to set up: put the piechart.zip file in your application/libraries folder, and write a controller based on the example. Requires a font, and you need to modify a view to display the results. Works with CI version 1.5
+
+Author Craig
+
+
+
+
+
+名称 3d-pie-chart
+
+URL http://codeigniter.com/wiki/3d-pie-chart/
+
+ 由两个数组(标签和值)生成一个饼图,然后把它保存在你的网站上。看起来很棒,但这是它所能做的一切。
+
+ 简单设置:把 piechart.zip 文件放在你的 application/libraries 文件夹中,然后写一个基于例子的控制器。需要一个字体文件,然后你需要修改一个视图来显示结果。在 CI 1.5 版本下运行。
+
+作者 Craig
+
+
+
+
+
+
+
+Name Panaci
+
+URL http://bleakview.orgfree.com/ or
+
+http://codeigniter.com/wiki/Charting/
+
+Dynamically generates charts and graphs, including bar, line, area, step, and impulse charts
+
+(but not pie charts). The wiki entry states: "Please note, this is NOT a commercial grade
+
+library such as jpgraph or chartdirector, but it is quite adequate for basic plots". The code
+
+example, and specimen plot, below, show what it looks like and how to use it.
+
+
+
+
+
+Works with CI version 1.5. As with 3d-pie-chart, you copy the file into your application/libraries folder, and call it from your controller, supplying basic parameters and an array of data.
+
+Short discussion in CI Forums, no major bugs found at time of writing.
+
+Author Oscar Bajner
+
+
+
+
+
+名称 Panaci
+
+URL http://bleakview.orgfree.com/ 或
+
+ http://codeigniter.com/wiki/Charting/
+
+ 动态生成图表和图形,包括:条,线,区域,阶梯,以及脉冲图(而不是饼图)。在其 Wiki 页面中声明:“请注意,这不是一个像 jpgraph 或 chartdirector 的商业级类库,但是它足够能胜任基本的绘图需要”。下面的代码示例和样本图展示了它的样子,以及怎样去使用它。
+
+ 在 CI 1.5 版本下运行。和 3d-pie-chart 一样,你复制文件到你的 application/libraries 文件夹中,然后从控制器中调用它(提供基本的参数和一个数组数据)。
+
+ 在 CI 论坛上的讨论很简短,所以,在编写本书时没有发现大的错误。
+
+作者 Oscar Bajner
+
+
+
+
+
+
+
+
+
+Name JP Graph
+
+URL
+
+http://codeigniter.com/wiki/JP_Graph/
+
+This is not strictly a plug-in: it's code that allows you to interface between CI and the
+
+external JP Graph library. You need to download the JP Graph library, create a series of
+
+plug-ins for each graph type you want to use, and then call the plug-ins from a controller as
+
+you need them.
+
+
+
+As these examples form its website, http://www.aditus.nu/jpgraph/features.
+
+php show, JP Graph offers a much wider range of charts, and they look great.
+
+
+
+There are two disadvantages with JP Graph. As the wiki entry says: "Keep in mind that
+
+JpGraph has a very large codebase, so be sure to include only the specific libraries you need
+
+for each chart." Secondly, JP Graph is free for personal use, but not for commercial use.
+
+Author Aditus Consulting
+
+
+
+名称 JP Graph
+
+URL http://codeigniter.com/wiki/JP_Graph/
+
+ 严格的讲,这不是一个插件:它是一段在 CI 和外部 JP Graph 类库之间提供接口的代码。你需要下载 JP Graph 类库,为每个你需要使用的图形类型建立一系列插件,然后当你需要他们时再从控制器中调用。
+
+ 正如其网站上的这些例子图表:http://www.aditus.nu/jpgraph/features.php 所显示的,JP Graph 提供了范围更加广阔的图表,并且他们看起来很不错。
+
+ JP Graph 有两个缺点。正如其 Wiki 页面中所说:“请记住 JpGraph 有一个非常庞大的代码库,所以请确保为每个图表只引入你需要的类库。”其次,JP Graph 对个人使用是免费的,但是商业使用并不免费。
+
+作者 Aditus Consulting
+
+
+
+
+
+
+
+Three options: the first two relatively simple, the second more complex. It depends on what you need (and if you are prepared to pay).
+
+ 关于这三个选择:前两个相对简单,第三个复杂一些。这个取决于你的需求(如果你准备购买的话)。
+
+
+
+CRUD: the Final Frontier
+
+You need to write CRUD pages in almost every application. It seems simple, and logical, to automate the process of creating those pages! They are tantalizingly standard—and yet they have deceptively large numbers of possible variations. It's impossible to write one without starting to impose your own rules and assumptions on the user. Also, there is always a trade-off between covering more and more possible options on the one hand, and simplicity of use on the other. The more exceptions and possibilities you try to cover, the more complex your code becomes and the larger the download is.
+
+
+
+
+
+CRUD:新领域
+
+
+
+ 几乎在所有的应用程序中你都要用到 CRUD。它能简单、逻辑化的自动完成页面的生成。它们是最最基础的,虽然它们存在有千百万种的形式。如果不按你的方式制订规则并让用户来遵守,要写出一个应用程序几乎是不可能的。因此,你需要在考虑尽可能多的可能性方面和在简化使用方面做出权衡。你考虑到的特例情况和可能性越多,代码就会变得越复杂,需要下载的也会越多。
+
+
+
+So, quite a few people have had a go at simplifying the basic CRUD operation.
+
+We tried our hand at developing our own CRUD application in Chapter 13. This was a fairly simple model that cut a lot of corners and only allowed you to use a subset of the available HTML form objects; but it does manage to incorporate CI's validation functions.
+
+ 所以,很多人已经开始尝试使 CRUD 的基本操作更简洁。
+
+ 在第 13 章中,我们已经尝试开发了我们自己的 CRUD 应用程序。这是一个相当简单的模型,它截掉了很多细枝末节,仅仅允许你使用 HTML 表单对象的部分功能;但它确实拥有验证功能。
+
+
+
+We've already mentioned, in this chapter, the Glossopteris library.
+
+Another interesting approach is 'CodeCrafter', which is listed on the CI wiki and available from Datacraft Software Consulting in South Africa, at: http://www.datacraft.co.za/index.php?contents=codecrafter/codecraft. This claims that, "CodeCrafter will help you generate your entire CodeIgniter application in just seconds." It comes with a 26 page online manual, which shows you how to use its interface to generate CI code. This is a different method to most of the other offerings reviewed here: it builds the CI code for you, using a graphical interface, rather than providing libraries or code for you to patch in.
+
+
+
+ 在本章中,我们已经提到了 Glossopteris 类库。
+
+ 另外一个有趣的实现是“CodeCrafter”,它被列在 CI 的 Wiki 上面,同时也发表在南非的 Datacraft 软件咨询网站上(http://www.datacraft.co.za/index.php?contents=codecrafter/codecraft)。该网站声称:“CodeCrafter 将会帮助你在仅仅几秒钟内生成你的全部 CodeIgniter 应用程序。”它有一个 26 页的在线手册,该手册向你展示了如何使用它的接口去生成 CI 代码。与大多数其他方法不同的评论如下:它使用图形化的界面为你构建 CI 代码,而不是提供供你调用的类库或代码。
+
+
+
+SuperModel (see http://codeigniter.com/wiki/SuperModel/) claims: "The SuperModel Library is an extension to models to automate most of the mundane form-generation and validation tasks. Think of it as scaffolding on steroids."
+
+ SuperModel (详见 http://codeigniter.com/wiki/SuperModel/) 就是:“SuperModel 类库是模型的一个扩展,使一般的表单生成和验证工作自动化。想一下脚手架(scaffolding)吧。”
+
+
+
+The author's comments explain the frustrations of writing this sort of code—and also the risk for users. He says: "Please note this library is a work-in-progress. I am currently making many changes, including API changes that will break applications. As I write this (May 30/2006) I am working on implementing one<>many and many<>many joins……It's impossible to write something like this, but stay as flexible like CodeIgniter is. Unfortunately, this library forces you into doing some things a certain way. I've tried to be as flexible as possible, but at the same time, there has to be a line drawn between being flexible, and being completely bloated. That's why this is an external 3rd party library—you're free to implement models the way you want, or use some other similar 3rd party library that does something similar."
+
+ 在 SuperModel 作者的一篇评论中阐述了编写这类代码所面临的困难以及用户所承担的风险。他说:“请注意这个类库是一个正在开发的产品。我目前正在做很多修改,包括对 API 的修改,这些修改将不兼容于旧的程序。在我写此文(2006年5月30日)的同时,我正在努力实现一对多和多对多的关联查询。不幸的是,这个类库迫使你以某种方式来工作。我试图让它尽可能的小巧灵活,但是同时,在小巧灵活和复杂臃肿之间必须找到一个平衡点,以上就是其作为一个外部第三方类库出现的原因—你可以自由的编写你需要的模型,或者使用其他类似功能的第三方类库。”
+
+
+
+Resources for Other Programmes, e.g. Xampplite, MySQL, PHP
+
+There are a lot of useful resources for PHP. Let's just touch on some of them briefly.
+
+
+
+
+
+其它编程资源,例如 Xampplite、MySQL 和 PHP
+
+
+
+ 有许多对 PHP 有用的资源。来让我们略微涉及一下他们。
+
+
+
+
+
+1.PHP itself can be downloaded free from www.php.net, which also includes a full manual.
+
+2.A low-cost PHP editor can be bought from MP Software at http://www.mpsoftware.dk/.
+
+
+
+There are many good books on PHP, including PHP Programming with PEAR, by Carsten Lucke, Aaron Wormus, Stoyan Stefanov and Stephan Schmidt, published by Packt.
+
+
+
+1.PHP 可以从 www.php.net 上免费下载,也包括了完整的手册。
+
+2.廉价的 PHP 编辑器可以从 MP Software http://www.mpsoftware.dk/ 那里购买。
+
+ 有许多关于 PHP 的好书,包括《PHP Programming with PEAR》,作者是 Carsten Lucke、Aaron Wormus、Stoyan Stefanov 和 Stephan Schmidt,出版社是 Packt。
+
+
+
+To run a local web server on your own machine, try looking at http://www.apachefriends.org/en/index.html—a site that offers free downloads of the XAMPP package. This installs an Apache web server with MySQL, PHP, and Perl. If the XAMPP package is too comprehensive for you, try Minixampp from the same site, on which the code used in this book was written.
+
+
+
+ 在你自己的机器上运行本地 Web 服务器,尝试看一下 http://www.apachefriends.org/en/index.html —一个免费提供 XAMPP 包下载的站点。它将安装一个 Apache Web 服务器、MySQL、PHP 和 Perl。如果 XAMPP 包对你来说太全面,请尝试这个站点上的 Minixampp,本书的代码就是在这个环境下编写的。
+
+
+
+MySQL too has its own web page—http://www.mysql.com/—though if you want to download the latest versions for free, go to http://dev.mysql.com/. (Bear in mind though that many ISPs don't use the latest versions. Although MySQL is up to version 5, most ISP's are still using Version 4. This prevents you using some of the more interesting new features, like stored procedures.) See, Creating your MySQL Database: Practical Design Tips and Techniques, by Marc Delisle, published by Packt.
+
+
+
+ MySQL 同样拥有自己的网页— http://www.mysql.com/ —不过,如果你想免费下载最新版本,请到 http://dev.mysql.com/ 。(请记住虽然很多 ISP 都没有使用最新的版本。虽然 MySQL 的最新版本是 5,但是大多数 ISP 仍旧使用版本 4。这阻止了你使用某些更有趣的新特性,例如存储过程。)更多内容请参考《Creating your MySQL Database: Practical Design Tips and Techniques》, 作者是 Marc Delisle,出版社是 Packt。
+
+
+
+
+
+Although MySQL comes with its own tools, the most popular (and most common) tool is PHPMyAdmin. (See Mastering phpMyAdmin 2.8 for Effective MySQL Management, also by Marc Delisle, published by Packt.)
+
+
+
+ 虽然 MySQL 有它自己的工具,但是最流行(也是最常见的)的工具是 PHPMyAdmin。(更多内容请参考《Mastering phpMyAdmin 2.8 for Effective MySQL Management》,作者也是 Marc Delisle,出版社是 Packt。)
+
+
+
+Summary
+
+In this Chapter, we've looked at some of the resources available to you when you start to code with CI. There's a lot of ready-made code available. You have to look before you use: don't just take the first plug-in or library that seems to do what you want and start using it. You need to study each offering to see what it really does, and it also helps to go through the code and make sure you understand it. However, if you are prepared to do this, you can find libraries at different levels of scope and complexity that will take on many of the tasks that would otherwise have involved a lot of hand coding.
+
+
+
+总结
+
+
+
+ 在本章中,我们为你展示了一些当你开始用 CI 编程时要用到的资源。有很多现成的代码可以使用,你需要在使用之前好好的看看它们:不要一看到满足你需要的插件或类库就直接开始使用。你需要好好的研究这些代码,看看他们是如何工作的,这对你整体把握代码很有帮助,并且有利于你更好的理解它。不管怎样,只要你决定使用 CI 框架,你就能找到各种不同层次和复杂程度的类库,它们将完成很多要由你手工完成的任务。
+
+
+
+In particular, we looked at libraries for
+
+
+
+1.AJAX and JavaScript
+
+2.Authentication
+
+3.Charting
+
+4.CRUD
+
+
+
+ 我们详细介绍了这些类库:
+
+1.AJAX 和 JavaScript
+
+2.身份验证
+
+3.图表
+
+4.CRUD
+
+
+
+Lastly, we looked at some of the resources available for PHP and MySQL and for running a local web server.
+
+ 最后,我们介绍了一些关于 PHP、MySQL 和运行一个本地 Web 服务器所需要的资源。
+
diff --git a/_docs/2.doc b/_docs/2.doc
new file mode 100644
index 0000000..c7a4f97
Binary files /dev/null and b/_docs/2.doc differ
diff --git a/_docs/2.txt b/_docs/2.txt
new file mode 100644
index 0000000..0155062
--- /dev/null
+++ b/_docs/2.txt
@@ -0,0 +1,125 @@
+第二章 2分钟:建立一个CodeIgniter网站
+
+
+用 CI 建一个网站很容易。这一章很短,解释了用 CI 制作网站时发生了些什么,哪些文件被创建,让我们来瞧一瞧:
+
+ 创建网站需要什么软件?
+
+ 安装 CI 文件:一个简单的下载和解压缩操作。
+
+ CI 的基本设置:有哪些文件夹及它们是如何组织的。
+
+ CI 安装时默认的控制器和视图。
+
+ 一些简单的修改来演示 CI 如何运作。
+
+准备知识
+
+CodeIgniter 有较好的版本兼容性。它工作在 PHP 4.3.2 及以上版本,或 PHP 5。由于大多数 ISP 还不支持 PHP 5,支持 PHP 4 版本是有用的。
+
+你还需要一个数据库。CI 的在线手册说:“已支持的数据库是 MySQL、MySQLi、MS SQL、Postgre、Oracle、SQLite 和 ODBC。”
+
+为了要开发并测试一个动态的网站,你需要一个 Web 服务器。通常,你会在本地服务器上开发并测试你的网站,也就是,这些软件运行在你自己的机器上(127.0.0.1 或 localhost), 一般来讲,开发环境不会建立在远程服务器上。
+
+如果你不熟悉如何分别建立本地开发环境,可以选择一个套装软件,像是 Xampplite,安装 Apache、PHP 和 MySQL 几乎不需要修改配置文件。Xampplite 是免费的,有简单易懂的安装指南。或者,某些版本的 Windows 附带自己的 Web 服务器。
+
+你还需要一个称心的 PHP 编辑器。所有的编码工作都可以在文本编辑器中完成。提供语法加亮特性和自动补齐功能的编辑器会更理想一些,因为它可以帮助一般水平的程序员节约时间。
+
+一旦你做好了这些准备工作,我担保你在2分钟内就可以搞定 CI 安装工作
+
+安装 CodeIgniter
+
+再次声明,CI 是完全免费的!
+
+建立好开发环境后,访问 CodeIgniter 网站:http://www.codeigniter.com/ 并下载最新版的框架。1.5.3 版是最新版(译注:编写此书时的最新版是 1.5.3),是一个只有 737KB 的压缩文件,几秒种就可以下载完成。
+
+解压缩这个文件,把它释放到网站根目录中。如果你正在使用 Xampplite,通常在 Xampplite 文件夹里面的 htdocs 文件夹中。
+
+CodeIgniter 的 index.php 文件应该在根目录中。这时,如果你在浏览器上打开 http://127.0.0.1 你也就实际打开了此文件。我们用 1-2 分钟的时间来建立一个可运行的网站!
+
+和 CI 包含在一起是一个简单易懂的用户手册。(在 user_guide 文件夹中)你将会经常用到它。它的内容很详细,细过这篇文章,所以,需要时,请经常查阅它。
+
+当这些文件保存在你的机器上的时候,有两个方法来访问它们:
+
+ 通过 URL,http://127.0.0.1
+
+ 经过正常的目录路径:举例来说,C:/xampplite/htdocs/index.php
+
+你应该通过浏览器访问 CI 的默认首页。真是简单!默认首页传递给你一个信息:你将看到的内容由两个文件组成:视图和控制器。
+
+分析文件结构
+
+安装 CI 文件后,我们来看一下目录结构。
+
+你的根文件夹现在应该看起来有点像下面的图表。如果你曾经看过 Rails,这个结构将会看起来非常熟悉。
+
+你能把这些文件夹分为三个小组:
+
+ application 是你自己的项目存放文件的目录(举例来说,控制器、模型和视图:全部在 application 文件夹中)。除了你刚才见到的默认视图和控制器,这些文件夹都是空的。
+
+ 在 system 文件夹中的文件是 CI 本身的代码。(system/libraries、system/codeigniter、system/drivers 等)。如果你愿意,你可以研读它们,或者改变他们—不过要等到你了解了 CI 是如何工作的,才能这么做。而且如果你改变了框架内的代码,记住,当你下载了 CodeIgniter 新版本时,备份它们,否则,新的版本会覆盖它们。当然,你也可能不需要自己修改代码而直接使用 CI 本身的代码,Rick 写的代码应该是很不错的。
+
+ 还有一些文件夹中已包含了文件,但是,可能需要增加或修改(如:language、config、errors)。这些文件夹被设置为默认的,但你可以修改它们。
+
+
+
+配置文件
+
+还记得我们要花 2 分钟建立我们的网站吗?第 2 分钟要用来做一些基本的设置。
+
+config 文件夹包含了一些为你的网站设定基本配置的文件。打开 config/config.php 文件告诉网站应该在哪里找到它自己的结构和配置信息。文件的第一行一般是这样的:
+
+代码如下:
+
+ /*
+
+|------------------------------------------------
+
+| Base Site URL
+
+|------------------------------------------------
+
+|
+
+| URL to your Code Igniter root. Typically this
+
+| will be your base URL, WITH a trailing slash:
+
+|
+
+| http://www.your-site.com/
+
+|
+
+*/
+
+$config['base_url'] = " http://127.0.0.1/";
+
+/*
+
+注意 CI 的注释多详尽啊!
+
+修改引号中的数据以匹配你网站的根目录。如有疑问,请查询在线手册以得到详细指导。
+
+作为一项基本的原则,使用 config.php 文件储存关于你网站的信息好过散布在你项目的不同文件中。这样做有几个好处:首先,更新比较容易;其次,当你把项目从开发服务器转移到实际存放的服务器时,修改配置较容易;最后,许多 CI 函数会首先在配置文件中寻找需要的信息。
+
+还有其他的 config 文件存放在 config 文件夹中,但是目前你可以放心地使用它们而不用修改它们的默认值。
+
+这就是2分钟内运行 CI 所需要做的第二件事儿。在这一章的余下部分,我们将会上我们刚做好的网站去逛逛。
+
+它能工作吗?
+
+验证网站能否正常工作的一个简单方法就是打开你的浏览器。假定你正在本地服务器的根文件夹中运行它,在地址栏输入:http://127.0.0.1,你能看到网站的默认页面:
+
+
+
+看到默认页面意味着你的网站正常运行了。不需要 2 分钟,对吗?
+
+总结
+
+在本章节中,我们已经见到,安装 CI 是多么容易。一旦建立好你的开发环境,你所需要做的是下载 CI 框架文件、解压并复制到一个目录而已。
+
+随后,我们快速浏览了 CI 的目录结构。
+
+这一章节非常短,因为 CI 容易安装,不需要太长的篇幅。其实其它章节也不长,因为 CI 的确很简单易懂,节约时间。
+
diff --git a/_docs/3.doc b/_docs/3.doc
new file mode 100644
index 0000000..4be4c12
Binary files /dev/null and b/_docs/3.doc differ
diff --git a/_docs/3.txt b/_docs/3.txt
new file mode 100644
index 0000000..1dca7a0
--- /dev/null
+++ b/_docs/3.txt
@@ -0,0 +1,943 @@
+第三章 分析网站结构
+
+
+既然我们已经安装了CI,那我们就开始了解它是如何工作的吧。
+
+读者已经知道CI实现了模型—视图—控制器(MVC)模式。这是管理文件和网站的方法,如果你喜欢的话你可以把他们合理的分成各个小模块,而不是把代码放在一起。
+
+这一章,我们将会对MVC理论做个简短的介绍,然后再介绍CI的MVC实现方式。特别地,要了解那些文件夹是如何相互交换信息的?网站结构是怎样的?以及CI是如何运作的?
+
+这一章将会介绍:
+
+ MVC如何架构一个动态网站
+
+ CI如何分析一个Internet请求,以及如何调配指定的代码来处理它
+
+ 这些指定的代码如何编制
+
+ CodeIgniter语法规则
+
+ 在CI中,你可以找到或自己编写各种文件和类
+
+ 如何使用URL传递参数给控制器
+
+ 如何编写更好的视图并把动态内容传递给它们
+
+ 如何返回信息给上网者
+
+ 文件和类如何传递信息和相互调用
+
+ 辅助函数和类库文件有什么用
+
+ 有助于网站设计的一些特别提示
+
+MVC—到底有什么用?
+
+MVC指的是一个动态网站的组织方法。该设计模式是1979年由挪威人Trygve Reenskaug首次提出来的,这里是一些概要:
+
+ 模型是包含数据的对象,他们与数据库交互,对这些数据进行存取,使其在不同的阶段包含不同的值,不同的值代表了不同的状态,具有特定的含意。
+
+ 视图显示模型的状态,他们负责显示数据给使用者。(虽然他们通常是HMTL视图,但是,他们可能是任何形式的接口。比如PDA屏幕或WAP手机屏幕)
+
+ 控制器用来改变模型的状态,他们操作模型,提供动态的数据给视图。
+
+CI中模型、视图和控制器文件都有自己的文件夹。文件本身是.php文件,通常以遵循某种命名规则的类的形式呈现。
+
+CI帮助你遵循MVC模式,使你更有效地组织代码。CI允许你有最大的灵活性,你可以获得MVC结构的所有好处。
+
+当你编程的时候,试着始终用MVC来思考问题。尽可能确保你的“视图”聚焦于显示内容,“控制器”纯粹地用来控制应用程序流。把应用程序逻辑保留在数据模型和数据库中。
+
+这样,如果你决定开发新的视图,你不必在任何一个控制器或模型中修改代码。如果你要更改“商业逻辑”,那么你只需要在模型中修改代码。
+
+另一方面,你必须认识到,MVC只是用来帮助你的一种设计方式,而不是用来约束你的。MVC可以有不同的实现方式。CI的论坛中包含许多如何“正确合理”的实现MVC的方式。(我应该在控制器部分实现数据库查询功能吗?我能直接从视图发送数据到模型吗?或者我必须通过控制器来访问?)
+
+与其寻找理论上的正确方式,不如遵循两项有用的原则。这些在CI用户手册的设计和架构目标一节中有相关描述:
+
+ 松耦合:耦合是指一个系统的组件之间的相关程度。越少的组件相互依赖,那么这个系统的重用性和灵活性就越好。我们的目标是一个非常松耦合的系统。
+
+ 组件专一性:专一是指组件有一个非常小的专注目标。在CodeIgniter里,为了达到最大的用途,每个类和它的功能都是高度自治的。
+
+这些是Rick Ellis开发CI要实现的目标,并且它们对于你的网站也是很好的目标。实现这些目标之后,你在代码中使用这些类时就不需要担心有什么副作用了。
+
+CI做到了这一点,我的经验是一个站点中的“松耦合”辅助函数和类库可以很容易的应用到其他站点中,这节省了很多开发时间。
+
+因此,如果你的控制器直接操作数据库,或你在模型中调用视图,CI代码将会以适当的方式工作—通常没有技术上的问题—但是从MVC理论来看这样似乎是“不正确的”。别担心,如果喜欢就这样做吧!
+
+CI的网站结构:控制器和视图
+
+你的整个CI网站是动态的。就是,可能找不到制作“静态”网页的简单HTML代码。(如果需要你可以添加,但是他们将会在CI框架之外。)那么,你的网站到底在哪里?
+
+当我们安装CI的时候,我们注意到application文件夹中包括名为models、views和controllers的子文件夹。每个CI网站都包含这三个类型的文件。
+
+让我们看看内部细节。
+
+再次强调我们并不处理静态网页和对应的URL,我们将会给你看CI如何分析“URL”请求和如何响应它。首先,考虑一个正常的Internet请求。用户建立一个连接到你的网站: www.example.com,然后经过socket发出一个如下的HTTP请求:
+
+GET /folder/file.html HTTP/1.0
+
+GET是请求的类型,HTTP/1.0指定HTTP协议的版本,中间是相对路径和文件名。但是在你的网站上,找不到简单的静态HTML文件。取代它的是,所有的连入请求被index.php文件拦截并进行处理。
+
+如果用户正在以正确的URL请求你网站上的页面—通常是单击网页上的一个超链接—请求一般看起来像这样:
+
+GET /index.php/tests/showall HTTP/1.0
+
+如果使用者不知道具体的URL,CI会设定一个默认页面(我们一会儿就告诉你怎么做。)。CI的处理步骤是:
+
+
+
+一个从Internet到你的网站根文件夹的请求被index.php文件拦截,作用就像一个“路由器”。换句话说,它调用一个“控制器”,然后返回一个“视图”。
+
+“路由器”怎么知道调用哪个控制器?就像我们已经见到的,有时候请求本身包含的信息会告诉它调用哪个控制器。举例来说,如果请求说:
+
+GET http://127.0.0.1/index.php/welcome/index
+
+并且如果你有一个控制器叫做welcome,这就是被调用的控制器。
+
+Welcome控制器
+
+所以,让我们看welcome控制器。它被存放在如下路径:
+
+system/application/controllers/welcome.php
+
+它的内容是这样的:
+
+代码如下:
+
+ load->view('welcome_message');
+
+ }
+
+ }
+
+ ?>
+
+文件的第二行开始是一个类。每个控制器从一个Controller类继承。在类中包含了两个函数或方法—welcome()和index()。
+
+注:CI要求控制器名称首字母大写(class Welcome),但文件名是小写字母:/system/application/controllers/welcome.php。
+
+接下来的三行代码组成构造函数。注意到CI使用PHP 4构造函数命名规则,兼容于PHP 5—CI在PHP 4和PHP 5中都能很好的工作。构造函数在类实例化时被调用,你可以做一些初始化的工作,比如调用类库或模型,或者定义类的成员变量。
+
+这个例子的构造函数中只有一行代码,它调用父类的构造函数:parent::Controller()。这只是显式的使用父类功能的一种方法。如果你想要详细地了解CI中的Controller类,你可以查看文件 /system/libraries/controller.php。
+
+(可以放心的是你可以随时查看CI的源代码,它们已经保存在你机器上的某个文件夹内。)
+
+让视图开始工作
+
+让我们回到连入请求那部分。路由器不仅要知道应该由哪个控制器来处理请求,而且也要知道是哪个控制器里面的哪个函数。这就是为什么请求是特定的GET http://127.0.0.1/welcome/index的原因。所以,路由器在welcome控制器中查找一个名为index的函数。你要确保存在index函数!
+
+来看看index()函数。这个函数只是用CI的装载函数(this->load->view)载入的一个视图(“welcome_view”)。在现阶段,它不对视图做任何操作,只是传递动态信息。稍后才会执行。
+
+“welcome_view”在CI中有特定的含义,实际上它指向了如下文件:system/application/views/welcome_view.php。这个视图文件中只是一些简单的HTML代码,但是,因为在稍后运行时,CI会向文件里放入PHP编码,因此使用了PHP文件后缀。(如果只是使用简单的静态HTML则不需要修改后缀。)
+
+下面是视图文件中的HTML代码(作了精简处理):
+
+代码如下:
+
+
+
+
+
+ Welcome to Code Igniter
+
+
+
+
+
+
+
+
Welcome to Code Igniter!
+
+
The page you are looking at is being generated dynamically by
+
+ Code Igniter.
+
+
If you would like to edit this page you'll find it located at:
+
+
+
+
+
+它基本上仍然是HTML,但是注意已高亮的PHP代码。
+
+你将会注意到有一个PHP代码片断存放在样式表中。让我们在站点根文件夹下保存一个简单的样式表为mystyles.css。它很简单:
+
+代码如下:
+
+ h1 {
+
+ margin: 5px;
+
+ padding-left: 10px;
+
+ padding-right: 10px;
+
+ background: #ffffff;
+
+ color: blue;
+
+ width: 100%;
+
+ font-size: 36px;
+
+ }
+
+ .test{
+
+ margin: 5px;
+
+ padding-left: 10px;
+
+ padding-right: 10px;
+
+ background: #ffffff;
+
+ color: red;
+
+ width: 100%;
+
+ font-size: 36px;
+
+ }
+
+这给了我们两种可选风格,而且你将会在视图中用到它们。
+
+首先,让我们在config文件加入:
+
+代码如下:
+
+ $config['css']=" mystyles.css";
+
+这只是告诉网站我们刚才编写的CSS文件的文件名和位置。
+
+但是注意样式表的实际位置是$base/$css—从哪里获取变量$base和$css的内容呢?你还可以联想到,变量$mytitle和$mytext的内容呢?答案是我们需要一个新的控制器!
+
+设计一个更好的控制器
+
+现在,我们需要一个新的控制器。我们将其命名为Start并保存在:
+
+/system/application/controllers/start.php
+
+该控制器必须做几件事:
+
+ 调用视图
+
+ 将基本URL信息和刚编写的css文件的名称传递给视图
+
+ 把另一些数据传递给视图:它正在期待标题($mytitle)和一些本文($mytext)
+
+ 最后,我们想要控制器接受来自使用者的一个参数(例如通过URL请求)
+
+换句话说,我们必须传递变量到视图中。因此让我们从Start控制器开始。这是一个OO类:
+
+代码如下:
+
+ config->item('name_of_config_variable');
+
+当作在:
+
+代码如下:
+
+ function Start()
+
+{
+
+ parent::Controller();
+
+ $this->base = $this->config->item('base_url');
+
+ $this->css = $this->config->item('css');
+
+}
+
+CI会从config文件中读取相关的设置内容。
+
+使用这一机制,不管我们编写多少个控制器和函数,如果我们的网站访问量很大,我们需要搬迁服务器的话,那么这些参数也只需修改一次。
+
+把参数传给一个函数
+
+现在,在Start控制器类里面,让我们定义将会实际工作的函数。
+
+代码如下:
+
+ function hello($name)
+
+{
+
+ $data['css'] = $this->css;
+
+ $data['base'] = $this->base;
+
+ $data['mytitle'] = 'Welcome to this site';
+
+ $data['mytext'] = "Hello, $name, now we're getting dynamic!";
+
+ $this->load->view('testview', $data);
+
+}
+
+这个函数期待一个参数,$name,(但你可以设置一个默认值—myfunction($myvariable=0)),通常会把一个字符串赋给$mytext变量。好吧,我们现在要问一个问题,$name变量来自哪里?
+
+在这个例子中,它需要来自URL请求的第三个参数。因此,它由HTTP请求提供:
+
+GET /index.php/start/hello/fred HTTP/1.0
+
+换句话说,当你输入URL:
+
+http://www.mysite.com/index.php/start/hello/fred
+
+注意:这个例子中的代码“干净地”传递了变量fred,没有用任何方式检查它。你需要在编程时对它进行检查。我们将会在第七章学习到如何检查输入。通常,参数必须在检查后确保没有问题再传递给控制器。如果不检查的话,一个不怀好意的用户可以很容易地侵入系统,他可以发送这样一个URL如:http://www.mysite.com/index.php/start/hello/my_malicious_variable。所以,你应该检查接收的变量,确保它们符合要求再进行处理。
+
+URL的最后一段作为一个参数传给函数。事实上,你能增加更多的参数,但不能超过你所使用的浏览器的设置。
+
+让我们总结一下CI处理URL的具体细节:
+
+URL段 用途
+
+http://www.mysite.com 定位你网站的基本URL
+
+/index.php 定位CI路由器并读取URL的其它部分,分析后定们到相关页面。
+
+/start CI要调用的控制器的名称(如果没有设置控制器名称,CI将调用你在config文件中设置的默认控制器)
+
+/hello CI要调用的函数名称,位于所调用的控制器内。(如果不存在该函数,默认调用index函数,除非你使用_remap)
+
+/fred CI把这个作为传递给函数的变量
+
+如果还有其他URL段,例如/bert CI把这个作为传递给函数的第二个变量
+
+更多变量 CI把更多的参数作为变量传递给函数
+
+传递数据到视图
+
+让我们回去再看一下hello函数:
+
+代码如下:
+
+ function hello($name)
+
+ {
+
+ $data['css'] = $this->css;
+
+ $data['base'] = $this->base;
+
+ $data['mytitle'] = 'Welcome to this site';
+
+ $data['mytext'] = "Hello, $name, now we're getting dynamic!";
+
+ $this->load->view('testview', $data);
+
+ }
+
+注意hello()函数如何先设置一个名为$data的数组,并把一些对象的属性及文本读入数组。
+
+然后它通过名称装载视图,并把刚生成的$data数组作为第二个参数。
+
+在幕后,CI很好地利用了另外一个PHP函数:extract(),这个函数的作用是把数组中的元素取出放入变量表,其中每个键值对中的键名即为变量名,对应该键名的值为变量的值。因此我们刚才定义的$data数组在视图中转换成一个单一的变量:$text(等于“Hello, $name, now we're getting dynamic”),$css(等于来自config文件的设置值),等等。
+
+换句话说,当它被建立的时候,$data数组看起来像这样:
+
+代码如下:
+
+ Array
+
+ (
+
+ [css] => mystyles.css
+
+ [base] => http://127.0.0.1/packt
+
+ [mytitle] => Welcome to this site
+
+ [mytext] => Hello, fred, now we're getting dynamic!
+
+ )
+
+但是当它被传递给视图的过程中,它被解开,并且下列变量在视图对象中生成,与$data的每个键/值相对应:
+
+代码如下:
+
+ $css = 'mystyles.css';
+
+ $base = 'http://127.0.0.1/packt';
+
+ $mytitle = 'Welcome to this site';
+
+ $mytext = "Hello, fred, now we're getting dynamic!";
+
+ )
+
+虽然你只传送一个变量到视图中,但是,你能把许多数据装进那个变量中。$data数组的每个值还可以是数组,这被称为多维数组,因此,用一个数组变量可以把大量的变量传递给视图,这是一个很好的编程技巧。
+
+现在访问http://127.0.0.1/packt/index.php/index/start/fred(注意:这个URL跟以前的不同—它要求在index控制器中寻找start函数,并把参数“fred”传递给该函数),你可以看到如何使用MVC架构编写动态网站。(当然,到目前为止至少是VC,因为我们还没有介绍M。)
+
+你将看到下列内容:
+
+
+
+你可以看到参数fred是URL的最后一部分,并被传递给函数,再经由函数传给了视图。
+
+请记住你的视图一定要与你的控制器相对应。如果视图中并没为一个变量安排显示的位置,它将不被显示。如果视图正在期待一个变量,而这个变量并没有在控制器中声明并赋值,你可能收到一个错误信息。(当然,如果变量被声明,则不会有错误信息,但它不会正常显示。)
+
+CI中的类彼此之间如何操控
+
+当你编写你的控制器、模型等内容时,你将会需要在他们之间进行互操作并传递数据。 让我们看看如何实现这些功能。
+
+调用视图
+
+我们已经见到控制器如何调用视图并传递数据给它:
+
+首先它创建数组($data)传给视图;然后它装载并调用视图:
+
+代码如下:
+
+ $this->load->view('testview', $data);
+
+直接调用函数
+
+如果你想要使用来自类库、模型、插件或辅助函数的代码,你必须首先装载他们,然后按上面表格里的方法调用它们。因此,如果“display”是一个模型,并且我想使用它的mainpage函数,我的控制器可能这样调用:
+
+代码如下:
+
+ $this->display->mainpage();
+
+如果函数需要参数,我们可以通过如下方式传递:
+
+代码如下:
+
+ $this->display->mainpage('parameter1', $parameter2);
+
+与控制器互动
+
+你可以在任何控制器内调用类库、模型、插件或辅助函数,并且模型和类库也能彼此调用,同插件和辅助函数一样。
+
+然而,你不能从一个控制器调用另外一个控制器,或从一个模型或类库调用一个控制器。 只有两个方法可以让一个模型或者类库与一个控制器关联:
+
+第一,它可以返回数据。如果控制器作如下赋值:
+
+代码如下:
+
+ $fred = $this->mymodel->myfunction();
+
+函数中返回一个值,那么控制器将得到赋给$fred的值。
+
+第二,你的模型或类库可以创建(并传递给视图)一个URL。控制器调用相关视图与使用者产生交互。
+
+你不能把超链接直接传递给一个模型或类库。用户总是与控制器交互,从不直接面对其它对象—但是,你能在控制器里写一个调用函数。换句话说,你的视图可能包含一个超链接指向一个控制器函数:
+
+代码如下:
+
+ echo anchor('start/callmodel', 'Do something with a model');
+
+
+
+通过callmodel函数,调用一个模型中的函数:
+
+代码如下:
+
+ function callmodel()
+
+ {
+
+ $this->load->model(mymodel);
+
+ $this->mymodel->myfunction();
+
+ }
+
+这就像一个鸡蛋杯
+
+
+
+这个图显示了组件间信息流的走向。
+
+实线表示直接函数调用,如:
+
+代码如下:
+
+ $this->mymodel->myfunction();
+
+这些信息流可以从控制器到视图,也可以从控制器到类库或模型。(模型也能调用视图,但理论上这样做不合适。)相反方向就不能调用,如:视图不能调用控制器。然而,类库和模型能互相调用。
+
+虚线表示通过返回值传递信息。模型和类库可以在内部互相传递数据,也可以把值返回给控制器。视图不能返回任何值。
+
+虚线表示通过用户传递信息或控制—换句话说,视图会在屏幕上显示一些内容,并可能让用户去单击一个超链接(调用一个控制器)。
+
+这个图很像一个“鸡蛋杯”纯属是一种巧合。它只是看起来像而已。
+
+一个CI辅助函数的例子:URL辅助函数
+
+下面介绍一个如何使用辅助函数的例子,使用辅助函数能使你的代码更简洁,更有针对性。CI的URL辅助函数包含一组帮你操作URL的函数。你可以像这样装载它:
+
+代码如下:
+
+ this->load->helper('url');
+
+然后,你可以用它查询并返回设置在config.php文件中的site和/或base URL:
+
+代码如下:
+
+ echo site_url();
+
+echo base_url();
+
+
+
+你也可以用它创建超链接。我们在上一节看到过在start控制器中访问hello函数,并把参数fred传递给该函数,这些都是通过一个URL实现的:
+
+http://www.mysite.com/index.php/start/hello/fred
+
+如果你想要你自己的代码创建一个这样的URL,你可以使用URL辅助函数实现。语法是:
+
+代码如下:
+
+ echo anchor('start/hello/fred', 'Say hello to Fred');
+
+
+
+这将创建一个相同URL的超链接,并显示:“Say hello to Fred"。换句话说,它等同于:
+
+代码如下:
+
+ say hello to Fred
+
+请记住,使用CI辅助函数有两个好处。第一,输入字符较少。(49个字符对82个字符,均包括空格。如果包括装载URL辅助函数的语句—另外的27个字符,每个控制器只需装载一次—那么,它还是76个字符而非82个字符。)
+
+第二,URL辅助函数自动在config文件中查找网站URL(和index文件名)。这意味着如果你改变你的网站URL,你只需要改变config文件一次,你不需要查遍代码修改超链接。
+
+URL辅助函数还有其他有用的功能。比如:它能创建“mailto”超链接。
+
+代码如下:
+
+ echo mailto('me@example.com', 'Click Here to Email Me');
+
+
+
+和下面的HTML代码等效:
+
+代码如下:
+
+ click here to email me
+
+
+
+如果你担心机器人在你的网站上搜集Email,并用它们发送垃圾邮件的话,那么用safe_mailto代替mailto。屏幕显示的内容相同,并且工作也正常。
+
+但是如果检查你的HTML代码,现在变成了复杂的JavaScript代码,机器人也许什么也抓不到(或者无法很容易的抓到)。
+
+代码如下:
+
+
+
+你,和你的用户,不需要看到这些代码。它只是用来迷惑机器人的,以确保你的Email地址的安全。而你只需要增加四个字母和一个下划线:你用safe_mailto代替mailto,CI为你做其它所有的一切。
+
+在URL辅助函数中有许多有用的函数。请查阅用户指南。
+
+总结一下URL辅助函数,它符合我们在这一章开始时讨论的话题:
+
+ 高度的“组件专一性”。URL辅助函数只做它要做的事情,简化对应的编码工作。
+
+ 松耦合—URL辅助函数有一个简单的接口,不依赖调用它的代码。你可以在任何CI项目中使用它。例如,大多数项目都需要很多的超链接。你可以使用这个辅助函数一遍一遍地创建它们。
+
+如果你查看URL辅助函数代码的话(在system/application/helpers/url_helper.php),那么你将会发现它是过程式的代码—只是一组函数而不是OO类。它不装载任何其他的CI类或辅助函数。(它本身不是对象,不能这样做。)
+
+一个简单的类库例子:创建一个菜单
+
+现在让我们看一些在CI框架中实际使用的代码。
+
+举例来说,这里是一个创建三个菜单项的类库文件:
+
+代码如下:
+
+ 1 load->helper('url');
+
+7 $menu = anchor("start/hello/fred","Say hello to Fred |");
+
+8 $menu .= anchor("start/hello/bert","Say hello to Bert |");
+
+9 $menu .= anchor("start/another_function","Do something else |");
+
+10 return $menu;
+
+11 }
+
+12 }
+
+(这时,不要为不寻常的语法担心—在第6行中是“$obj->”而不是“$this->”。这在第七章会详细介绍。)
+
+注意:这些代码是OO代码,函数show_menu()包含在一个简单的类“Menu”中。它可以访问其他的CI类和辅助函数:在这个例子中,它使用URL辅助函数,我们刚刚介绍过它。
+
+首先,它装载URL辅助函数,然后创建一个字符串($menu),包含链接到三个控制器和函数的HTML代码。然后它返回$menu字符串。
+
+你可以在一个控制器里这样调用它:
+
+代码如下:
+
+ $mymenu = $this->menu->show_menu();
+
+然后控制器可以使用$menu变量调用视图:
+
+代码如下:
+
+ $data['menu'] = $mymenu;
+
+$this->load->view('myview', $data);
+
+这个类生成一个菜单,对特定网站而言的菜单。正因为这样,我把它保存在/system/application/libraries,而不是/system/libraries。它不像URL辅助函数那样松耦合,可以在任意的网站上使用。
+
+它具有高度的专一性:它创建一个菜单,我可以在任何控制器中调用它,显示一个标准菜单。
+
+总结
+
+MVC框架已被广泛地用于构建复杂的动态网站。CI使用它帮助你编写高度可复用的代码。
+
+当你编写你自己的代码时,要始终保证代码的“松耦合”和“组件专一性”。不要担心你的项目是否严格遵守了MVC的理论。关键是理解文件的类型和它们如何彼此交互。然后,你再确定是否使用类库、模型、辅助函数或是插件编写代码。
+
+我们已经了解了CI的文件结构,而且知道我们可以随时随地的查看CI的源代码,不过我们通常并不需要这样做。
+
+config文件中包含网站的基本信息,可以使我们方便地修改信息,有利于我们快速迁移服务器,减少出错的机会。
+
+我们已经见到了控制器的基本结构,而且用构造函数从config文件中读取数据并把它存入对象的属性中。我们还动态地把数据从控制器传递给视图。到目前为止,CI中的一些重要知识已经学习过了。当我们继续学习时,我们会很清楚这些知识是非常重要的。
+
+同时,我们也学习了CI组件间传递数据的方式,了解这些对你的编程是很重要的。
+
+最后,我们分析了两个例子,一个是URL辅助函数的例子,还有一个是创建“menu”类库的例子。
+
diff --git a/_docs/3_old.doc b/_docs/3_old.doc
new file mode 100644
index 0000000..99cc0a1
Binary files /dev/null and b/_docs/3_old.doc differ
diff --git a/_docs/4.doc b/_docs/4.doc
new file mode 100644
index 0000000..988ce77
Binary files /dev/null and b/_docs/4.doc differ
diff --git a/_docs/4.txt b/_docs/4.txt
new file mode 100644
index 0000000..b97feb4
--- /dev/null
+++ b/_docs/4.txt
@@ -0,0 +1,792 @@
+第四章 使用CI简化数据库开发
+
+
+
+你学习CI是因为你想要使编程更容易和更有生产力。这一章讲述CI的Active Record类。 如果CI只提供一个Active Record类,它还是物超所值的。当然,CI是免费的,只不过我要强调Active Record(以下简称AR)类的价值是非常高的,它是你提高生产力的主要工具。
+
+AR使你以最小的代价获得最大的回报。它简单,易于使用和维护。
+
+这一章描述CI如何连接到一个数据库,你如何使用AR操纵数据库。你将会见到:
+
+ AR类与传统PHP/MySQL接口的比较
+
+ 如何读取数据库并显示结果
+
+ 如何创建,更新, 删除查询
+
+CI保留让你用传统的方法编写数据库查询,但是我不会详细介绍这部分内容。它的知识完全被在线手册覆盖。使用AR类后,你可能不会再用传统的方式来做数据库查询了。
+
+配置config文件
+
+你或许已经注意到在这本书的大多数的章节会谈到system/application/config 文件夹和里面的config文件。这些文件对控制CI按要求工作相当必要。而且你可以让大部分的配置参数等于系统的默认值。数据库config文件在正常使用数据库之前需要进行设置。
+
+基本上,你仅仅必须告诉它你的数据库在哪里、它是什么类型。文件的默认值为:
+
+$active_group="default";
+
+$db['default']['hostname']="";
+
+$db['default']['username']="";
+
+$db['default']['password']="";
+
+$db['default']['database']="";
+
+$db['default']['dbdriver']="";
+
+(注:到CI1.6.1时又多了如下一些项目)
+
+$db['default']['db_debug'] = TRUE;
+
+$db['default']['cache_on'] = FALSE;
+
+$db['default']['cachedir'] = "";
+
+$db['default']['char_set'] = "utf8";
+
+$db['default']['dbcollat'] = "utf8_general_ci";
+
+
+
+其他的选项可以保留为默认值。 必选项是:
+
+hostname: 你的数据库的位置, 举例来说, 'localhost' 或 IP 地址
+
+username和password: 使用者名称和密码必须有充分的权限,允许你的网站存取数据库中的数据。
+
+database: 你的数据库的名字, 举例来说, 'websits'
+
+dbdriver: 你正在使用的数据库的类型 - CI可受的有选项有MySQL、MySQLi、 Postgre SQL、ODBC和MS SQL
+
+以我的经验来看, 最困难的事情之一就是把新的CI网站连接到数据库。你可能需要查询你的ISP。有时他们的数据库运行在与他们的web server IP地址不同的地方。如果你正在使用 MySQL,他们可能提供 phpMyAdmin,通常告诉你hostname-这可能是 'localhost' 或者它可能是一个 IP 地址。
+
+你可能注意到 config 文件的内容实际上是一个多维数组。在 $db数组里包含一个叫做default的数组,你所做的设置就是往里增加键/值对,例如 hostname = 127.0.0.1 。你还可以增加其他的数据库设置, 通过改变$active_group的设置可以容易地更改数据据库。
+
+这为网站连接到数个数据库提供了可能性-举例来说, 一个测试数据库和一个产品数据库。你可以很容易地在他们之间切换。 或者你可以在二个数据库之间交换数据。
+
+为我们的网站设计数据库
+
+我想表达的是 CI 能用来开发正式的网站。 我现在正在维护客户的一些网站,而且我想要监控他们,用我设计的方法测试它们, 用数据库保存我想要的数据, 而且可以得到这些网站的分析报告。 因此让我们试着创建它。 先让我们确定一些目标。 它们是:
+
+1. 用最少的人工干预管理一个或更多的远程网站
+
+2. 对远程网站进行定期的测试
+
+3. 生成符合要求的分析报告, 提供网站的细节和测试结果
+
+因此, 第一件事情是我们将会需要一个网站的数据库。 建立一个名为websites的MySQL数据库,你也可以使用别的数据库产品。
+
+现在,我们需要增加一些表来保存各种数据。让我们为网站增加一张表,字段有URL,他们的名字和密码/用户名, 和他们的类型。 我们也将会为每个网站建立一个ID字段。而且在 MySQL数据库中,至少需要为实体生成一个唯一标识符,可以使用自动增量类型来达到这一目的。
+
+每个网站必须有一个不同的主机,我们需要另一表来保存主机信息。一般有一个域名与主机相关联,所以我们需要一个域名表来保存有关域名的信息,还需要一个人员表来记录这些人的姓名,密码,邮件地址,备用邮件地址,手机号码,曾至宠物的名字,可能还有其它的一些什么。
+
+因此我们的网站表需要包括这样一些字段:domain ID, host ID, 两个people ID,一个存放网站站长的ID一个存放网站管理人的ID(管理人为网站提供技术支持,保证网站正常运行。)
+
+你能见到,这是一个完整的关系型数据库,让我们来建立它!
+
+完整的建表文档,SQL文件格式:
+
+websites.sql:
+
+DROP TABLE IF EXISTS `ci_sessions`;
+
+CREATE TABLE IF NOT EXISTS `ci_sessions` (
+
+ `session_id` varchar(40) NOT NULL DEFAULT '0',
+
+ `peopleid` int(11) NOT NULL,
+
+ `ip_address` varchar(16) NOT NULL DEFAULT '0',
+
+ `user_agent` varchar(50) NOT NULL,
+
+ `last_activity` int(10) UNSIGNED NOT NULL DEFAULT '0',
+
+ `left` int(11) NOT NULL,
+
+ `name` varchar(25) NOT NULL,
+
+ `status` tinyint(4) NOT NULL DEFAULT '0'
+
+) ENGINE=MyISAM DEFAULT CHARSET=latin1;
+
+
+
+DROP TABLE IF EXISTS `domains`;
+
+CREATE TABLE IF NOT EXISTS `domains` (
+
+ `id` int(10) NOT NULL AUTO_INCREMENT,
+
+ `url` varchar(100) NOT NULL,
+
+ `name` varchar(100) NOT NULL,
+
+ `registrar` varchar(100) NOT NULL,
+
+ `dateregd` int(11) NOT NULL DEFAULT '0',
+
+ `cost` float NOT NULL DEFAULT '0',
+
+ `regdfor` int(11) NOT NULL DEFAULT '0',
+
+ `notes` blob NOT NULL,
+
+ `pw` varchar(25) NOT NULL,
+
+ `un` varchar(25) NOT NULL,
+
+ `lastupdate` int(11) NOT NULL DEFAULT '0',
+
+ `submit` varchar(25) NOT NULL,
+
+ PRIMARY KEY (`id`)
+
+) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=10 ;
+
+
+
+DROP TABLE IF EXISTS `events`;
+
+CREATE TABLE IF NOT EXISTS `events` (
+
+ `id` int(10) NOT NULL AUTO_INCREMENT,
+
+ `name` varchar(50) NOT NULL DEFAULT 'not set',
+
+ `type` enum('test','alert','report') NOT NULL,
+
+ `testid` int(10) NOT NULL,
+
+ `siteid` int(10) NOT NULL,
+
+ `userid` int(10) NOT NULL,
+
+ `reported` int(11) NOT NULL,
+
+ `result` blob NOT NULL,
+
+ `time` int(11) NOT NULL,
+
+ `timetaken` float NOT NULL,
+
+ `isalert` varchar(2) NOT NULL,
+
+ `emailid` int(11) NOT NULL,
+
+ `submit` varchar(25) NOT NULL,
+
+ PRIMARY KEY (`id`)
+
+) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=69 ;
+
+
+
+DROP TABLE IF EXISTS `frequencies`;
+
+CREATE TABLE IF NOT EXISTS `frequencies` (
+
+ `id` int(10) NOT NULL,
+
+ `name` varchar(16) NOT NULL,
+
+ `submit` varchar(25) NOT NULL,
+
+ PRIMARY KEY (`id`)
+
+) ENGINE=MyISAM DEFAULT CHARSET=latin1;
+
+
+
+DROP TABLE IF EXISTS `hosts`;
+
+CREATE TABLE IF NOT EXISTS `hosts` (
+
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+
+ `cost` float NOT NULL,
+
+ `name` varchar(100) NOT NULL,
+
+ `hosturl` varchar(100) NOT NULL,
+
+ `un` varchar(50) NOT NULL,
+
+ `pw` varchar(50) NOT NULL,
+
+ `ns1url` varchar(36) NOT NULL,
+
+ `ns1ip` varchar(36) NOT NULL,
+
+ `ns2url` varchar(36) NOT NULL,
+
+ `ns2ip` varchar(36) NOT NULL,
+
+ `ftpurl` varchar(100) NOT NULL,
+
+ `ftpserverip` varchar(36) NOT NULL,
+
+ `ftpun` varchar(50) NOT NULL,
+
+ `ftppw` varchar(50) NOT NULL,
+
+ `cpurl` varchar(36) NOT NULL,
+
+ `cpun` varchar(36) NOT NULL,
+
+ `cppw` varchar(36) NOT NULL,
+
+ `pop3server` varchar(36) NOT NULL,
+
+ `servicetel` varchar(50) NOT NULL,
+
+ `servicetel2` varchar(50) NOT NULL,
+
+ `serviceemail` varchar(100) NOT NULL,
+
+ `webroot` varchar(48) NOT NULL,
+
+ `absoluteroot` varchar(48) NOT NULL,
+
+ `cgiroot` varchar(48) NOT NULL,
+
+ `booked` int(11) NOT NULL,
+
+ `duration` int(11) NOT NULL,
+
+ `lastupdate` int(11) NOT NULL DEFAULT '0',
+
+ `submit` varchar(25) NOT NULL,
+
+ PRIMARY KEY (`id`)
+
+) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=6 ;
+
+
+
+
+
+DROP TABLE IF EXISTS `people`;
+
+CREATE TABLE IF NOT EXISTS `people` (
+
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+
+ `uname` varchar(25) NOT NULL,
+
+ `pw` varchar(25) NOT NULL,
+
+ `status` smallint(3) NOT NULL DEFAULT '1',
+
+ `name` varchar(50) NOT NULL,
+
+ `firstname` varchar(50) NOT NULL,
+
+ `surname` varchar(50) NOT NULL,
+
+ `email` varchar(120) NOT NULL,
+
+ `lastupdate` int(11) NOT NULL DEFAULT '0',
+
+ `submit` varchar(25) NOT NULL,
+
+ PRIMARY KEY (`id`)
+
+) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=5 ;
+
+
+
+DROP TABLE IF EXISTS `sites`;
+
+CREATE TABLE IF NOT EXISTS `sites` (
+
+ `id` int(10) NOT NULL AUTO_INCREMENT,
+
+ `name` varchar(100) NOT NULL,
+
+ `url` varchar(100) NOT NULL,
+
+ `un` varchar(50) NOT NULL,
+
+ `pw` varchar(50) NOT NULL,
+
+ `client1` int(10) NOT NULL DEFAULT '0',
+
+ `client2` int(10) NOT NULL DEFAULT '0',
+
+ `admin1` int(10) NOT NULL DEFAULT '0',
+
+ `admin2` int(10) NOT NULL DEFAULT '0',
+
+ `domainid` int(10) NOT NULL DEFAULT '0',
+
+ `hostid` int(10) NOT NULL DEFAULT '0',
+
+ `webroot` varchar(50) NOT NULL,
+
+ `files` text NOT NULL,
+
+ `filesdate` int(11) NOT NULL DEFAULT '0',
+
+ `lastupdate` int(11) NOT NULL DEFAULT '0',
+
+ `submit` varchar(25) NOT NULL,
+
+ PRIMARY KEY (`id`)
+
+) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=15 ;
+
+
+
+DROP TABLE IF EXISTS `tests`;
+
+CREATE TABLE IF NOT EXISTS `tests` (
+
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+
+ `siteid` int(11) NOT NULL DEFAULT '0',
+
+ `name` varchar(250) NOT NULL,
+
+ `type` varchar(25) NOT NULL,
+
+ `url` varchar(120) NOT NULL,
+
+ `regex` varchar(250) NOT NULL,
+
+ `p1` varchar(250) NOT NULL,
+
+ `p2` varchar(250) NOT NULL,
+
+ `p3` varchar(250) NOT NULL,
+
+ `p4` varchar(250) NOT NULL,
+
+ `p5` varchar(250) NOT NULL,
+
+ `p6` varchar(250) NOT NULL,
+
+ `frequency` int(10) NOT NULL DEFAULT '0',
+
+ `lastdone` int(10) NOT NULL DEFAULT '0',
+
+ `isalert` varchar(2) NOT NULL,
+
+ `setup` int(10) NOT NULL DEFAULT '0',
+
+ `lastupdate` int(10) NOT NULL DEFAULT '0',
+
+ `notes` varchar(250) NOT NULL,
+
+ `submit` varchar(25) NOT NULL,
+
+ PRIMARY KEY (`id`)
+
+) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=11 ;
+
+
+
+DROP TABLE IF EXISTS `types`;
+
+CREATE TABLE IF NOT EXISTS `types` (
+
+ `id` varchar(7) NOT NULL,
+
+ `name` varchar(50) NOT NULL,
+
+ PRIMARY KEY (`id`)
+
+) ENGINE=MyISAM DEFAULT CHARSET=latin1;
+
+
+
+我们现在要用一个更简单容易的方法来实现这一切。 所以,让我们看看CI框架为我们提供了什么功能,我们要重点介绍AR类。
+
+
+
+Active Record
+
+AR是一个“设计模式”。另一方面又是一个高度抽象的东西,就象 MVC。 它本身不是代码, 只是一个模式。对于代码,有一些不同的解释。它的核心是把你的数据库和PHP对象建立一个对应关系。每次当你执行一个QUERY语句。每一张数据库里的表是一个类,每一行是一个对象。所有你需要做的只是创建它,修改它或者删除它。例如,“方法”,是从类继承而来。Ruby on Rails是使用AR模式的,CI也是,尽管这两种框架实现AR的方式有一点不同。
+
+理论的东西够多了-但是这是什么意思呢?好吧, 用代码简单和清楚地描述一下吧。
+
+使用AR类的优点
+
+AR节约你的时间,自动运作,使SQL语句方便易懂。
+
+节约时间
+
+当你在PHP编程时,每写一个数据库查询的时候,你每次一定要与数据库建立连接。 对CI来说,第一次连接数据库时,你在每个控制器或模型的构造函数里放入这样一行语名:
+
+代码如下:
+
+ $this->load->database();
+
+一旦你这样做了,你不需要重复连接, 在那个控制器或模型就可以做任意多次的查询。
+
+你已经在 config文件中设置了关于数据库的参数,就象我们在这一章开始时看到的一样。再一次,这使更新你的网站比较容易,如果你想要改变数据库名字、密码或位置的话。
+
+自动机制
+
+你一旦已经连接到数据库,CI的AR生成隐含的代码。举例来说,如果你进行下列的插入操作:
+
+代码如下:
+
+ $data = array(
+
+ 'title'=> $title,
+
+ 'name' => $name,
+
+ 'date' => $date
+
+ );
+
+$this->db->insert('mytable',$data);
+
+你正在插入的数据据已经被在幕后转换成这样一行代码:
+
+代码如下:
+
+ function escapte($str) {
+
+ switch (gettype($str)) {
+
+ case 'string':
+
+ $str="'".$this->escape_str($str)."'";
+
+ break;
+
+ case 'boolean':
+
+ $str=?($str=== FALSE) 0: 1;
+
+ break;
+
+ default :
+
+ $str=?($str=== NULL) 'NULL': $str;
+
+ break;
+
+ }
+
+return $str;
+
+}
+
+换句话说, CI框架使你的代码变得更强健。 现在,让我们看看它是如何工作的。
+
+第一, 连接数据库非常简单。在传统的PHP编程时,你可能是这样做的:
+
+代码如下:
+
+ $connection = mysql_connect;(" localhost" , " fred","12345")
+
+ mysql_select_db("websites",$connection);
+
+ $result = mysql_query;("select * from sites", $connection);
+
+ while ($row = mysql_fetch_array($result,MYSQL_NUM)) {
+
+ foreach($row as $attribute) {
+
+ print "{$attribute[1]}";
+
+ }
+
+ }
+
+换句话说,你必须重复地输入host、username和password,建立一个连接,然后选择一个连接的数据库。你必须每次都这样做,然后再开始一个查询。CI以一条命令替换连接工作:
+
+$this->load->database();
+
+在每个控制器、模型或者其它类的构造函数里放入这条命令。之后,你就可以直接开始查询了。连接数据被保存在你的数据库config文件中,CI每次都会去那里查询它。
+
+因此, 在每个CI函数中,你可以直接进行数据库查询。上面的query在CI中被转换成:
+
+代码如下:
+
+ $query = $this->db->get('sites');
+
+ foreach($query->result() as $row) {
+
+ print $row->url
+
+ }
+
+很简单, 不是吗?
+
+
+
+这一个章的余下部分给出不同query的用法。
+
+读取查询
+
+常用的查询是取回来自数据库的数据。等同于select的查询是:
+
+$query =$this->db->get('sites');
+
+这是一个“select *”查询,目标是site表。换句话说,它取回所有的行。如果你偏爱分开来写,你能这样做:
+
+$this->db->from('sites');
+
+$query = $this->db->get();
+
+
+
+如果你想要得到特定的列,而不是全部列,这样做:
+
+$this->db->select('url','name','clientid');
+
+$query = $this->db->get('sites');
+
+
+
+你可能要对结果排序,你可以在get语句前插入:
+
+ $this->db->orderby("name","desc");
+
+desc是降序的意思。你也能选择asc(升序) 或rand(random随机)。
+
+
+
+你也可能想要限制返回的行数,比如你想要最初五个结果。你可以在get语句前插入:
+
+$this->db->limit(5);
+
+
+
+ 当然,在大多数的查询,你不可能在表中返回所有记录。数据库的具有按给定条件过滤返回结果的能力。这通常使用where子句来实现,CI这样表达:
+
+ $this->db->where('clientid','1');
+
+ 这条语句会查找客户号是 1 的客户相连的所有的网站。 但是这对我们并不是很有帮助,我们并不会记住人员表的ID号。对人来说,用姓名是比较合理的办法,因此我们需要连接到people表:
+
+
+
+$this->db->from('sites');
+
+$this->db->join('people','sites.peopleid=people.id');
+
+有SQL知识的人都知道,这相当于:
+
+SELECT sites.* FROM sites,people WHERE sites.peopleid=people.id
+
+
+
+注意SQL的约定,如果一个列名在二张表中是重复的,你需要在列名前加上表名和一个“."号。因此sites.peopleid在位置桌子中意谓peopleid所在的表是sites。在进行SQL多表查询时,最好把列名进行唯一性的标识,这样可以避免产生岐义,也可以让你自己明了。
+
+你可以增加更多的where子句的操作符。举例来说,增加否定操作符:
+
+$this->db->where('url!=','www.mysite.com');
+
+或比较操作符:
+
+$this->db->where('id >','3');
+
+ 或联合陈述:(“where …and…”):
+
+ $this->db->where('url!=',' www.mysite.com');
+
+$this->db->where('id >','3');
+
+
+
+ 或使用$this->db->orwhere();来表示:(“where…or”):
+
+$this->db->where('url!=',' www.mysite.com');
+
+$this->db->orwhere('url!=',' www.anothersite.com');
+
+
+
+现在让我们建立一个完整的查询:
+
+代码如下:
+
+ $this->db->select('url','name','clientid','people.surname as client');
+
+$this->db->where('clientid','3');
+
+$this->db->limit(5);
+
+$this->db->from('sites');
+
+$this->db->join('people','sites.clientid=people.id');
+
+$this->db->orderby("name"," desc");
+
+$query = $this->db->get();
+
+这应该给我们前五个(用姓名排序)网站,这些网站属于3号客户,而且还显示客户的姓和他或她的身份证数字!
+
+使用AR的潜在好处是已经进行了自动的转义,因此,你不必关心转义的问题。这适用于这样的函数象$this->db->where(),以及在下一个段中被描述的数据插入和修改语句。(安全警告:这不同于阻止交叉脚本攻击-对付这个你需要CI的xss_clean()函数。它也不相同于验证你的数据-对付这个你需要 CI 的验证类,见第 5 章。)
+
+显示查询结果
+
+在CI显示查询结果相当简单。假定我们定义了上述的查询语句, 最后一句是:
+
+$query =$this->db->get();
+
+ 然后,如果有多个结果,他们被保存在$row对象中,你可以用一个 foreach 循环:
+
+代码如下:
+
+ foreach($query->result() as $row) {
+
+ print $row->url;
+
+ print $row->name;
+
+ print $row->client;
+
+}
+
+或如果我们只想要一个结果,它可以作为一个对象被返回, 或在这里当做一个$row数组:
+
+代码如下:
+
+ if ($query->num_rows()>0) {
+
+ $row =$query->row_array();
+
+ print $row['url'];
+
+ print $row['name'];
+
+ print $row['client'];
+
+}
+
+我比较喜欢对象语法胜过数组-更简洁!
+
+如果你遵守 MVC 模式,你将会在模型中保存你的查询和数据库交互, 然后通过视图显示数据。
+
+生成和更新查询
+
+AR有三个函数帮助你在数据库内生成新的实体,它们是$this->db->insert(),$this->db->update,$this->db->set().
+
+“create”和“update”的不同之处是“create”是向表中插入一条全新的记录,而“update”是修改表中已经存在的记录。因此对“update”,你必须首先定位需要修改的记录。
+
+CI 用数组来保存数据,或是使用$this->db->set();你可以任选一种。
+
+因此, 如果要在websites数据库中加入一行。首先,确保在我们的控制器中的构造函数加入:
+
+$this->load->database();
+
+我们想要增加一个新的网站,包含有一个网址,一个名字,一个类型和一个客户ID。如果用数组的方式,这可能是:
+
+代码如下:
+
+ $data = array(
+
+ 'url' => 'www.mynewclient.com',
+
+ 'name' => 'BigCo Inc',
+
+ 'clientid' => '33',
+
+ 'type' => 'dynamic'
+
+ );
+
+把这些信息增加到sites表,我们使用:
+
+$this->db->insert('sites', $data);
+
+ 我们更可以使用$this->db->set():
+
+代码如下:
+
+ $this->db->set('url','www.mynewclinet.com');
+
+$this->db->set('name','BigCo Inc');
+
+$this->db->set('clientid', '33');
+
+$this->db->set('type','dynamic');
+
+$this->db->insert('sites');
+
+如果我们正在更新一笔现有的记录,我们也可以创建一个数组或使用$this->db->set(),但是现在有二个不同。
+
+第一,我们必须定位我们想要更新的记录;其次,我们需要使用$this->db->set(),如果我想要在sites中更新一笔记录(针对'id'列的值1的那行记录),使用数组的方式是这样处理的:
+
+$this->db->where('id','1');
+
+$this->db->update('sites', $data);
+
+你也可以使用$this->db->set()方式,就象前面做过的那样。
+
+CI 提供几个函数检查数据库是否成功执行了相关操作。 最有用的:
+
+$this->db->affected_rows();
+
+ 在执行insert或update后应该返回 '1'-但是如果我正在update一批记录的话,可能返回更大的一个整数。
+
+你已经注意到当我insert一笔新的记录时,我没有设定ID这一列。这是因为ID这列被设定为自动插入类型。但是当我update一笔现有的记录时候,我必须引用ID属性,否则数据库不知道该改变哪一笔记录。
+
+如果我正在insert一笔新的记录, 在实际产生它之前,我们并不知道ID具体的值。如果我需要引用新的记录的ID, 使用下列语句:
+
+$new_id_number = $this->db->insert_id();
+
+ (这一行代码必须跟在insert语句之后,否则可能得到错误的结果.)
+
+还有一点需要知道,CI的AR操作,包括$this->db->insert_id()和$this->db->update_id()会自己转义。
+
+从 1.5 版,CI也包含了对事务的支持,即指定的一批SQL操作要么全成功,要么全失败,换句话说,要么提交,要么回滚。这在复式记帐应用和许多商业应用中是很重要的。举例来说,说你正在卖电影票。你需要接受付款,同时分配座位。如果你的系统收费成功但分配座位失败,这个客户肯定是要光火的。
+
+CI 现在也让事务处理变得很简单,即要么“提交”,要么回滚。你可以参考用户手册以得到更多关于事务的信息。
+
+
+
+删除操作
+
+删除操作也许是最简单的。你只要定位好需要删除记录,比如我们要删除ID为2的记录:
+
+$this->db->where('id','2');
+
+$this->db->delete('sites');
+
+要小心使用删除操作,因为如果你不注意where中的条件,可能会误操作,甚至最坏的结果是删除整张表。
+
+
+
+AR和传统SQL编程的结合
+
+CI不要求你只能使用AR,你也能用CI直接发送SQL查询。比如:假定你已在构造函数里已连接了数据库,你可以在需要的地方直接使用类似的SQL查询:
+
+$this->db->query("select id, name, url from sites where 'type'='dynamic'");
+
+我个人觉得AR更容易使用。借助数组为AR准备数据更直观,更容易理解,虽然可能需要更多的代码。AR还能自动转义,对不熟悉SQL语法的程序员会更有吸引力。
+
+还有需要介绍的一个使用AR的技巧是关于where函数的,当我们需要在where中放入这样一个比较复要的语句时:
+
+client='3' and (type = 'dynamic' or type = 'static')
+
+由于使用了左右括号,因此不能直接放入where函数,你可以把这分成两行:
+
+ $condition = "client ='3' AND (type ='dynamic' OR type='static')";
+
+$this->db->where($condition);
+
+在一些复杂的情况下,你可能需要直接使用SQL语法建立查询而不是使用AR,比如要使用复杂的表连接,子查询,左连接等等。不管怎样,CI框架确保你能做到传统PHP能做到的一切,而带来更多的好处。
+
+需要指出的是,如果你试着混合使用AR和直接的SQL查询,你可能会有麻烦。
+
+
+
+摘要
+
+我们已经介绍了CI的Active Record类,而且见到,它是多么容易使用:
+
+ 建立和一或较多的数据库的连接
+
+ 使用AR从数据库读取数据或插入,更新,删除信息
+
+ 或直接使用SQL的标准语法执行查询
+
+CI的AR功能是概念清晰又容易使用,而且使代码非常易读。它自动化数据库连接,并把连接数据保存至一个config文件。
+
diff --git a/_docs/4_old.doc b/_docs/4_old.doc
new file mode 100644
index 0000000..0d4b0d8
Binary files /dev/null and b/_docs/4_old.doc differ
diff --git a/_docs/5.doc b/_docs/5.doc
new file mode 100644
index 0000000..9904736
Binary files /dev/null and b/_docs/5.doc differ
diff --git a/_docs/5.txt b/_docs/5.txt
new file mode 100644
index 0000000..a6f8273
--- /dev/null
+++ b/_docs/5.txt
@@ -0,0 +1,538 @@
+简化 HTML 页面和表格设计
+
+这一章介绍了又一个节约你的时间而且使你的代码更具安全性和逻辑性的领域。
+
+第一,我们将会介绍创建视图的各种不同方法-与你的控制器和模型协同并用来显示结果的页面。
+
+然后,你将会学到如何很快地创建 HTML 表格, 与实现内建的保护; 而且你也将会看到该如何校验你的表格。
+
+我假定这本书的读者熟悉 HTML 和 CSS 。 下列的例子非常简单,因此,我们能把重点放在 CI 代码上。 而且我已经假定我们已经写一个 CSS 文件并已把它保存在网站的某个目录中。
+
+编写视图
+
+视图是用户用户能看到你的网站的所有。 他们使用一个统一的接口, 而且可以根据需要进行修改。 MVC 的好处之一是你分开了表示层和逻辑层, 一切都显得很干净。
+
+到现在为止, 我们已经完成了那非常简单的 'welcome' 页面,(记得第 3 章吗?) 现在让我们看看该如何使它变得更精细。
+
+视图实际上是一组包含有你的内容的HTML结构。结构中有各种元素,如颜色,字体,文字布局等; 不过视图不关心这些,它要做的只是取来内容,显示出来。
+
+创建视图, 首先你需要创建一个HTML 网页的骨架,并保存为.php后缀。 让我们称它为 basic_view.php 。保存在application/views目录中。 (CI的loader会在这个目录寻找视图文件。)
+
+<html>
+<head>
+</head>
+<body>
+<p>Hello World!</p>
+</body>
+</html>
+
+然后当你想要从一个控制器装载它时, 使用在某个函数中调用$this->load->view():
+
+function index() {
+ $this->load->view('basic_view');
+}
+
+注意,如果这是一个model或者一个helper,你将会首先装载它, 然后根据需要使用它。 通过视图,调用它只需要一行代码。
+
+当然,那是一个空的视图。 为了要使它有用,我们需要内容。因此我们要增加名称和一些文本。 首先我们在控制器中定义他们:
+
+function() {
+ $data['mytitle'] = "A website monitoring tool";
+ $data['mytext'] = "This website helps you to keep track of the other websites you control";
+}
+
+注意我们并没有把它们定义为单独的变量, 而是作为数组$data的元素。 对于第一个元素, 键名是 'mytitle',值是 "A website monitoring tool".
+
+然后,我们调用装载函数:
+
+function index() {
+ $data['mytitle'] = "A website monitoring tool";
+ $data['mytext'] = "This website helps you to keep track of the other websites you control.";
+
+ $this->load->view('basic_view',$data);
+}
+
+我们把$data数组作为$this->load->view()的第二个叁数,在视图名称之后。视图接收到$data数组后,使用PHP函数extract()把数组中的每个元素转换成内存变量,数组的键名即为变量名,值为变量内所包含的值。这些变量的值能直接被视图引用:
+
+<html>
+<head>
+</head>
+<body>
+ <h1 class='test'><?php echo $mytitle; ?></h1>
+ <p class='test'><?php echo $mytext; ?></p>
+</body>
+</html>
+
+虽然你只能传送一个变量到视图, 但是通过建立数组,你能把大量变量整洁地传入视图。它似乎复杂, 但是实际上是一种紧凑和优秀的信息传输方式。
+
+PHP标记的长格式和短格式
+
+在我们继续之前, 先了解一下PHP标记的两种不同格式。
+
+常用的方式是:
+
+<?php echo $somevariable?>
+
+然而,如果你不喜欢这种, CI 也支持一个较短的版本:
+
+<?=$somevariable?>
+
+<?php ?>被<??>取代,而且echo由“=”代替,你也能用短格式来使用if, for, foreach, 和while循环。完整的介绍请参考在线用户手册。
+
+我个人偏好标准格式,因为我已习惯使用它。如果你使用短格式,注意有些服务器不能正确地解释短格式。 如果你仍然想要使用短标签, 可以打开 config 文件, 改变下列设置:
+
+$config['rewrite_short_tags']= FALSE;
+
+如设置为TRUE,CI在把它们送到服务器之前,将把短格式改成标准格式。 不过这样做会对调试造成困难。因此,建议使用标准格式。
+
+CI 也有一个 '模板语法分析器' 类,允许你把变量放入HTML代码而不需要任何的PHP代码。本文不涉及这个内容。如果在与可能被 PHP 代码弄糊涂的 HTML 设计者合作,它相当有用,细节请参见用户手册。
+
+嵌套视图
+
+为了最大程度地重用代码,我们可以提取HTML页面的公共部分,例如,header和footer,然后在显示实际视图时把它们组合起来。
+
+让我们创建一个视图的header部分, 这是一个符合W3C标准的header、包含HTML声明和 meta 数据。
+
+首先, 我们列出header部分的代码:
+
+<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 strict//EN'http://www.w3.org/TR/xhtml1/DTD/xhtml-strict.dtd'>< html xmlns=' [url]http://www.w3.org/1999/xhtml'>[/url];
+<title><?php echo $mywebtitle?></title>
+<base href=<?php echo "$base"; ?>>
+<?php echo $myrobots?>
+<link=" stylesheet" type="text/css" href="<?php echo "$base/$css;?>">
+
+把这保存为views/header_view. 下面介绍它包含的变量:
+
+。$mywebtitle, 是标题 (meta标签; 这将不在屏幕上出现,但是搜索引擎将会读取,每个页面的meta有可能有变化,因此,我把它设为一个变量。)
+
+。$myrobots, 这个变量用来告诉机器人当前页面不需要被编入索引。
+
+。$base和 $css, 描述基本网址的字符串。$css包含css文件的路径信息, 这些信息会从CI的config 文件读取(也可以用 CI config 变量 site_url代替。)
+
+现在我们需要知道:
+
+。我们如何调用嵌套视图?
+
+。我们如何获取变量的值?
+
+有二个方法可选择。 第一,在主调用视图中读入其它视图,因此我们的主视图,也就是basic_view,应该加上一行:
+
+<html><head>
+<?php $this->load->view ('header_view'); ?>
+</head><body>
+<?php echo $mytitle; ?>
+<?php echo $mytext; ?>
+</>
+</html>
+
+变量可以在控制器中加上两行:
+
+ function index() {
+ $data['mytitle'] = "A website monitoring tool";
+ $data['mytext'] = "This website helps you to keep
+ track of the other websites you control.";
+ $data['myrobots'] = '<meta name="robots" content
+ ="noindex,nofollow">';
+ $data['mywebtitle'] = 'Web monitoring tool';
+ $data['base'] = $这->config->item('base_url');
+ $data['css'] = $this->config->item('css');
+
+ $this->load->view('basic_view',$data);
+ }
+
+在这里新的变量 $myrobots,$css, $base, $mywebtitle被创建为数组$data的新元素,当header_view被basic_view调用时,CI使用extract()解开他们, 在视图中显示出来(在两个视图中不要出现同名的变量,否则会引起冲突)。
+
+第二个方法将把视图加入控制器里面, 给它分配一个变量:
+
+ function index() {
+ $data['mytitle'] = "A website monitoring tool";
+ $data['mytext'] = "This website helps you to keep
+ track of the other websites you control.";
+ $data['myrobots'] = '<meta name="robots" content
+ ="noindex,nofollow">';
+ $data['mywebtitle'] = 'Web monitoring tool';
+ $data['base'] = $这->config->item('base_url');
+ $data['css'] = $this->config->item('css');
+ $data['header'] = $this->load->view('header_view', '', TRUE);
+
+ $this->load->view('basic_view',$data);
+ }
+从严格的 MVC 设计原则来看,这样做似乎更正确。
+
+实际上有三个叁数可传给load->view函数。
+
+。 第一个, header_view, 是要装载的视图的名字。 这是必选。
+
+。 第二个,是可选项, 是装入视图的数据。
+
+。 第三个是布尔值。如果你不指定它,默认是FALSE, 将视图送到浏览器。 然而,在嵌套视图这种情况下,你需要将header_view送到主视图basic_view中,因此需要将第三项参数设置为TRUE。
+
+现在我们已经建立了与 stylesheet 的关联, 我们能够用定义好的css中的类来更新视图中的显示部分:
+
+<html><head>
+<?php $this->load->view('header_view'); ?>
+</head><body>
+ <h1 class='test'><?php echo $mytitle; ?></h1>
+ <p class='test'><?php echo $mytext; ?></p>
+</body>
+</html>
+
+请注意CI的 MVC 系统能让你分离显示的内容。 视图只为内容提供结构, 结构的风格则由css stylesheet控制。
+
+视图不关心什么 $mytext的内容是什么,它只是按照正确的格式在正确的位置上显示它。 定义 $mytext 的控制器甚至不知道 (也不关心) 它产生的数据如何被显示。
+
+因此, 如果我们需要改变我们网页的外观, 或在一个不同的系统 (如WAP)上显示他们,那么我们只需要改变视图和CSS stylesheet。 我们不需要修改控制器。
+
+而且如果我们想要改变在网页上的信息,我们不需要去改动视图, 而是只需要改变控制器里的变量值。
+
+记得 '松藕合' 原则吗? 这里再一次体会到了这个原则,这使设计,升级, 和维持你的网站比较容易。
+
+网站结构的现实问题
+
+请稍等片刻,我们在header_view动态地生成了 CSS stylesheet地址:
+
+<link="stylesheet" type="text/css" href="<?php echo "$base/$css";?>">
+
+这意味着控制器必须生成变量的值,这些值与数据如何被显示有关, 但是我们在上面说过控制器不应该知道或者关心它们具体的值是什么。这样不就符合了我们刚才提及的所谓'松藕合'原则? 动态地产生这些数据需要这样一些操作: 首先,控制器必须在 config 文件中读取它们,然后控制器必须在$data数组中装入它们而且传送它们到视图,然后视图必须解开成为内存变量$base和$css, 真正使用这两个变量的是HTML协议。
+
+似乎这样做太绕圈子了,为什么不直接在视图中静止地插入数据?
+
+<link="stylesheet" type="text/css" href="http://www.mysite.com/mystylesheet.css";">
+
+用变量方式做这件事情的好处是:如果你迁移网站, 或移动你的 CSS 文件,你只需要在 config 文件中改变设置,而且每个控制器和视图将会立刻反映变化。而如果把CSS位置硬编码到每个视图的后果是一旦需要变化,你必须到每个视图中去修改它们,明白这样做的好处了吧?
+
+CI 的表格助手: 录入数据
+
+让我们把目光转向你如何使用你的 HTML 页。任何动态的网站最重要部份之一是和用户互动,而且这通常意味着使用 HTML 表格。 编写表格是重复和无聊的。
+
+CI 的表单辅助函数是非常有用的代码片断。 它有一个稍稍不同的定义, 使表格创建起来比较容易。 让我们建立一个表格,这个表格允许我们在浏览器中录入数据。 在websites数据库的sites表中,我们想要录入网站的名字、类型和网址, 和更新的日期。
+
+你能用简单的 HTML代码 建立表格, 或你能在一个控制器内建立它,把它赋给一个变量, 然后调用视图, 而且传送该变量到视图。 我正在按第二种方式做。
+
+第一,我们必须装载表格助手到我们需要使用它的控制器内。 然后, 我们把下列的代码放入控制器的构造函数:
+
+$this->load->helper('form');
+
+然后,我们必须开始编写表格。
+
+现在, 为了生成表格的输入项, 我们不用这样写:
+
+$variable .= <input type='text' name='name' value=''>
+
+CI 让你这样做:
+
+$variable .= form_input('name','');
+
+(记得'name'是输入项的名称, 'value'是你想输入的内容。 在这里可以设定value的初始值,或你能动态地从表格中获取.)
+
+可以看到,使用CI的表格助手列方便。
+
+使用表格助手的好处之一: 清楚
+
+使用 CI 表格助手的第一个利益是你的代码绝对的清楚。 如果你想要一个比较精细的输入框, 如果用HTML是这样的:
+
+$variable = 'input type="text" name=" url" id="url" value=" www.mysite.com" maxlength="100" size="50" style="yellow"/>';
+
+name是将在$_POST数组中取得的变量名称。
+
+id是在网页上定位这个输入框的标识符,如果你使用JavaScript的话。
+
+value是输入框里显示的值,它一开始是一个默认值,用户也可以在输入一个新的值。
+
+maxlength 和size是明显的; style一组 HTML 格式或者在css stylesheet 中定义。)
+
+CI 用一个数组代替上述的HTML代码:
+
+$data = array(
+ 'name' => 'url',
+ 'id' => 'url',
+ 'value' => 'www.mysite.com',
+ 'maxlength'=> '100',
+ 'size' => '50',
+ 'style' => 'yellow',
+ );
+
+$variable = form_input($data);
+
+它看上去蛮长的, 实际上并不比HTML代码长,而且,它非常清楚, 容易理解和维护。而且是动态的。
+
+隐藏的表格输入框非常简单。如果我们想要自动地记录我们的数据库被更新的日期。 我们把日期放入一个$date变量, 然后:
+
+form_hidden('updated', $date);
+
+如果你想要一个'文本'输入框, 给你的使用者提供一个可以输入超过一行的地方,可以使用CI的form_textarea()函数,下面的代码使用默认的长度,在网页上显示一个文件输入框:
+
+$data = array(
+ 'name' => 'url',
+ 'id' => 'url',
+ 'value' => 'www.mysite.com',
+ );
+
+$variable = form_textarea($data);
+
+CI的表格助手在你编写下拉框,多选框和单选框时特别有用,如果我们要改变我们的URL输入框为一个下拉框,允许用户从下拉列表中选取一个URL。首先,把下拉列表的选项存入一个数组,然后调用form_dropdown()函数:
+
+$urlarray= array(
+ '1' => 'www.this.com',
+ '2' => 'www.that.com',
+ '3' => 'www.theother.com',
+ );
+
+$variable = form_dropdown('url' 、 $urlarray, '1');
+
+被传给表格中url下拉框的第一个参数是输入框的名字; 第二个是包含下拉列表的数组,第三个默认选项。 换句话说,如果使用者接受默认值, 你的 $_POST数组将会包含值 'url=>1' ,但是你的用户将会见到选项 'www.this.com'.
+
+如果使用HTML代码编写:
+
+<select name="type">
+<option value="1" selected>www.this.com</option>
+<option value="2">www.that.com</option>
+<option value="3">www.theother.com</option>
+</select>
+
+CI实现的代码实际上比较短, 很容易学会。
+
+如果你在一个数据库表('urls')中储存你的可能选择的网址的目录,那么生成一个动态下拉框很容易。 首先把数据从表中读出放到一个数组中:
+
+ $urlarray = array();
+
+ $this->db->select('id,url');
+
+ $query = $this->db->get('urls');
+ if ($query->num_rows()>0) {
+ foreach($query->result() as $row) {
+ $urlarray[$row->id] = $row->url;
+ }
+ }
+
+然后重复我们以前用过的 CI form_dropdown() 功能:
+
+ echo form_dropdown('type', $urlarray,'1');
+
+只有$urlarray 的内容会发生变化; 代码总是一样的。
+
+如果你正在更新一个表中的记录而不是插入, 你不想为你的用户显示默认值。你想要为那一个记录显示已经存在的值。你应该已经知道你想要的修改的记录的id值,因此,你需要先读取数据库中'site' 表中相关记录。确定把查询结果赋给一个变量,使用第二个变量取出第一个变量的中的相关记录,再调用CI的form_dropdown函数,把第二个变量和对应的列名作为参数传入:
+
+ $this->db->select('id, url, name');
+ $this->db->where('id','$id');
+ $sitequery = $this->db->get('sites');
+ $siterow = $sitequery->row();
+
+然后你的 CI 下拉框函数会从中读取相关信息:
+ echo form_dropdown('url' 、 $urlarray, $siterow->url);
+
+本书没有太多的篇幅讨论所有的表格助手。 它还能编写单选框,隐藏文件框,多选框和一些其它的输入框,完整的资料请参考CI用户手册。
+
+使用表单辅助函数的好处之二: 自动化
+
+使用表格助手的第二个好处是可以自动化实现一些功能,不然的话,你只能自己编写相关的脚本了。
+
+首先, 它拦截HTML的一些字符,比如用户输入的引号,并且转义它们以免破坏表格。
+
+其次,它自动链接。当你打开一个表单时,你必须声明目标页,它将会接受表单的数据并且处理它。(在CI中,这是一个控制器里面的一个功能而不是一个静态页。 比如它指向控制器的更新函数.) 因此,如果你用纯 HTML 代码,你将会这样写:
+
+<form method="post" action="http:/ www.mysite.com/index.php/websites/update"/>
+
+如果你用 CI 打开你的表格,你只需要这样做:
+
+form_open(websites/update);
+
+CI 自动地在你的 config 文件中取出基本URL并定位到对应的控制器函数。 再次强调,如果你迁移你的网站,你只需要修改config文件,而不是去一个一个地修改代码文件。
+
+顺便提一下, CI 假定你的表格将会总是以POST的方式提交数据而不是GET方式。CI普遍使用URL本身,因此,不要搞错。
+
+我的 '显示' 模型
+
+作为示范(稍微简化了一下),这里是我的显示模型:
+
+<?php
+class Display extends Model {
+
+/*create the array to pass to the views*/
+ var $data = array();
+/*two other class variables*/
+ var $base;
+ var $status = '';
+/*
+the constructor function: this calls the 'model' parent class, loads other CI libraries and helpers it requires, and dynamically sets variables
+*/
+ function Display()
+ {
+ parent::Model();
+ $this->load->helper('form');
+ $this->load->library('user_agent');
+ $this->load->library('errors');
+ $this->load->library('menu');
+ $this->load->library('session');
+/*now set the standard parts of the array*/
+ $this->data['css'] = $this->config->item('css');
+ $this->data['base'] = $this->config->item('base_url');
+ $this->base = $this->config->item('base_url');
+ $this->data['myrobots'] = '<meta name="robots"
+ c>';
+/*
+note that CI's session stuff doesn't automatically recall the extra variables you have added, so you have to look up the user's status in the ci_sessions table
+*/
+ $sessionid = $this->session->userdata('session_id');
+ $this->db->select('status');
+ $this->db->where('session_id', $sessionid);
+ $query = $this->db->get('ci_sessions');
+ if ($query->num_rows() > 0)
+ {
+ $row = $query->row();
+ $this->status = $row->status;
+ }
+ }
+
+/*
+function to assemble a standard page. Any controller can call this. Just supply as $mydata an array, of key/value pairs for the contents you want the view to display. Available variables in this view are:
+mytitle. menu, mytext, diagnostic
+*/
+ function mainpage($mydata)
+ {
+ $this->data['mytitle'] = 'Monitoring website';
+ $this->data['diagnostic'] = $diagnostic;
+ foreach($mydata as $key => $variable)
+ {$this->data[$key] = $variable;}
+/*here's the menu class we looked at in Chapter 3*/
+ $fred = new menu;
+ $this->load->library('session');
+ $mysess = $this->session->userdata('session_id');
+ if(isset($this->status) && $this->status > 0)
+ {$this->data['menu']=
+ $fred->show_menu($this->status);}
+ $this->load->view('basic_view', $this->data);
+
+ }
+}
+?>
+
+我能用下面的代码在任何的控制器中调用这个主页:
+
+$this->load->model('display');
+$this->display->mainpage($data);
+
+并且我也知道我的视图正在被动态地装配,完全符合我的需要。
+
+CI 的校验类: 方便地检验数据
+
+在你编写HTML表格时一个重要的工作是检查输入。我们都知道我们应该这样做,但是…直到现在为止,我们已经编写过一种简单的表格, 将会信任地接受任何用户输入的任何数据。 你应该意识到可能有一些用户是不怀好意的,而且所有的其余都是不负责任的。(别直接告诉他们.) 如果他们有可能犯一个简单的错误,他们就会犯。确保你始终检查用户输入的数据,并使它们符合你的要求。
+
+你能在客户端用javascript做到这一点, 但是这样做作用有限,使用者能容易地绕过它。 而在服务器端的校验需要一个额外的信息来回,这点额外的开销是值得的。
+
+编写校验代码也相当复杂, 但是-你一定猜到了-CI 提供了一个校验类可以使这项工作变得非常容易。
+
+让我们改变我们自己的表格处理过程来实现校验。 你需要在表格里作一些调整, 还要在它指向的函数里作一些调整。
+
+如果你的表格由 form_open('sites/update') 开始, 你需要修改的函数是'sites控制器里的'update'函数。 如果你没有使用 CI 的表格助手, HTML等价代码是:
+
+<form method="post" action="http://www.mysite.com/index.php/sites/update"/>
+
+你需要做三件事情:
+
+1. 设置校验
+2. 设置控制器
+3. 设置表格
+
+设置校验
+
+在你的表格指定的那个函数中装载校验类并声明你的校验规则:
+
+ $this->load->library('validation');
+
+ $rules['url'] = "required";
+ $rules['name'] = "required";
+
+ $this->validation->set_rules($rules);
+
+'url'和 'name'输入框一定要有输入内容。 CI提供了各种操作,确保一些操作一定要进行,用户手册全面地解释了这些内容。他们的含义非常明了: min_length[6] 显然意味着输入的信息长度一定要大于等于六个字符。numeric意味着只能输入数字,等等。你还能组合规则,用“|”把它们连接起来:
+
+ $rules['name'] = "required |alpha| max_length[12]";
+
+意味着不能为空,字母,长度至少12个字符。你甚至能编写你自己的规则。
+
+设置控制器
+
+仍然在相同的函数中,创建一个 'if/else'语句:
+
+ if ($this->validation->run() == FALSE) {
+ $this->load->view('myform');
+ } else {
+ $this->load->view('success');
+ }
+
+你进行确认测试,而且如果输入内容不能通过测试的话,就再返回到输入页面。(如果你在一个控制器内的一个函数中生成你的视图, 则使用$this->myfunction 代替$this->load->view('myform')。
+
+如果校验成功,就生成view("success"),告诉用户输入的信息已被接受, 然后给出一个链接让他进到下一步。
+
+设置表单
+
+录入信息的表格也要做相应的调整。 每次校验没有通过的话,你不但要让系统返回到录入界面,而且必须说明哪一项出错, 以及为什么出错。 因此你必须在表格的某处给出一个附加信息:
+
+$this->validation->error_string;
+
+这行代码显示适当的信息, 避免用户在那里犯嘀咕。
+
+你也需要自动地填写用户已正确输入的那些内容,否则,用户必须再次录入上一次他们已经正确录入的信息。
+
+首先,你需要在控制器里增加更多的代码。而且是立刻加在校验规则之后,加入一个数组来存放给用户的提示信息。 数组的键名是你表格中的输入框名,值是给出的错误提示信息:
+
+$fields['url'] = '你的网址';
+
+然后,增加一行代码:
+
+$this->validation->set_fields($fields);
+
+现在你已经在控制器里声明了一个存有信息的数组,你只需要在表格内加入显示它们的代码。 对于HTML代码,这会是:
+
+<input type="text" name="url" value="<?php echo $this->validation->url; ?>"/>
+
+或, 如果你正在使用 CI 的表格助手:
+
+$variable .= form_input('url', "$this->valication->url");
+
+如果使用这个表格插入一个新的记录到数据库的表中,上面的代码已经够用了。如果你正在使用表格更新一个已经输入过的记录,当表格第一次显示时,应该在输入框中显示数据库表中的实际信息,这个时候,它的值应该是从数据库里读回来的(记得前面的例子吗?我们把代码再显示在这里:
+
+ $this->db->select('id, url, name');
+ $this->db->where('id','$id');
+ $sitequery = $this->db->get('sites');
+ $siterow = $sitequery->row();
+
+ echo form_dropdown('url' 、 $urlarray, $siterow->url);
+
+如果你在更新一个现有的记录时,上一次的录入内容由于一个输入框内容没有录入而无法通过校验,在重新回到表格之前,你需要在通过校验的输入框中填写用户刚录入的信息,而在校验出错的输入框里再次放入从数据库表中读入的信息,否则,你就需要再次录入已经校验通过的信息了。
+
+还好,这可以通过一个简单的“if/else”语句来实现:
+
+if (isset($_post['url'])) {
+ $myvalue=$this->validation->url;
+} else {
+ $myvalue=$siterow->url;
+}
+
+第一次表格显示的时候,在$_POST数组中将会没有内容; 因此你从数据库的相关表中读取信息。但当你提交一次以后,$_POST 数组中有数据存在,所以你选择validation函数中返回的值。
+
+查阅CI用户手册,了解表格校验的其它内容,你还可以做到:
+
+。自动地准备你的数据, 举例来说, 通过它消除可能产生的跨站脚本攻击
+
+。编写你自己的复杂校验标准,举例来说, 用户录入的值不能已经存在于数据库中
+
+。编写你自己的错误信息
+
+CI的校验类非常有用而又功能强大,值得花时间好好研读并掌握。
+
+摘要
+
+我们已经学习了CI中生成视图的方法, 以及它如何让你创建'迷你-视图', 你能把视图嵌套到其它视图中去。这意谓你能建立共用的HTML header,HTML footer, 实现视图的重用。
+
+我们也已经见到 CI 如何帮助你编写 HTML 录入表格,通过表格助手函数简化HTML form的编写工作。
+
+
+最后,我们学习了 CI 的校验类,这是检查用户录入信息的有用工具。没有什么是完美的,但是这个工具的确能阻击你的用户录入垃圾,或企图进行攻击。它也使你的网站看起来更加专业,能够有效地捕捉用户造成的各种输入错误,而不是一味地接受无意义的输入。
+
+在整个学习过程中,我们也再次玩味了MVC的原则, 而且有时稍稍地做一些变通会让生活变得更容易。 CI 有一种非常有柔性的哲学: 如果要有效率地解决问题,就要学会灵活地使用工具。
+
+下一章我们学习:
+
+简化session和安全
\ No newline at end of file
diff --git a/_docs/6.doc b/_docs/6.doc
new file mode 100644
index 0000000..c9ef58e
Binary files /dev/null and b/_docs/6.doc differ
diff --git a/_docs/6.txt b/_docs/6.txt
new file mode 100644
index 0000000..3812c5a
--- /dev/null
+++ b/_docs/6.txt
@@ -0,0 +1,384 @@
+简化使用 Session 和安全
+
+理论说得够多了! 现在让我们开始写我们自己的应用。 在这一章里,我们将会大致描述一下我们要建立的一个应用系统,而且我们分析一些会影响到网站系统的的基本问题也就是会话管理和安全。
+
+在这一章,我们将会见到:
+
+。如何使你的网页安全
+
+。如何使用 CI 的会话类
+
+开始用 CI 设计一个实际的网站
+
+我们已经看过 CI 安装时生成的welcome页以及它内部动作中控制器文件和视图文件的动作流程。 实际上这就是CI的'Hello, World!'。
+
+从前,业余者写的发烧友网站都使用开放源代码系统,而且总是被一些所谓的大公司看成是低档的玩艺儿。
+
+不过现在,套用一句老话,时代不同了。很多大公司开始使用开放源代码技术。 举例来说,美国太空总署和美联社使用MySQL 、美国审计处和雅虎正在使用 PHP 的应用。而且我相信规模适中,具有高度灵活性的动态网站正在迅速增长。当大公司了解到那些他们一直使用的商业技术无法新的需求后,他们引进规模适中,具有高度灵活性的运用代替原来的系统
+
+CI 无疑是设计开发适度规模,具有高度灵活性的利器。它手把手地教你规划网站并做出一致而可靠的运用。
+
+为了示范 CI 的灵活性,让我们开始建立一个应用。
+
+重新说一下我的需求,我要让这个网站为我解决我的问题。 我正维护着一些网站, 有些是我自己的,有些是我的客户的。我需要对这些网站进行日常维护,测试它们, 确保它们运转良好。大部分的工作内容是重复性的,我可以雇人来做这件事,但是写一个网站会是比较合理的方案,它能自动化的完成工作,不管白天还是黑夜。
+
+因此我的需求是:
+
+1. 不需要人工干预,管理一个或更多的远程网站
+
+2. 定期检测远程网站
+
+3. 生成报表, 提供网站的细节和测试结果
+
+如果ISP允许的话,我想设定网站能运行Cron(注:linux下的计划任务软件),如果不允许, 我会自己一天两次或者1小时1次地运行, 让它运行一个预设的测试方案。
+
+我不想要知道细节,除非什么东西出现故障(然后我想要让它发送一封电子邮件告诉我出院什么事,在哪个环节出的事),然后我想要能够打印出管理报告让我的客户意识到我一直做定期的检查, 并正常地运行他们的网站。
+
+为了避免使代码太长和过于重复,因此在这本书里的代码并不足够安全, 因此请记住不用在实际应用环境中使用这些代码。 这一章涉及了针对未认证用户的确保网站安全的一般概念其他的PHP安全问题,本书没有涉及。
+
+在现阶段,我们可以看到CI实现方式与一般动态网站的方式相似,因此让我们先放下设计这个主题,从一些非常基本概念谈起。
+
+关于网站
+
+任何的网站是一个各类应用的集合,而且它们彼此之间应该能够交互。我们在第 3 章看到, CI 使用URL进行链接。典型的URL如下表:
+
+基本url http://www.mysite.com. 这是最基本的URL,用户用它定
+ 位你的网站。用户不需要知道URL的其余部分,网站会在需要时自动加上其它部分
+
+index 文件 第一部分: index.php
+ 这是CI的的网站主文件
+
+class 第二部分:start
+(或称为controller) 如果没有设置控制器,CI会从config文件中寻找默认控制器
+
+method 第三部分:assessme
+(或称为函数) 如果没有设定函数,CI会使用本控制器中的默认函数,找不到就
+ 重定向到404页
+
+增加的任何的叁数 第四部分:fred
+ 传递给函数的参数
+ 第五部分: 12345, 第六部分: Hello, 等等。
+
+
+
+因此, 在start控制器中调用accessme函数,并传入fred,12345两个叁数,你的网址将会是:
+
+http://www.mysite.com.index.php/start/assessme/fred/12345
+
+
+系统会查找start.php, 其中有一个Start类,它的内部有一个accessme方法,需要传入两个参数,现在传给这两个参数的值分别是fred和12345。一个这样的URL可以调用你网站上任何一个控制器中的任何功能。
+
+为了体会它如何工作,让我们建立我们网站的第一个页面。我们将会建立叫做Start的控制器,而且把它设定为默认控制器。
+
+好吧, 首先, 因为安装CI时, 它指定welcome为默认控制器,因此我需要改变这个设置。
+
+CI默认的路由存放在下列路径
+
+/system/application/config/routes
+
+当前设置为:
+
+$route['default_controller']="welcome";
+
+我们把它改成::
+
+$route['default_controller']="start";
+
+(记住, 如果你的默认控制器中没有一个index方法,如果使用基本URL,系统会显示404错误)
+
+现在我需要编写我的start控制器。 记得基本的格式:
+
+<?php
+class Start extends Controller {
+ function start() {
+ parent::Controller();
+ }
+
+ function assessme($name, $password) {
+ if ($name =='fred' && $password == '12345') {
+
+ $this->mainpage();
+ }
+ }
+
+}
+?>
+
+把这个文件保存为/system/application/controllers/start.php。
+
+第二行告诉你这是一个控制器。 然后构造函数装载父控制器构造方法。 assessme函数需要二个变量 $name和 $password。 CI(从 1.5 版开始) 自动分配URL的相关部分作为叁数, 因此 fred 和 12345 将会变成那 $name和 $password。
+
+因此, 如果我们键入上面的网址,如果用户存在,密码无误,我会被重定向到mainpage()函数。 我们会在start控制器中增加这个函数。(如果用户检测不能通过,系统运行结束.)
+
+对于那些过去一直使用过程式编程而不是OO编程的人来说,请注意一个类中的方法必须被表示成 $this->xxxx。 因此, 如果我调用start控制器里的mainpage()函数,我必须这样表示$this->mainpage(). 否则,CI会找不到它。
+
+当然, 一般用户不会这样定位一个网站:
+
+http://www.mysite.com.index.php/start/assessme/fred/12345.
+
+他们仅仅输入:
+
+http://www.mysite.com
+
+而且希望网站给出导航条。 因此让我们让这里开始。
+
+通常, 你在网站上看到的第一个页面是登录页面。 因此让我们来做一个。 首先,我把一个新的函数加入start控制器。我想要网站默认调用这个函数,因此,我将它命名为index().
+
+ function() {
+
+ $data['mytitle'] = "My site";
+ $data['base'] = $this->config->item('base_url');
+ $data['css'] = $this->config->item('css');
+ $data['mytitle'] = "A website to monitor other websites";
+ $data['text'] = "Please log in here!";
+
+ $this->load->view('entrypage',$data);
+ }
+
+它调用视图:entrypage。 视图中包含一个表格,这个表格让用不着户输入用户名和密码。 HTML 表格需要指定一个处理$_POST的函数,我们已经把它放在start控制器中,就是 assessme() 。用HTML代码表示,在我们的视图上的表格应该是:
+
+<form method="post" action=" http:/ www.mysite.com/index.php/start/assessme"/>
+
+我已经稍稍解释了 assessme函数,它接受一个用户/密码组合,我需要在一个数据库中查询它。为了要使代码更结构化, 我们要引入一个函数叫做checkme()。
+
+所以,你会见到, assessme() 调用 checkme(),Checkme()在数据库里检查用户是否存在,密码是否有效,然后把'是' 或 '不' 返回给assessme()。如果返回是, assessme() 调用另外一个函数,mainpage(),返回一个视图。
+
+注意代码模块好的好处。 每个函数扮演一个角色。 如果我需要改变系统检查密码的方式,我只需要改变 checkme() 函数。如果我需要修改页面,我只要修改 mainpage() 函数。
+
+让我们看看代码结构以及各部分如何交互。 (注意为了要使例子简洁,我们不校验用户的输入。 当然,这不会造成问题, CI 的表格类自动为输入数据“消毒”。
+
+/*receives the username and password fromthe POST array*/
+ function assessme() {
+ $username = $_POST['username'];
+ $password = $_POST['password'];
+
+ /*calls the checkme function to see if the inputs are OK*/
+ if ($this->checkme($username,$password)=='yes') {
+ /*if the inputs are OK, calls the mainpage function */
+ $this->mainpage;()
+ }
+ /*if they are not OK, goes back to the index function, which re-presents the log-in screen */
+ else
+ {
+ $this->index();
+ }
+ }
+ /*called with a u/n and p/w, this checks them against some list. For the moment, there's just one option. Returns 'yes' or 'no' */
+
+ function checkme($username='', $password='') {
+ if ($username == 'fred' && $password =='12345') {
+ return 'yes';
+ } else {
+ return('no';
+ }
+
+ }
+
+在第 5 行-6 上, assessme() 接收来自表格的$_POST数组,包含以下一些数据:
+
+[username] => fred
+[password] => 12345
+
+assessme() 函数把这二个变量传递给另一个函数checkme()。该函数检测具有密码12345的用户fred是否是合法的用户, 如果他们是, 它返回 'yes'. 显然地,在一个真正的网站上,这会更复杂。你或许会为有效的用户名/密码做数据库查询。把它定义成一个单独的函数意味着我可以测试我的代码的其它部分, 并在稍后改进 checkme()函数。
+
+如果使用者名称和密码是一个有效的组合, assessme()函数调用另一个函数 mainpage(),让用户进入网站。否则,它返回到index()函数-即,再次显示登录窗口。
+
+下一个问题是如何处理状态。换句话说,当进入到另一页面时如何识别这是一个已登录的用户。
+
+安全/会话: 使用另一个 CI 类
+
+如果我想要建立一个会话机制确保未授权用户无法存取我的文件, 需要多少代码?
+
+英特网通过一系列请求工作。 你的浏览器给我的服务器发请求,要求某一特定页。 我的浏览器把该页处理后返回你的服务器。服务器处理后向浏览器发出另一页面,你再按下一个超链接,对我的服务器提出新的页面请求。如此而已。
+
+英特网是'没有状态的'-就是, 你的浏览器对我的服务器做的每个请求都被当做一个单独事件,HTTP协议没有办法把你的请求和任何其他的请求联系起来。
+
+如果你想要你的网站页面与页面之间建立联系,你必须管理“状态”,要服务器记得哪些请求来自你的浏览器, 需要做一些特别的处理。
+
+PHP 提供管理状态的二种办法: 使用cookie、或使用会话。PHP的session自动地检查你的浏览器是否接受cookie. 如果不接受,它会使用会话ID的方法,这个ID直接通过URL进行传递。
+
+cookie是小块的数据由服务器发送到你的浏览器。浏览器自动地保存它。一旦保存了cookie,当浏览器尝试访问网站的时候,网站就会检查是否存在cookie。如果它找到正确的cookie,它能读取它里面的数据适当地配置它本身。可能关闭特定页面,或显示你的个人数据。
+
+
+因为一些人设定他们的浏览器不接受cookie, PHP 提供其它变通的方法。 每次当浏览器请求访问时,网站产生 '会话ID', 把它发送到浏览器端,当浏览器发送下个请求时把这个ID加入URL,所以网站能够识别浏览器。
+
+CI 有一个会话类来处理会话相关工作。 事实上,它大大减少了编码量。我们在上一章学习到 CI 有各种各样的library,大大简化了PHP的编程。 这些就是框架的核心: 集成大量代码,为你提供各种强大的功能。你要做的只是使用它们,而不必关心他们是如何运作的。作为结果,你的应用使用了最专门的代码,而你却不需要自己去编写它们!
+
+如果你想要在你的控制器内或模型中使用某个类内的函数,你一定记得首先要装载这个类,(有些类, 比如 config 总是自动地被装载,这是为什么我们不需要在代码中装载它的原因。)
+
+你可以这样很简单地装载library:
+
+$this->load->library('newclass');
+
+通常,把这一行放入你的控制器或模型的构造函数中。
+
+如果你认为你会在每个控制器中使用某个library,你能象config类一样自动地装载它。找到/system/application/config/autoload文件, 增加你想自动装载的类名:
+
+$autoload['libraries'] = array();
+
+因此它看起来像这一样:
+
+$autoload['libraries'] = array('newclass',' oldclass');
+
+在这里,我们首先要用到session类,这帮助你维持状态, 而且可以识别用户。 做到这一点相当简单。下面列出增强的assessme()函数:
+
+ function assessme() {
+
+ $this->load->library('session');
+
+ $username = $_POST['username'];
+ $password = $_POST['password'];
+
+ if ($this->checkme($username, $password) == 'yes') {
+ $this->mainpage;()
+ } else {
+ $this->index();
+ }
+}
+
+(你可以看到,在函数一开始就装载session library,但是通常,我们将会在控制器的构造函数中装载它,因此,在这一个类中的其它函数都可以使用这个类。)
+
+当这个类装入后,立即通过它本身强大的功能为你提供对会话的读取,创建,修改等功能。
+
+好吧, 坦白地讲, 不仅仅就一行代码那么简单。 你必须首先修改 config 文件, 告诉会话类你想要它做的。
+
+检查你的system/application/config/config.php 文件,你会找到像这样的一个部分:
+--------------------------------------------------------------------------
+| Session Variables
+|--------------------------------------------------------------------------
+|
+| 'session_cookie_name' = the name you want for the cookie
+| 'encrypt_sess_cookie' = TRUE/FALSE (boolean). Whether to encrypt the cookie
+| 'session_expiration' = the number of SECONDS you want the session to last.
+| by default sessions last 7200 seconds (two hours). Set to zero for no expiration.
+|
+*/
+$config['sess_cookie_name'] = 'ci_session';
+$config['sess_expiration'] = 7200;
+$config['sess_encrypt_cookie'] = FALSE;
+$config['sess_use_database'] = FALSE;
+$config['sess_table_name'] = 'ci_sessions';
+$config['sess_match_ip'] = FALSE;
+$config['sess_match_useragent'] = FALSE;
+
+现在,确定 [sess_use_database] 被设定成FALSE。
+
+保存设置,现在,每次当你的客户登入网站,网站会在你的机器上保存一个cookie,包含下列的数据:
+
+。CI生成的一个唯一的SESSION ID,这是CI为这个会话生成的任意组合的字符串。
+
+。使用者的 IP 地址
+
+。使用者的用户代理数据 (浏览器数据的最初 50个字符)
+
+。最大有效时间
+
+如果你设定 sess_encrypt_cookie 为FALSE, 你能在你的浏览器上看到cookie的内容:(它部分被编码了,但是你能辨认出来):
+
+ip_address%22%3Bs%39%3%22127.0.0.1%22%3Bs%310%3A22
+
+包括使用者的URL, 本例为127.0.0.1). 如果你设定 sess_encrypt_cookie 为TRUE, cookie被加密, 只是一行无意义的代码。 浏览器不能识别它,当然使用者也无法修改它。
+
+当使用者发出另一个页面请求时, 网站会检查会话ID是否已经以cookie的形式保存在用户的机器里,如果有,这会作为一个已经存在的会话的一部分,如果没有,就会建立一个新的会话。记得在所有的其它控制器上加载会话类。CI将作同样的检查。我必须做的只是告诉每个控制器当没有cookie时该如何运作。
+
+使会话更安全
+
+这本来不会造成安全问题。任何访问网站的用户都会开始一个会话。代码只是判断他们是否是一个新用户。避免未认证的用户访问某些页面的一个方法是如果他们已经登录,就在cookie中加入一些信息,因此我们可以对此进行验证。一旦用户输入正确的用户名和密码一次,就会在cookie中被记录,当发出新的请求時,会话机制会检查cookie,如果是已登录的用户,就返回用户请求的页面,如是不是,就返回登录画面。
+
+把信息加入cookie很容易。 在我的 assessme() 控制器中,如果密码和使用者名称验证通过,加上如下代码:
+
+if($this->checkme($username, $password)=='yes') {
+ $newdata = array(
+ 'status' => 'OK',
+ );
+ $this->session->set_userdata($newdata);
+ $this->mainpage();
+}
+
+这样会把$newdata数组中内容-上例中只有一个变量-加入到cookie中。现在,每当密码/用户名组合是可接受的, assessme() 将会把 'OK' 加入cookie,而且我们可以把这些代码加到每个控制器中:
+
+ /*remember to load the library!*/
+ $this->load->library('session');
+
+ /*look for a 'status' variable in the contents of the session cookie*/
+ $status = $this->session->userdata('status');
+
+ /*if it's not there, make the user log in*/
+ if(!isset($status) || $status != 'OK')
+ { /*function to present a login page again…*/}
+
+ /*otherwise, go ahead with the code*/
+
+这样,你在你的网站周围构建起安全的地基。 你可以很容易地使它变得更精细。 举例来说,如果你的部分用户有更高级的权限, 你可以在cookie中保存了个存取级别码,而不是只放进一个“OK”,你能用这个进行更细致的权限调控。
+
+在一个cookie中存放明码数据会比较糟糕,因为用户可以打开cookie并看到它的内容,当然也可以很容易地修改它。因此使用CI加密cookie会是一个很好的办法。还有,你可以在数据库中建立一个用户表,在用户登录以后修改用户表的登录状态,在用户表中维护一些权限属性,每次访问时,从数据库的用户表中读取权限信息,也是一个比较好的做法,如果结合缓存技术,就会解决系统的性能问题。
+
+在你的数据库中保存会话数据非常简单。 首先,创建数据库表。 如果你正在使用 MySQL, 使用这一 SQL 语句:
+
+CREATE TABLE IF NOT EXISTS `ci_sessions` (
+session_id varchar(40) DEFAULT '0' NOT NULL,
+ip_address varchar(16) DEFAULT '0' NOT NULL,
+user_agent varchar(50) NOT NULL,
+last_activity int(10) unsigned DEFAULT 0 NOT NULL,
+status varchar(5) DEFAULT 'no',
+PRIMARY KEY (session_id)
+);
+
+然后,修改在system/application/config/database.php 文件中的连接叁数告诉 CI 数据库在哪里。详细介绍参见第 4 章
+
+如果工作正常的话, 当你登录或退出时,在你的数据库表中增加了相关的数据。如果你在一张数据库表中储存会话, 当用户登录到你的网站,网站会查找cookie, 如果它找到了,就会读取会话ID,并用这个ID去匹配数据库表中保存的ID。
+
+你现在拥有一个强健的会话机制。 而这一切只需要一行代码!
+
+一点声明,PHP本身的会话类在用户关闭cookie功能时能够应付。(取代生成cookie,它把会话数据加入URL) CI 类不会这样做,如果使用CI,一旦用户禁止cookie,他就不能登录到你的网站。这方面需要增强功能。 希望Rich将会很快升级CI。
+
+安全
+
+注意到:会话类会自动地保存关于访问者的 IP 地址和使用者的资料。你可以使用一些增强的安全措施。
+
+通过修改config文件的二处设置可增加安全性:
+
+。 sess_match_ip: 如果你设定这个参数为TRUE, 当它读取会话数据时,CI 将会尝试相配使用者的 IP 地址。这将预防使用者使用'抢'来的cookie信息。但是,有些服务器 (ISP和大公司服务器) 可能存在不同 IP 地址上的相同使用者登录的请求。如果你设定这个参数为TRUE,可能会给他们造成麻烦。
+
+。 sess_match_useragent: 如果你设定这为TRUE, 当读取会话数据的时候, CI将会试着匹配使用者代理。这意谓试着“抢夺”会话的人还需要匹配真正用户的user agent设置。 它使“抢夺”变得稍稍有些困难。
+
+CI 也有一个 user_agent 类, 你可以这样装载:
+
+$this->load->library('user_agent');
+
+一旦装载, 你能要求它返回访问你网站的浏览器和操作系统的各种信息, 而不管它是一个浏览器,手机或机器人。 比如,如果你想列出叁观你的网站的机器人,你可以这样做:
+
+$fred = $this->agent->is_robot();
+if ($fred == TRUE)
+ {$agent = $this->agent->agent_string();
+/*add code here to store or analyse the name the user agent is returning*/
+}
+
+类通过装入后开始工作, 与用代理,浏览器,机器人和其它的类型的数组作比较,这个数组存放在system/application/config/user_agents。
+
+如果你愿,你可以容易开发出使你的网站锁定特定机器人,特定浏览器类型的功能。不过,记得一个攻击者很容易写出一个代理类型,并返回他想要扔给你的那种类型。因此,他们能容易地伪装成通常的浏览器。 许多机器人,包括象CI 的 user_agents 数组已列出的 Googlebot,是'品行端正的'。 这意味着如果你设定你的 robots.txt 文件排除他们,他们将不会强行攻击。没有容易的方法去阻击一个不知名的的机械手,除非你预先知道他们的名字!
+
+在 CI框架中 ,会话机制保存那发出请求的 IP 地址,因此你可以使用这个功能维护一个网站的黑名单。可以用如下代码取回来自会话的 IP:
+
+/*remember to load the library!*/
+ $this->load->library('session');
+/*look for an 'ip_address' variable in the contents of the session cookie*/
+$ip = $this->session->userdata('ip_address');
+
+因此你能针对黑名单作相应的安全处理,比如拒绝登录。
+
+你也可以用CI 的会话机制限制来自重复请求的损害-像是一个机器人通过重复地请求网页来使你的网站超载。 你也可以使用这一个机制处理 '字典'攻击,即一个机器人不断重复尝试数百或者数以千计的密码/用户名组合直到它找到正确的一组。
+
+因为 CI 的会话类保存每个会话的last_activity ,所以你能做到这一点。 每次,当网页被请求时, 你能检查多久以前这个IP地址的用户发出一请求,两次请求的间隔如果不正常,你可以终止会话,或放慢响应的速度。
+
+摘要
+
+我们已经概略介绍了我们想要建立的一个应用, 和几乎所有应用都需要解决的问题:会话管理和安全保证。
+
+为了做到这一点,我们已经学习了CI会话类的一引起细节,而且见到它如何生成会话记录和在访客的浏览器中生成cookie。
+
+当后续的请求发生时,你可以读取cookie对响应进行控制。
+
+下一章介绍
+
+CodeIgniter 和对象
\ No newline at end of file
diff --git a/_docs/7.doc b/_docs/7.doc
new file mode 100644
index 0000000..16e7cc2
Binary files /dev/null and b/_docs/7.doc differ
diff --git a/_docs/7.txt b/_docs/7.txt
new file mode 100644
index 0000000..b91e415
--- /dev/null
+++ b/_docs/7.txt
@@ -0,0 +1,269 @@
+CodeIgniter 和对象
+
+本章是“技术发烧友”的最爱。它讲述的是 CodeIgniter 的工作原理,也就是揭开 CI 头上的“神秘面纱” 。如果你是 CI 新手,你可能想要跳过本章。不过,迟早你会想要了解 CI 的幕后都发生了什么,为什么不真正的玩转它呢?
+
+当我刚开始使用 CodeIgniter 的时候,对象使我迷惑。 我是在使用 PHP 4的时候接触CI的, PHP4并不是真正的面向对象的语言。我在一大堆对象和方法、属性和继承,还有封装等数据结构中转悠,总是被类似的出错信息包围 " 调用非对象的成员函数". 我如此频繁地看到它们,因此我想到要印一件T恤衫,上写: 神秘,无规律可循, 而我仿佛正穿着它站在一个现代艺术展会的会场上。
+
+这一章的内容包含CI使用对象的方法, 和你OO编程的方法。 顺便说一下,术语 '变量/属性', '方法/函数'是等义的,当 CI 和 PHP 经常会混着使用它们。比如,你在你的控制器中写一个 '函数', 纯 OO 程序员会称他们是'方法'。你称之为类的变量而纯OO程序员会叫它们‘属性’。
+
+面向对象编程
+
+我正在假定你和我一样有 OOP 的基本知识, 但如果只是在PHP4中尝试过可能还不太够。 PHP 4 不是一种 OO 语言, 虽然具备了一些 OO 的特征。 PHP 5 会更好一些, 它的引挚已经彻底改写成面向对象的了。
+
+不过基本的OO特征PHP4也能实现,而且 CI 设法让你无论是使用PHP4 还是PHP5,都有一样的行为特征。
+
+重要的是你要记住,当 OO 程序运行时,总会有一个或数个真实的对象存在。对象可能彼此调用,只有当对象处于运行状态的那一刻你才可以读取变量(属性) 和方法 (函数)。因此知道和控制哪个对象当前在运行很重要。当一个类没有实例化时,你不能对它内部的属性和方法操作,静态方法和属性除外。
+
+PHP,作为一个过程式编程和OO编程的混合体, 可以让你混合编写又是过程式又是OO的程序,你可以在一过程式代码中实例化一个类,然后使用它的属性和方法,用完后把它从内存中释放掉。这一些工作,CI都可以为你代劳。
+
+CI '超级-对象'的工作原理
+
+CI 生成一个超级大对象: 它把你的整个项目当作一个大的对象。
+
+当你启用 CI 的时候,一连串复杂的事件开始发生。 如果你设定你的 CI 产生日志,你将会见到类似下列的记录:
+
+1 DEBUG - 2006-10-03 08:56:39 --> Config Class Initialized
+2 DEBUG - 2006-10-03 08:56:39 --> No URI present. Default controller
+ set.
+3 DEBUG - 2006-10-03 08:56:39 --> Router Class Initialized
+4 DEBUG - 2006-10-03 08:56:39 --> Output Class Initialized
+5 DEBUG - 2006-10-03 08:56:39 --> Input Class Initialized
+6 DEBUG - 2006-10-03 08:56:39 --> Global POST and COOKIE data
+ sanitized
+7 DEBUG - 2006-10-03 08:56:39 --> URI Class Initialized
+8 DEBUG - 2006-10-03 08:56:39 --> Language Class Initialized
+9 DEBUG - 2006-10-03 08:56:39 --> Loader Class Initialized
+10 DEBUG - 2006-10-03 08:56:39 --> Controller Class Initialized
+11 DEBUG - 2006-10-03 08:56:39 --> Helpers loaded: security
+12 DEBUG - 2006-10-03 08:56:40 --> Scripts loaded: errors
+13 DEBUG - 2006-10-03 08:56:40 --> Scripts loaded: boilerplate
+14 DEBUG - 2006-10-03 08:56:40 --> Helpers loaded: url
+15 DEBUG - 2006-10-03 08:56:40 --> Database Driver Class Initialized
+16 DEBUG - 2006-10-03 08:56:40 --> Model Class Initialized
+
+在启动时-每次一页通过英特网发出请求-CI 每次启动都执行相同的程序。你能通过 CI 文件追踪记录:
+
+1. index.php 文件收到一个页请求。 URL可能指出哪一个控制器被调用, 如果不, CI 有一个默认值控制器 (第 2 行).Index.php 开始一些基本检查然后调用 codeigniter.php 文件(\codeigniter\ codeigniter.php).
+
+2. codeigniter.php 文件实例化 Config 、Router、Input,URL(等等)类.(第 1 行, 和 3-9行) 这些被调用的叫做'基础'类: 你很少直接与它们交互,但是CI做的每件事都与它们有关。
+
+3. codeigniter.php 测试了解它正在使用哪一个PHP版本,根据版本决定调用base4 还是base5(/codeigniter/base4.(或base5)php). 这些创建一个 '单一' 实例: 即一个类只能有一个实例。不管哪个都有一个&get_instance() 方法。 注意符号 &:, 这是引用实例的符号。因此如果你调用 &get_instance() 方法, 它产生类的单一实例。换句话说,整个应用中这个实例是唯一的,其中包含许许多多框架中其它类的实例。
+
+4. 在安全检查之后,codeigniter.php 实例化被请求的控制器、或一个默认控制器 (第 10 行) 。 新的类叫做 $CI 。在URL中(或默认值)中被指定的函数被调用,类被实例化之后,相当于活了,实实在在存在于内存中。 CI 然后将会实例化你需要的任何其他的类, 并包含函数库脚本文件。 因此在日志中,model类被实例化。(第16 行)'模板文件' 脚本, 也被装载(第 13 行), 这是我编写的包含标准代码的一个文件。 它是一个.php 文件,保存在scripts目录中,但是它不是一个类: 仅仅是一组函数。 如果你正在写 '纯粹的' PHP代码,你可能会使用 'include ' 或者 'require'把这个文件放进命名空间,CI会使用它自己的 '装载' 函数把它放入“超级对象“中。
+
+'namespace' 的概念或范围在这里是决定性的。当你声明一个变量、数组、对象等等的时候,PHP把变量名称保存在内存中并为它们的内容分配一个内存块。如果你用相同的名字定义二个变量就会出现问题。 (在一个复杂的网站中,容易犯这样的错误。) 因为这个原因,PHP 有几条规则。 举例来说:
+
+。 每个函数有它自己的namespace 或者范围, 而且定义在一个函数中的变量一般是一个局部变量。 在函数外面, 它们是看不到的。
+
+。 你能声明 '全局' 变量, 放在特别的全局 namespace,在整个程序中都可以调用。
+
+。 对象有他们自己的 namespaces:对象内的变量(属性)是与对象同时存在的,可以通过对象来引用。
+
+因此 $variable, global variable, 和 $this->variable是三件不同的事情。
+
+特别地,在 OO 之前,这可能导致各种混乱: 你可能有太多的变量在同一namespace中(以致于许多冲突的变量名互相覆盖),也可能发现有些变量在某个位置无法存取。CI 为此提供了一个解决办法。
+
+假如现在你已经键入如下URL: www.mysite.com/index.php/welcome/index, 你是希望调用welcome控制器的index函数。
+
+如果你想要了解,哪个类和方法在当前的namespace 中可用, 试着在welcom控制器中插入下列 '检测' 代码:
+
+$fred= get_declared_classes();
+
+foreach ($fred as $value) {
+ $extensions = get_class_methods($value);
+ print "class is $value, methods are: ";
+ print_r($extensions);
+}
+
+试着运行它,它列出了270个已明的类。 大部分是PHP的。 最后的 11 个来自 CI: 10个是 CI 基础类 (config 、router等等。) 而且都是我的控制器调用的类。下面列出这11个类,清单只保留了最后的两个方法,其它的被省略了:
+
+258: class is CI_Benchmark
+259: class is CI_Hooks,
+260: class is CI_Config,
+261: class is CI_Router,
+262: class is CI_Output,
+263: class is CI_Input,
+264: class is CI_URI,
+265: class is CI_Language,
+266: class is CI_Loader,
+267: class is CI_Base,
+268: class is Instance,
+269: class is Controller, methods are: Array ( [0] => Controller [1] => _ci_initialize [2] => _ci_load_model [3] => _ci_assign_to_models [4] => _ci_autoload [5] => _ci_assign_core [6] => _ci_init_scaffolding [7] => _ci_init_database [8] => _ci_is_loaded [9] => _ci_scaffolding [10] => CI_Base )
+270: class is Welcome, methods are: Array ( [0] => Welcome [1] =>
+index [2] => Controller [3] => _ci_initialize [4] => _ci_load_model [5] => _ci_assign_to_models [6] => _ci_autoload [7] => _ci_assign_core [8] => _ci_init_scaffolding [9] => _ci_init_database [10] => _ci_is_loaded [11] => _ci_scaffolding [12] => CI_Base )
+
+注意-看一下Welcome类括号中包含的内容 (270号: 即我正在使用的控制器) ,它列出了Controller类的所有方法 (269 号). 这就是为什么你总是需要从一个控制器类派生子类的原因-因为你需要你的新控制器保留这些函数。 (而且同样地,你的models应该总是从model类继承.) Welcome类有两个额外的方法: welcome()和index()。到现在为止,在 270个类中,我写的只有这二个函数!
+
+你可能还注意到类的实例-即object。 有一个指向它的变量,注意到那个引用符号了吗?表明在整个系统中,CI_Input类只有一个实例,可以用类变量input调用它:
+
+["input"]=>&object(CI_Input)#6(4){[" use_xss_clean"]=> bool(false)[" ip_address"]=> bool(false)[" user_agent"]=> bool(false)[" allow_get_array"]=> bool}(false)
+
+记得我们何时装载了input文件而且创建了最初的输入类? 它包含的属性是:
+
+use_xss_clean is bool(false)
+ip_address is bool(false)
+user_agent is bool(false)
+allow_get_array is bool(false)
+
+你可以看到, 他们现在已经全部被包括在实例中,“设计图纸”变成了房子,不是吗?
+
+所有其它的 CI 的基础类(routers, output等等。) 同样地被包含了。 你不需要调用这些基础类,但是 CI 本身需要他们使你的代码工作。
+
+引用复制
+
+刚才提到,类变量input引用了CI_Input类:(["input"]=>&object(CI_Input)), 加不加引用符号区别在于:加上引用符号,一变俱变,不加引用符号,原始对象的内容不会改变。你可能会对此感到困惑,用一个简单的例子来说明:
+
+$one = 1;
+$two = $one;
+echo $two;
+
+显示 1, 因为 $two是$one的拷贝。 然而,如果你再重新$one赋值:
+
+$one = 1;
+$two = $one;
+$one = 5;
+echo $two;
+
+仍然显示 1, 因为在对 $one 重新赋值前 $two已经赋为1了,而$one和$two是两个不同的变量,各自分配有一小块内存,分别存放它们的值。
+
+如果在$one改变的时候,$two也要相应地改变,我们就要使用引用了,这个时候,$one和$two实际上是指向了同个内存块,一变俱变:
+
+代码:
+$one = 1;
+$two =& $one;
+$one = 5;
+echo $two;
+
+现在显示5: 我们改变变量$one,实际上也同时改变了$two。
+
+把符号“=” 改成 “=&” 意味着 '引用'. 针对对象来说,如果你要复制一个对象,与原来的对象没有关联,用“=”,如果要使用两个变量指向同一个对象,就使用“&=", 这时候,一个变量作出的任何改变都会影响到别个变量。
+
+
+在CI'超级对象'中加入你自己的代码
+
+你可以为CI'超级对象'加上你自己的代码。假定你已经写了一个名为Status的model, 它有两个属性:$one和 $two, 构造函数分配两个值给他们:$one = 1 和 $two = 2。 当你装载这model时,让我们来看看会发生什么。
+
+instance类有一个变量叫做load, 用来引用对象CI_Loader。 因此你在你的控制器中写的代码是:
+
+$this->load->model($status);
+
+换句话说,调用当前CI“超级对象”的类变量load的model方法,装载一个model, 这个model的名称存放在变量$status中. 让我们看一下保存在/system/libraries/loader.php)中的model方法:
+
+ function model($model, $name ='') {
+ if ($model == '') {
+ return;
+ }
+ $obj=& get_instance();
+ $obj->_ci_load_model($model, $name);
+ }
+
+(这个函数里的变量$name是你要装载的model的一个别名。 我不知道为什么要使用一个别名,也许它会用在其他的 namespaces 中。)
+
+就象你看到的,model实例是被类变量引用的。 因为 get_instance()是一个单一实例的方法,你总是针对同一个实例进行操作。
+
+如果你再运行控制器, 把我们的 '检查' 代码来显示类变量, 你现在将会见到这个类实例包含两个新的属性,$one的$two:
+
+["status"]=> object(Status)#12(14){["one"]=> int(1)["two"]=> int(2)... (等等)
+
+换句话说, CI'超级对象' 现在包括一个对象叫做$status, 它包含了我们刚定义的两个变量,并被赋以我们给定的值1和2。
+
+因此我们正在逐渐地创建一个大的 CI'超级对象', 允许你使用它的某些方法和属性,而不担心它们来自哪里,或处于什么 namespace 中。
+
+这是需要 CI 箭符号的理由。 为了要使用一个model中的方法, 你一定先装载model到你的控制器中:
+
+$this->load->model('Model_name');
+
+这使model被装载入当前控制器类的实例中,也就是$this->中。你随后可以调用控制器中的model对象中的方法, 像这样:
+
+$this->Model_name->function();
+
+就行了。
+
+
+CI'超级对象'的问题
+
+当Rick刚开始开发CI时,为了让CI在PHP4和PHP5下行为一致,他必须在Base4文件中使用比较丑陋'的代码,不管丑不丑,我们不用关心,只要CI能够在PHP4环境下工作得和PHP5一样好就行了。
+
+有其他二个话题值得在这里提一下:
+
+。 你可以尝试开发一个不是现成的对象并让它参与工作
+
+。 你必须小心地架构成你的网站, 因为你不能从别一个控制器里调用某个控制器里的方法
+
+让我们一个一个地来分析这二个问题。 你记得我提到的T恤衫那件事吗?在调用一个成员函数时我一直收到“企图调用一个非对象的成员函数”的出错信息,这个出错信息产生的原因一般是因为你调用了一个类方法,但是忘了装载这个类。换句话说,你写了下列语句:
+
+$this->Model_name->function();
+
+但是忘记在它之前调用:
+
+$this->load->model('Model_name');
+
+还有一些其它的情形,比如,你在类的一个方法中装载了model, 然后你企图在另一个方法里调用model的成员方法,虽然在同一个对象中,这样做也不行。所以最好的方法是在类的构造函数中装载model, 然后可以在这个类的所有方法中使用。
+
+问题也可能更严重。 如果你写你自己的类, 举例来说,你可能想要使用这个类存取数据库, 或在你的 config 文件中读取信息, 换句话说,让这个类存取CI超级对象的某些部分。(如何装入你自己的类和libraries会在第 13 章讨论。) 概括起来,除非你的新类是一个控制器,一个模型或视图,它不能在CI 超级对象中被构造。 因此你不能在你的新类中写这样的代码:
+
+$this->config->item(base_url);
+
+这不会工作, 因为对你的新类来说, $this-> 意味着它本身, 而不是 CI 超级对象。取而代之地,你必须通过调用Instance类用另一个变量名(通常是 $obj)把你的新类加入CI超级对象:
+
+$obj =& get_instance();
+
+现在你能象调用CI超级对象一样地调用它:
+
+$obj->config->item('base_url);
+
+而且这次它能够工作。
+
+因此,当你编写你的新类时,记得它有它自己的标识符。 让我们使用一个较简短的例子来把这个问题讲得更清楚一点。
+
+你想要写一个library 类, 用向你的服务器发出页面请求的URL查找它的地理位置。 这个library类有点象netGeo类,你可以在下列网址找到它:
+
+http://www.phpclasses.org/browse/package/514.html
+
+这个类使用一个switch函数,根据URL的地域分派不同的网页,比如来自英国和美国的URL请求,你就返回一个英语网页,德国和奥地利的URL请求就返回一个德语网页等等。现在,完整的URL会分成二个部分:基本URL(www.mysite.com/index.php)和附加的URL部分(mypage/germanversion)。
+
+你需要从 CI 的 config 文件取得基本URL部分。后半段网址通过你的新类的构造函数中的swith语句生成,如果这个客户在德国,调用德国页函数,依次类推。当这个工作在构造函数中做完以后,你需要把结果放到一个类变量中,所以可以在同一个类的其它函数中使用,这意味着:
+
+。 基本URL从CI config文件中取得,这个只能通过CI超级对象的引用获得,换句话说,你可以用$obj->config->item('base_url');获得
+
+。 URL的后半部分由你的新类的构造函数生成,并写到一个类变量中:$base. 这个变量与CI超级对象无关,它属于你的新类, 被引用为$this->base
+
+装载时会用到两个关键词:$this-> 和 $obj->, 在同一段代码中被引用,举例来说:
+
+class my_new_class {
+
+ var $base;
+ My_new_class() {
+ $obj= & get_instance();
+
+ // geolocation code here, returning a value through a
+ // switch statement
+ // this value is assigned to $local_url
+ $this->base =$obj->config->item('base_url');
+
+ $this->base .= $local_url;
+ }
+}
+
+如果你不清楚这些概念,就会成为频繁出现“调用非对象的成员函数"的原因. 举例说,如果你试着调用$obj->base或 $this->config->item()时,这个出错信息就出现了。
+
+转向剩余的问题, 你不能从一个控制器内部调用别一个控制器的成员方法。 你为什么会想要这样做? 这视情况而定。在一个应用中,我在每个控制器内部写了一系列自我测试函数, 如果我调用 $this->selftest(), 它完成了各种不同的有用测试。但是在每个函数中重复代码似乎与OO编程的设计原则不符,因此我想在其中一个控制器中写一个函数,可以进入到其它的控制器中执行自我测试代码。当我这样做了,期望得到想要的结果。结果,当然不能如我所愿,因为在一个控制器内不能调用另一个控制器的成员方法。
+
+作为一个准则,如果你有代码被超过一个控制器调用的话,把它放入一个model或者其它什么分离的代码文件中,这样就可以使用了。
+
+这些都是小问题。 正如Rick告诉我的一样:
+"我想要简化问题,所以我决定创建一个大的控制器对象包含需要的很多对象实例...当一个用户创建他们自己的控制器时,他们能够轻松地访问任何资源,不用担心作用域的问题"。
+
+这样做相当不错,绝大多数情况下,CI超级对象有效率地,完全处在幕后完成工作。因此我不必要去做那件T恤衫了。
+
+摘要
+
+我们已经看到CI创建的'超级对象' 确保所有的方法和变量可以自动地获取而不用担心如何管理以及为“作用域”操心。
+
+CI 用引用实例的方法把一个一个类实例组合成一个超级对象。大多数情况下,你不需要知道CI超级对象是如何工作的,只需要正确使用“->"符号就行了。
+
+我们也学习了如何编写自己的类,并使它与CI很好地协同工作。
+
+下一章我们学习:
+
+使用 CI 测试代码
\ No newline at end of file
diff --git a/_docs/8.doc b/_docs/8.doc
new file mode 100644
index 0000000..013f766
Binary files /dev/null and b/_docs/8.doc differ
diff --git a/_docs/8.txt b/_docs/8.txt
new file mode 100644
index 0000000..5443c33
--- /dev/null
+++ b/_docs/8.txt
@@ -0,0 +1,399 @@
+使用 CI 测试代码
+
+这一章介绍 CI 如何帮助你测试代码。 测试是一个应用程序的核心。 我们用它测试其他的远程应用程序; 我们也想要测试它自己, 因为它本身也是代码。 CI 使测试变得很容易。
+
+不过, '测试' 的含义很广泛, 因此我们从两种主要的测试类型之间的差异开始,分析一下你应该如何开展测试工作。
+
+我们来看看 CI 类如何帮你测试代码:
+
+。 单元测试
+
+。 基准测试
+
+。 profiler
+
+。 CI提供你在数据库还没数据的时候进行测试的方法
+
+为什么测试, 为谁测试?
+
+有关测试已经有很多的文献。 它已经变成一种必要的技术。 复杂程序需要一支测试员组成的队伍来测试软件。 而且 '测试驱动开发' 的概念是你在写下你的第一行代码之前首先设计你的测试程序, 然后把你编写的代码交给它们。
+
+与此相反的另一个极端,许多程序员不做任何的系统测试。因为测试似乎太困难,烦人并且花费大量时间。也许我们会做几下测试,然后希望其它的都工作正常。
+
+CI 提供一些方法使测试变得容易。 或者可以说-更有乐趣。
+
+有二种主要的测试类型:
+
+。 单元测试: 采取'由下而上的' 的方法。 他们查看你的一个代码块,比如说一个函数,把一些变量放进去,看看它是否返回正确的结果。
+
+。 端到端测试: 这些是'由上而下的'。 他们把重心集中在某件事上,看系统能不能做到。举例来说,他们试着登录到你的网站(使用一个有效的用户名称和密码) 看看系统是否正常工作。 (曾至他们会试着使用一个无效的密码登录…)
+
+如你看到的,它是一种不同的理论。 单元测试, 不关心测试结果; 而完整测试只关心结果,不考虑代码是否正常工作。
+
+重要的事情是要考虑你为什么要测试。什么让你最担心? 什么最有可能出毛病而且使你困窘? 你希望从你的测试中获取什么信息?仅仅是好或不好,还是更多的细节? 对每个应用, 你能负担多少时间编写测试代码并进行测试?
+
+我们正在开发我们的测试网站,不过当我们编程时,我们需要测试我们的代码。 当然,我们试着预期用户要做的每件事, 和可能出现的每种情形。 单元测试在很多方面是有用: 设计测试方法帮助你改善代码的设计。
+
+一旦我们的代码上传到一个服务器上,它的数据量一天一天地增大超过我们的控制。最坏的事情是导致客户发现错误信息或者空白的荧屏, 而且期待你在忙着其它事情时来解决问题。这就是为什么我们正在创建这个网站, 去测试其他的远程网站。
+
+CI 能帮助我们检测网站,看是否发生如下的事情:
+
+第一, 我们已经预期多种可能出问题的情况。 举例来说,我可能通过ID来做一个数据库查询,删除记录。是的,它可以工作: 我实际这样操作同时也是测试它。 但是发生了什么事:如果-不知何故-代码在调用一张不存在的表?或是ID号有问题? 或根本没有什么ID值? 这是单元测试有帮助的地方。
+
+第二, 当我在别的地方写更多代码的时候, 我的第一部分的代码是否按我的要求工作,或者我已经不经意地修改了一些第一部分代码所依赖的部分?再一次地,给单元一个测试。他们也能定期地帮助我们检查产品服务器(包括它的所有部件,例如:如果数据库在一个分离的服务器, 将不需要做一般性的 'ping' 检测!)
+
+CI 给你许多帮助, 无论你处在什么情况下。 它不提供一个功能测试的类,但是你能使用其他 PHP 代码做这个测试。 但是让我们先看一下CI如何捕获你代码中的错误。
+
+CI 的错误处理类
+
+CI 有它自己的检测和报告错误的系统。 一方面,这些是最简单和最普通的测试: 他们是那些有帮助的(或者令人发怒的)信息:当你正在编写代码时并且它不正常工作的时候,你会看到这些信息。
+
+默认地, CI 在屏幕上显示所有的错误。 另一个选择是不报错; 不显示出错信息会让你无法处理,因此,报错对调试是必要的。 全部的行为由 index.php 文件控制, 样式如下:
+/*
+|---------------------------------------------------------------
+| PHP ERROR REPORTING LEVEL
+|---------------------------------------------------------------
+|
+| By default CI runs with error reporting set to ALL. For security
+| reasons you are encouraged to change this when your site goes live.
+| For more info visit: http://www.php.net/error_reporting
+|
+*/
+ error_reporting(E_ALL);
+
+这是一个 PHP 指令,表示报告所有的错误。 为了要关掉错误报告, 把最后一行改为:
+
+error_reporting(0);
+
+这会适合在产品网站上使用,它抑制所有的出错信息。
+
+CI 有三个函数、 show_error() 、 show_404() 和 log_message(),控制错误如何在你的系统上被显示。 (不同寻常地,这些函数是全局性的: 你不需要装载就能使用他们,用就行了!)。 事实上, show_error() 和 show_404() 通常默认产生; 前一个在屏幕顶端的一个整洁的小 HTML 格式的框子中显示你的错误; 后一个在你企图请求一个不存在的网页时显示一个'404'页。
+
+第三个函数,log_message(), 更有趣。你可能想要开发你自己的错误日志,原因有多种多样的,其中一个是也许因为你不能访问在你的ISP的Apache上的日志文件。 首先, 你需要设定权限确保 /system/logs目录是可读写的。 然后你在config文件中设定logging的级别:
+/*
+|--------------------------------------------------------------------------
+| Error Logging Threshold
+|------------------------------------------------------------------------
+|
+| If you have enabled error logging, you can set an error threshold to
+| determine what gets logged. Threshold options are:
+|
+| 0 = Disables logging
+| 0 = Error logging TURNED OFF
+| 1 = Error Messages (including PHP errors)
+| 2 = Debug Messages
+| 3 = Informational Messages
+| 4 = All Messages
+|
+| For a live site you'll usually only enable Errors (1) to be logged
+| otherwise your log files will fill up very fast.
+|
+*/
+$config['log_threshold'] = 4;
+/*
+
+这样会开启日志。
+
+如果你修改 index.php 关闭错误信息显示并不会使错误日志不工作。 因此你能看到信息,但你的用户看不到。
+
+当你开启日志后, CI 每天产生新的记录文件, 并把信息写入这个文件。 但是小心, 这些记录文件能快速地变得很大。
+
+在实际使用过程中,你可能需要开发在某件事发生时显示特定出错信息的错误处理代码。
+
+CI 的单元测试类
+
+现在让我们开始做一些适当的测试工作: 检测你的代码能否在不同的环境之下工作。
+
+CI 使单元测试类和它的其它类一样简单。 你以这装载它:
+
+$this->load->library('unit_test');
+
+然后, 为每个测试准备三个变量数:
+
+。 $test-实际的测试内容, 一般是一个 PHP 表达式
+
+。 $expected_result-你期待的结果
+
+。 $test_name-你想要显示的测试名称
+
+针对PHP 函数floor() 的二个测试列在下面。(floor()是PHP的取整函数) 注意到第一个预期的结果是正确的; 第二个是错误的。 (一个故意的错误):
+
+ $test = floor(1.56);
+ $expected_result = 1;
+ $test_name = 'tests php floor function';
+ $this->unit->run($test, $expected_result, $test_name);
+ $test = floor(2.56);
+ $expected_result = 1;
+ $test_name = 'tests php floor function';
+ $this->unit->run($test, $expected_result, $test_name);
+
+
+增加:
+
+echo $this->unit->report();
+
+显示结果作为带格式的 HTML:
+
+Test Name tests php floor function
+Test Datatype Float
+Expected Datatype Integer
+Result Passed
+File Name E:\xampplite\htdocs\packt2\system\application\
+ controllers\tests.php
+Line Number 108
+---------------------------------------------------------------
+Test Name tests php floor function
+Test Datatype Float
+Expected Datatype Integer
+Result Failed
+File Name E:\xampplite\htdocs\packt2\system\application\
+ controllers\tests.php
+Line Number 113
+
+如果你想要你的系统分析或者保存它, 使用:
+
+echo $this->unit->result();
+
+返回一个你能使用的二维数组:
+
+Array (
+ [0] => Array
+ ( [Test Name] => tests php floor function
+ [Test Datatype ] => Float
+ [Expected Datatype] => Integer
+ [Result] => Passed
+ [File Name] => E:\myfile.php [Line Number] => 69 )
+ [1] => Array
+ ( [Test Name] => tests php floor function
+ [Test Datatype ] => Float
+ [Expected Datatype] => Integer
+ [Result] => Failed
+ [File Name] => E:\myfile.php
+ [Line Number] => 73 )
+)
+
+
+因此现在我们有一个得到测试结果的简单方法。
+
+除了简单的等不等于这样的测试(floor(1.56)是不是等于 1?) CI的单元测试类也测试数据类型 (is_string 、 is_bool 、 is_true, 等等。-完整的清单在用户手册中:
+
+你可以把下面的表达式:
+
+ $expected_result=1;
+
+替换成:
+
+ $expected_result = 'is_float';
+
+测试过程和前面的一样。
+
+如果你在你的密码各处放上这样的测试代码,它可能会运行得很慢, 而且将会在你的屏幕上显示所有的诊断。 但是你能让它停下来。 只要简单地把下列的代码加入你的构造函数:
+
+ $this->unit->active(FALSE);
+
+而且 (令人惊喜地) 如果你将FALSE改成TRUE, 信息将再度显示,你甚至能动态地这样做。
+
+什么时候使用单元测试
+
+事实上很少有人会去测试一个PHP的内置函数。但是用来测试你自己的函数是有价值的。观察它们是否可以返回正确的结果,需要担心的主要有:
+
+。 他们的表现完美吗?
+
+。 但是如果用户想在其他环境下运行, 它还能正常工作吗?
+
+。 或你将会写更多的代码, 或修改现有的代码, 造成你自己的函数不能正常地工作。
+
+有时, 出错是是由于编程问题引起的, 所以我们可以用编程来捕捉和修改错误。你能在用不同叁数进行测试的过程中获得乐趣。
+
+让我们回到我们那个运行一个数据库查询删除指定ID值的记录的那个例子。如果下面情况出现它会做什么:
+
+。 ID是NULL,或者没有给出值?(特别地重要地,你可能偶然地删除表中所有的数据。)
+
+。 ID不是一个整数? ("x", 举例来说?)
+
+。 身份证是一个整数, 但是超出范围 (你在你的表中有 1000个记录,但是ID是1001?)
+
+。 ID是一个负整数?
+
+
+诸如此类,想出不同测试条件是有趣的。
+
+在单元测试中把这些参数放进函数, 并看看结果。 当然,结果可能和你预料的一样。第一个情形和第二个情形会报错。 你应该修改以阻止它发生。因此执行后,单元测试不能通过。
+
+我们定义我们要从每个测试中得到的结果,如果结果和我们设定的一样,测试就通过。但是如果在测试过程中,程序抛出一个异常,后面的代码不再执行,我们如何能够让单元测试完成呢?这就要求我们必须先保证程序没有语法上的错误,让函数能够执行所有的代码。毕竟,单元测试不是用来对付语法错误的,这是PHP环境的工作。
+
+上述假设的第三个情况是ID超出范围,这不是一个代码错误,数据库能安全地处理这种情况。但是,你可以在把查询发给数据库之前做一定的检测工作。或者你也可以让它运行一下,因为有可能会使数据库返回一个出错信息,因此你需要用自己的错误信息来代替系统给出的信息,比如“对不起,现在系统正忙,无法提供服务 ”。
+
+
+单元测试的示例
+
+
+让我们编写一些代码测试这个 '删除' 函数。 我已经建立了一个 '删除' 函数 (放在一个model中) 以便我们来测试它, 如果单元测试失败返回$dbvalue上.
+
+if($test == 'yes')
+ {
+ $place = __FILE__.__LINE__;
+ $dbvalue = "exception at $place: sent state value $state to
+ trydelete function ";
+ return $dbvalue;
+}
+
+如果测试是成功的, 一个简单的循环后在$dbvalue返回'OK'. 测试代码很简单。首先,我们建立一个ID值的数组和我们期待的结果。换句话说,如果我们试着删除一个ID值为 '' 或者 'abc' 的身份证,系统应该抛出异常, 如果ID是 1, 或 9999, 系统应该接受它作为一个有效的ID, 它将返回‘OK’.
+
+因此数组的键名是你给定的测试条件而值是你期望函数返回的结果。
+
+$numbers = array(
+ '' => 'exception',
+ 'NULL' => 'exception',
+ 'x' => 'exception',
+ '9999' => 'OK',
+ '-1' => 'exception',
+ '1' => 'OK'
+ );
+
+现在使用下列的代码循环传递$numbers数组的每个元素给CI的单无高度类来做所有的测试。
+
+测试将运行 $this->delete() 函数, 记录你要删除的'fred'表中的记录和ID值 ($testkey).
+
+foreach($numbers AS $testkey => $testvalue) {
+ $dbvalue = $this->delete('fred', $testkey);
+ $result .= $this->unit->run(preg_match("/$testvalue/",
+ $dbvalue), 1, $dbvalue);
+}
+
+
+记得, CI 单位测试让你提供三个叁数:
+
+。 $test: 对于每个数组的键, 我们把$testkey作为参数调用删除函数, 数组的键就是给出的ID值。函数返回一个值。(在这里称为$dbvalue)。 我们的$test将使用regex比较那个值, 我们期待它是$testvalue,数组中键对应的值。 (它包括 'OK' 或 'exception'?)
+
+。 $expectedresult 是 '1', 因为如果我们的代码是正确的,我们期待 regex 找到一个匹配。 我们希望 'NULL' 返回一个“exception”而‘1’返回“OK”。
+
+。 $testname: 这一个叁数是可选项: 它是测试返回的字符串, 用来解释我们进行什么测试,用什么参数
+
+你可以从结果中看到, 所有的的测试都返回'passed', 因此我们可以对我们的代码有信心了。 (测试结果和预期结果的数据类型都是整数,即使我们的输入可能不是整数, 因为测试实际上是一个正则表达式的比较, 返回 1 或者 0).
+
+
+充满乐趣, 实际上也相当有用, 测试结果存放在数组中。
+
+例如, 如果ID是一个数字,如果不是一个整数会有什么结果,用上面的测试,试验:
+
+ '3.5' => 'exception',
+
+看看结果是什么?
+
+结果会让你感到惊讶(我也是):这个测试没有得到预期的结果,它显示你的函数将把3.5当作一个函数。 理由是 PHP 做一种松散的'相等' 测试; 如果你在这种情况下要得出正确的结果,你需要把比较设为'严厉'模式。 为了要设定这个模式, 使用:
+
+ $this->unit->use_strict(true);
+
+
+CI 的基准测试类
+
+这个类让你测试从一点运行到另一点所需要的时间。 你在开始测试的地方插入一行代码:
+
+$this->benchmark->mark('here');
+
+在代码的另一行插入:
+
+$this->benchmark->mark('there');
+
+然后你插入第三行,告诉你执行的时间:
+
+$fred = $this->benchmark->elapsed_time('here','there');
+
+然后你能打印结果, $fred, 或做任何你想做的事情。
+
+基准测试可以使用你喜欢的任何的名字,只要它们是不同的, 而且你能设定很多组。你能使用这些测试看看是否你的代码执行需要太多的不正常的时间。 如果你的页面装载时间太长,你可以插入一些基准测试识别符来测试引起延迟的代码块。
+
+对于我们的网站监控应用的测试, 我们对执行时间不是太感兴趣。当我们在英特网上登录进网站时,我们希望他们的速度是可接受的。每次执行时间略有长短其实没有多大意义。然而,如果我们在一些连续地测试同一个追踪基点,我们可能注意到它会有变化: 这会为我们找到问题的原因提供一些线索。 一个数据库查询可能费时很多; 可能是我们的主机工作状态不是很稳定。因此为为了达到我们的目标,我们将会采集 $fred 的内容, 并存放在数据库中。
+
+
+CI 的评测器类
+
+profiler 类非常精彩,你在你的类的一个函数中插入一行代码(它在构造函数中工作,因此放在那里才有意义.) 这行代码是:
+
+$this->output->enable_profiler(true);
+
+如果你改变主意了, 你可以修改它:
+
+$this->output->enable_profiler(false);
+
+插入这行代码得到的回报,就是你能在你的屏幕上得到一个完整的报告。显示CI超级对象和你的控制器的时间花销以及$_POST数组和数据库查询等等所用的时间。在开发阶段,这个特别有用。
+
+如果你加上你自己的基准测试,它也会显示这些。 你必须使用特别的名字命名你的基准测试-他们必须包括在"_start"和"_end"中,每一组的命名都要不一样:
+
+$this->benchmark->mark('fred_start');
+
+并且, 还有:
+
+$this->benchmark->mark('fred_end');
+
+
+你能见到,在这两个基准点之间的时间间隔被表示为'fred'.
+
+用数据库的模拟数据测试
+
+动态网站是围绕着数据库工作的。 如果你正在测试它们,你应该测试你的代码是否能真正修改一个数据库。端到端(双向测试)测试这样做: 举例来说,如果你的测试是你能否用正确的用户名/密码组合登录,你可能需要读一个数据库并实际这样操作。
+
+但是测试你能否更新,插入, 或是删除一个生产数据库上的数据是危险的, 因为它会破坏你的真实数据!
+
+记得: CI 可以声明超过一个数据库, 而且可以容易地在他们之间切换,见第 4 章。 使用这个功能,你可以很容易地建立一个模拟数据库, 然后增加,改变, 并且在它里面删除数据。
+
+你也能使用 CI 建立并且删除表、或可能根据你的主机和权限建立和删除数据库。CI的:
+
+$this->db->query('YOUR QUERY HERE');
+
+函数让你运行任何 SQL 查询, 包括有点像这的:
+
+$this->db->query('create table fred(id INT, name VARCHAR(12),INDEX(id))');
+
+能创建一个新表,或者象:
+
+$this->db->query("insert into fred values (1, 'smith')");
+
+将插入一行数据到fred表中。
+
+因此, 通过几行代码, CI 让你建立模拟数据进行完整的测试,在不需要的时候删除这个模拟数据库。 你可能针对delete()函数进行几次单元测试,看看这个函数在有不同ID值时是否正常工作。
+
+现在你正在超越简单的单元测试。 如果我们进行在我们的表中删除数据的单元测试,我们需要检查这些数据是否真的删除了。这容易地用下面的代码实现, 再使用 CI 的单元测试类。和它的AR类:
+
+$test = $this->db->count_all('fred');
+$expected_result = 0;
+$test_name = 'tests number of entries left in table after unique
+ entry
+
+
+$this->unit->run($test, $expected_result,$test_name);
+
+$this->db->count_all 把计算表中所有的结果,而且我们知道那里现在应该没有任何结果。你能容易地使用这种代码检查'插入' 操作, 看看是否在操作后会有一个记录保存在表中。
+
+因为这是虚拟数据, 我们特别地为测试而生成的,我们完全地知道该期待什么,而且做什么都没有关系。 只是记得在测试后要把系统恢复到产品数据库上,否则会得到奇怪的结果。
+
+
+控制和时间安排
+
+测试是应用程序的心脏, 因此重要的不是要不要测试,而是如何让测试有效。
+
+你应该记得在第 4 章我们有一个建表的SQL文件,有一个表名叫tests,另一个表叫events,每次网站在进行一次测试运行时,它会在tests表中寻找两个字段:frequency和last_done. 如果frequency中的值是“hourly",我们就再检查"last_done,如果当前这个小时内没有做过测试,我们就启动测试,然后更新它的 last_done字段为本次测试的时间。
+
+测试完成后,程序在events表中插入一条记录,这里会插入测试过的网站ID,和各种各样其它的测试结果信息。这一张表提供我们网站和我们的客户网站的统计数据,挑出个体测试, 或一个给定网站的所有测试, 等等。
+
+我们在这一章稍早的时候讨论的benchmark类还有一点没有涉及到: 当你针对一个如似上面谈到的函数进行测试的时候,放入基准点进行计时是一个好主意,你可以得到测试花费多少时间的数据。把时间保存到events表中,那里有一个字段timetaken,这是一个浮点类型放入测试花费的秒数。如果定期地评估测试用时可能会得出很多结论,比如如果花时较长,有可能是ISP超负荷运行,或者你的网页有太多的内容,还或者是你的网站变得十分出名,流量大增,你需要增加你的带宽。
+
+不管哪种方法, 经常地测试会让你及早地发现事故苗子,及早地排除故障。
+
+摘要
+
+我们已经在测试上度过许多时间。 它不是最让人激动的内容, 但是如果你们正在开发网站, 测试是保证你晚上睡个安稳觉的好方法。
+
+我们已经见到 CI 如何处理错误, 如何显示出错信息,但是当你的网站正式开始运行后你可以关闭它们(或者停止日志操作)。
+
+我们看了如何操作CI的单元测试工具。 我们也看了benchmark,它能简单地统计任一代码块之间的运行时间。
+
+profiler是一个在你编写代码时用来显示许多信息的相当棒的工具。CI提供了一系列开发测试你代码的好工具。
+
+我们还学习了使用模拟数据库调试数据库交互部分代码的方法,它不会破坏你的生产数据库。
+
+我们还能CI整合一些外部代码, 让我们创建网页机器人进行远程网站的完整功能测试。
+
+
+使用 CI 进行通信编程
diff --git a/_docs/9.doc b/_docs/9.doc
new file mode 100644
index 0000000..680e78b
Binary files /dev/null and b/_docs/9.doc differ
diff --git a/_docs/9.txt b/_docs/9.txt
new file mode 100644
index 0000000..16946a7
--- /dev/null
+++ b/_docs/9.txt
@@ -0,0 +1,380 @@
+使用 CI 通信
+
+英特网的主要优点是它的通信能力。 这一章介绍CI在三个方面使通信更容易。
+
+首先,我们将会通过使用CI的FTP类存取远程目录中的文件,来增加测试工具包。
+
+然后, 我们将会使用电子邮件类在某些条件满足时自动地给我们发邮件。
+
+最后,我们将会尝试进入web 2.0 的领域,使用XML-RPC创建一个个人WEB服务,允许我们的远程网站响应并返回来自我们测试网站的信息。
+
+
+使用FTP类测试远程文件
+
+FTP是在英特网上传输文件的方法。 它通常使用一个特别的FTP程序来上传或下载网站的文件。对我们大多数人来讲,它只是在创建一个新的网站时偶尔用到。
+
+不过你能使用CI轻松地实现全部过程。一种用途是检查你远程网站的完整性:文件还在那里吗?作为一个网站拥有者,你必须面对某人针对你的网站文件的一些特别企图。你的ISP或服务器管理员可能误删你的文件。(我本人就遇到过一次, 我的ISP重建服务器时忘记重新安装我的应用文件)。这个文件不经常被用到,但是它与系统有很大的关系。这导致我花了一些时间去追踪这个有趣的错误!)
+
+作为一个体现CI的FTP类威力的例子,让我们建立一个常规测试程序, 用来检查远程网站的文件。 有些代码是我们每个人都需要的:
+
+function getremotefiles($hostname, $username, $password) {
+ $this->load->library('ftp');
+ $config['hostname'] = $hostname;
+ $config['username'] = $username;
+ $config['password'] = $password;
+ $config['debug'] = TRUE;
+ $this->ftp->connect($config);
+ $filelist = $this->ftp->list_files('/my_directory/');
+ $this->ftp->close();
+ return $list;
+}
+
+
+首先,如果还没有装载请先装载FTP类。 然后,定义设置叁数: 主机名称(比如, www.mysite.com) 、访问FTP使用的用户名和密码。
+
+一旦连接建立,CI 的FTP类给你一些选项。 在这种情况下,我们使用list_files() 返回在/my_directory/目录下的文件清单。函数返回一个数组。而且你能容易地检查包含文件的数组以发现你要查找的文件。在这之前,我们正在尝试在一个数据库中列出我们所有的测试。 因此这次我们需要列出FTP URL的清单(或者主机名称) 、用户名和密码, 没有使用正则表达式而是一个用于检出的存放文件的数组。为了确保数组的完整性,如果你要把它保存在数据库中,你需要在保存前对它进行序列化,取出后需要反序列化。
+
+比较由getremotefiles()函数返回的$remotearray和从你的数据库返回的反序列化的 $referencearray:
+
+function comparefiles($remotearray, $referencearray) {
+ $report = " On site, not in reference array: ";
+ $report .= print_r(array_diff($remotearray, $referencearray), TRUE);
+ $report .= " In reference array, not on site: ";
+ $report .= print_r(array_diff($referencearray, $remotearray), TRUE);
+ return $report;
+}
+
+PHP的array_diff函数比较两个数组。因此,它将会列出在第一个数组存在但第二个数组中不存在的文件, 因此, 运行这个函数两次, 颠倒叁数的次序: 那样,你能得到两个清单,一个是不在你网站上的文件清单(但是应该在),一个是在你网站上文件清单(但是不应该在)。你一个目录显示了你的ISP可能误删的那些文件。第二个清单是那些我们应该加入的文件。
+
+CI的FTP类应该允许你上载,移动更名和删除文件。假如你的测试显示你的参考文件数组中的一个文件丢失了(让我们假定它的文件名是 myfile.php),你可以FTP类来上传它:
+
+$this->ftp->upload('c:/myfile.php','/public_html/myfile.php');
+
+在这个例子中,第一个参数是本地的路径, 第二个参数远程网站的路径。 可选择地,你能在第三个叁数中指定文件应该如何被上传 (作为ASCII或binary) 如果你不选, CI会根据文件的后缀名自己决定。 如果你正在运行PHP5, 你能增加第四个叁数设定文件权限, 这第四个参数用在你正在上传到一个Linux服务器。
+
+要非常小心那个删除选项。 就象用户手册中说的那样, "它将会递归地删除指定路径中的每样东西,包括子目录和所有的文件",甚至写这一个段落都已经使我紧张。
+
+使用FTP的删除函数和上传函数的一个组合,你能自动地在你的远程网站上更新文件。列出你需要更新的文件, 而且依次访问每个网站,首先划除旧的文件, 然后上传每个新的。
+
+还有一有趣的 'mirror' 函数,让你在另外的一个服务器上建立网站的一个完全副本。
+
+如果你正在运行 PHP5 ,FTP类还有一个让你改变文件权限的函数。
+
+正如你见到的,从测试你的远程网站到实际上维护或更新他们涉及到你的应用的许多方面。 你可以,举例来说,写代码自动地进行更新。
+
+
+机器之间的对话--XML-RPC
+
+WEB 2.0 带来的革命是巨大的,体现在建立电脑对电脑的接口,允许mashup和APIs以及其它的好东西。
+
+这是'网络服务'的基础。 你能提供一个接口给你的网站,允许其它人用它为他们服务。举一个简单的例子, 如果你建立一个'网络服务'对华氏温度进行转换,变成摄氏温度, 客户用一个叁数 (要转换的温度) 送出一个请求,而服务器返回被转换的值。因此, 任何人都能增加一个温度转换功能在他自己的网站上, 但是实际上是调用你的服务。
+
+XML-RPC 让二部电脑直接地对话。接收网站创建一个简单的API(应用程序接口)。任何一个想要与它交互的人需要知道那个API-什么方法是可调用的, 它们需要什么叁数, 语法是什么-为了访问它们。举例来说,许多主要的网站使用这个系统: Google ,让你通过公布的API直接调用它的搜索引擎或Google地球。
+
+建立你自己的个人API相对来说很容易, 对 CI 说声谢谢吧。你需要建立二个网站来测试它,让它成为一件比大多数事情复杂一点的事情吧。一个网站(让我们称它为 '接收'网站) 提供API,侦听请求, 并回答它们。 (在我们的例子中,这是我们正在尝试测试并管理的远程网站之一。) 另一个网站使用API取回答案。(在我们的例子中,这就是测试网站本身。)
+
+在XML-RPC协议,这二个网站通过高度结构化的XML进行对话。 (名字XML-RPC由此而来-它是XML Remote Procedure Call的宿略词) 客户将一个XML包送到 '接收网站'服务器, 告诉它想要使用的函数并传入一些参数。 服务器解码XML, 如果它符合API的要求, 调用函数并返回一个结果,封装成客户可以解码和使用的XML。
+
+你的API由接收网站提供的函数组成,并指导如何使用他们-举例来说, 他们需要什么叁数,这些参数应该是什么数据类型, 等等。
+
+在接收方,我们创建一个XML-RPC 的服务器,确保内部的函数能为外部网站所用。这些'内部方法'实际上是你的控制器里面的正常的函数: 服务器的角色是处理外部的调用和内在的函数之间的接口。
+
+当你建立一个XML-RPC流程的时候 , 有二组问题:
+
+。 获取需要对话的这二个网站
+
+。 确定数据以一个适当的格式被传输
+
+两者都充分利用多维数组, 电脑能够十分容易地理解他们, 虽然人脑反而会对多维数组感到有一点迷惑。 CI 使它变得比较容易-虽然要做得正确还需要相当地睿智。
+
+
+使XML-RPC的服务器与客户交互
+
+首先,你必须在远程网站上建立一个服务器, 和在请求网站上的一个客户机。这些通过几行代码就可以做到。让我们假定我们正在一个叫做mycontroller的控制器(在接收网站上)中建立服务器和在叫做 'xmlrpc_client' 的控制器中建立客户(在请求网站上)。
+
+在每个网站上,通过在构造函数中初始化CI类。分二步; 对于客户端,你只需要装载第一行, 在服务器端你需要装载这两行:
+
+$this->load->library('xmlrpc');
+$this->load->library('xmlrpcs');
+
+现在, 在服务器一方,关闭你的构造函数, 并且在 'mycontroller'控制器的index() 函数里面,定义供处部调用的函数。 你通过建立一个“functions”子数组(在CI的$config主数组中),映射进入的请求名和你实际要使用的函数:
+
+$config['functions']['call']= array('function'=>'mycontroller.myfunction');
+
+$config['functions']['call2']= array('function'=>' mycontroller.myfunction2');
+
+在这个例子中,有二个被命名的函数叫做-'call' 和 'call2'. 这就是request所请求的。(它不需要函数的名称, 而是需要调用名。 如果你愿意,当然,你能使用相同的名字。) 对于每个调用,你定义一个子子数组给控制器里面的函数-例如:分别是'myfunction' 和 'myfunction2'。
+
+你然后通过设定它初值并且实例化完成服务器的设定:
+
+ $this->xmlrpcs->initialize($config);
+
+ $this->xmlrpcs->serve();
+
+而且现在它已准备好侦听请求。
+
+现在你需要去另一个网站-客户网站-并且建立一个XML-RPC的客户来发出请求。 这应该在你的客户网站上的一个单独的控制器。 它相当短:
+
+ $server_url='http://www.mysite.com/index.php/mycontroller';
+ $this->load->library('xmlrpc');
+ $this->xmlrpc->set_debug(true);
+ $this->xmlrpc->server($server_url, 80);
+ $this->xmlrpc->method('call');
+
+
+你定义接收网站的URL,指定包含你想要的XML-RPC 的服务器的控制器。 你装载XML-RPC 的类,定义服务器, 和你想要调用的方法-这是你定义的调用的名字,不是函数的真实名称, 如果你正在调用的函数需要叁数,你这样传递它们:
+
+ $request = array('optimisation','sites');
+
+就象你见到的, 我们正在传递二个参数。
+
+然后, 如果一个结果已经返回, 处理它:
+
+ if (! $this->xmlrpc->send_request()) {
+
+ echo $this->xmlrpc->display_error();
+ } else {
+ print_r($this->xmlrpc->display_response());
+ }
+
+最简单的做法是显示它; 但是在一个真正的应用中你更有可能想要机器分析它,比方说,通过使用正则表达式, 然后计算出结果。 举例来说,如果结果包含一个错误信息,你可能想要在你的数据库中记录错误, 而且采取行动向某个用户报告此事。
+
+格式化XML-RPC数据交换
+
+让我们使用一个真实的, 也比较简单的例子。在这一节中,我们将会创建一个XML-RPC调用/响应,让你远程启动一个数据库的优化操作。
+
+我们在上面写的客户端, 正在申请调用一个方法名为 'call' 并带有两个参数: 'optimisation' 和 'sites'。
+
+接收网站的服务器把这个名为'call'的请求和一个实际的控制器中的函数'myfunction'建立起一个映射。
+
+让我们大致看一下这个函数。 它基本上是在控制器里面的一个平常的函数。 它尝试优化一张 MySQL 数据库的表、并根据优化结果返回 'success' 或 'failure'。
+
+function myfunction($request) {
+
+ $paramters = $request->output_parameters();
+ $function = $paramters['0'];
+ $table =$parameters['1'];
+ if ($this->db->query("OPTIMIZE TABLE $table")) {
+ $content = 'success';
+ } else {
+ $content = 'failure';
+ }
+ $response = array(
+ array(
+ 'function'=> array($function, 'string'),
+ 'table' => array($table, 'string'),
+ 'result' => array($content, 'string')
+ ),
+ 'struct'
+ );
+ return $this->xmlrpc->send_response($response);
+}
+注意$request,设定作为函数的参数。这包含来自客户的$quest的数组, 它有二个值, 'optimisation' 和 'sites'。 CI 已经把数组转换成一个对象, $request。因此你不能把它当做数组来处理,你必须使用$request对象的$request->output_parameters() 方法。这返回一个原先的数组。
+
+利用这个,我们已经告诉接收网站上的函数我们需要优化的表名, 'sites'表。 我们也已经告诉它该调用函数'optimisation')。它还带有一个叁数叫做'result', 获得所有的值后返回这三个值。
+
+返回到客户网站的结果看起来有点像这:
+
+
+
+
+
+
+
+
+ function
+
+ optimisation
+
+
+
+ table
+
+ sites
+
+
+
+ result
+
+ Success
+
+
+
+
+
+
+
+
+(实际上没有缩进: 我这样做是为了使结构变得更清楚。)
+
+正如你看到的,我们的简单的三个词的结果(optimisation、exercises,success) 已经被装进如此复杂的分层标签中,在一定程度反应出XML可悲之处, 精确地地告诉一部机器收到了什么。有三个 标签对。 每个有一个 对. ('function','table','results') 而且每个都有 对, 包含了我们实际需要的信息(连同数据类型)-也就是 'optimisation','sites', 'success'。
+
+不必介意我是否喜欢它。 计算机因这种东西而有收获: 一部机器需要精确、不含糊、和容易的数据特性。 这一个章节是讲解计算机之间如何交谈的事的, 不是有关如何方便真正的人类的。
+
+现在,在你的客户网站上的XML-RPC客户函数能够解析出收到的数据。 它可以方便地使用正则表达式来做到这一点,因为每个答案都清楚地被XML标签标示着。
+
+注意 CI 如何让你免写许许多多的尖括号。
+
+调试
+
+一旦你开始测试你的客户/服务器组合, 你或许会得到这样的信息:
+
+The XML data received was either invalid or not in the correct form for XML-RPC. Turn on debugging to examine the XML data further.
+
+加入下列代码打开调试功能:
+
+$this->xmlrpc->set_debug(true);
+
+在你的客户端。 这让你完整地看到你接收的信息。被警告,这是调试功能获取失败原因的地方。
+
+有几个地方容易出错:
+
+。 远程网站没有正确回应。 (你可能暂时设定它显示错误,目的是为了要找出它为什么没有回应的原因。如果它是一个活动的网站,这是令人感到懊恼的。另外的,如果捕获 22 是它将会显示 HTML格式的错误信息, 返回不是你的客户端网站期望的XML格式信息,因此,你们将会收到由第一组所引起的第二组错误信息… ) 对这个情况除错这可能涉及许多FTP来回传输,直到你达成正确的结果。
+
+。 客户端代码可能工作不正常。
+
+。 你把网址弄错了。(XML_RPC 服务器上要求以CI的方式来定位控制器-也就是 http://www.mysite.com/index.php/mycontroller。 如果你把所有的服务器代码放入控制器的构造函数而非在index()函数中,它会正常运行, 但是你需要给出你要调用的函数的名称-比如:
+http://www.mysite.com/index.php/mycontroller/myfunction)
+
+。 XML信息交换可能不完全正确。 set_debug 函数让你了解什么正在被返回 , 但是你能需要花好长时间盯着屏幕试着找出哪里出了问题。 (相信我…)
+
+然而,一旦调试正确了, 你就会很有成就感。 你已经在远程网站上创建了一个函数,可以让你远程调用它。
+
+换句话说,你已经建立一个能维护和操作远程网站的应用程序。 如果你有一些元程网站需要维护,你能容易地复制这些去访问它们, 允许你 (举例来说) 每天一次在本地网站上优化你的数据库中的表。
+
+XML-RPC 带来的问题?
+
+安全当然是一个问题。 你应该密码保护你的函数调用, 所以客户在收到网站响应前必须发送一个密码作为一个叁数。 这能通过发送密码作为请求的一个另外的叁数来做到, 并且让被调用的函数在响应之前检查它是否正确。
+
+如果你正在暴露重要的函数,你可能想要所有的事情在 SSL 层后面发生。 我们的例子看起来无害处-如果一个电脑黑客反复地闯入到你的网站,你可能不介意, 每次他所能做的就是让你的数据库表变得更“整洁”。 另一方面, 它会为“拒绝服务攻击”创造条件。
+
+必须承认即使有CI的帮助,XML-RPC容易出错,不易设置和调试。 你需要一次性针对两个网站编程和调试,而且在它们之间传输数据的XML格式要求非常苛刻。 甚至最小的错误不都不可以犯。
+
+有人主张XML-RPC 是一种必须被替代的技术, 可以使用其它高级语言编写的更新的接口或者APIs,像是SOAP(创建更费时)。
+
+然而,以我们的观点,XML-RPC 是理想的。它让我们没有细节烦恼地运行远程网站中复杂的内部函数。
+
+
+与人交流的工具: 电子邮件类
+
+我们已经在我们的测试网站中放入了许多代码。我们有一个测试的数据库,并且我们已经构建了许多函数进行不同类型的测试。我们能存取我们的网站和检查我们浏览的网页; 我们能检查所有的文件是不是如我们所希望的正常地存放在远程服务器上。我们能自动地在网站上运行函数并让它优化自身。写使用这些工具进行测试的代码非常简单,如果我们需要, 我们可以登入或可以使用一些定期执行软件,以适当的时间间隔运行我们的程序。
+
+但是只是充分地进行测试并且仅仅在一个数据库中的保存结果是不够的。 如果发生了故障,我们需要尽快地知道。
+
+这就需要CI的电子邮件类上场了,每当特定的条件满足,它允许我们编制程序,把电子邮件寄给我们。 你可能想要为每个失败的测试寄一封电子邮件, 或者你可能想要进行一系列的测试,收集结果, 然后再寄一封电子邮件报告。
+
+使用电子邮件类, 首先(一如往常)你必须装载它。
+
+$this->load->library('email');
+
+然后我们必须设定一些config参数。因为这个类仰赖它运行的服务器能够提供电子邮件的收发功能,所以我们可能在这里陷入麻烦。再一次,我们需要检查 ISP是否提供这个功能。(因为 Xampplite ,举例来说,可能不能够提供你一个邮件服务器,也因此在本地测试也会很困难。)
+
+然而,如果我们已经确认ISP提供相应的电子邮件服务,我们能容易地配置电子邮件类。 有许多选项,在用户手册有详细的列表。 主要是:
+
+。 协议: 你的系统使用mail, sendmail还是SMTP发送电子邮件?
+
+。 mailpath: 你系统的邮件程序保存在哪里?
+
+你应该这样设定他们:
+
+$config['protocol'] = 'sendmail';
+
+$config['mailpath']='/usr/sbin/sendmail';
+
+$this->email->initialize($config);
+
+其他的选项, 全部都有有意义的默认值, 包括像字自动换行,字符集,个性组, text/HTML,等等。设定可选项并使用它们正常工作是使用这个类唯一有点困难的部分。
+
+一旦你已经装载类并且进行了初始化,使用它简单得不可思议:
+
+$this->email->from('david@mysite.com');
+$this->email->to('someone@myownsite.com');
+$this->email->bcc('fred@somewhere.com');
+$this->email->subject('Test message');
+$this->email->message('Hello world');
+$this->email->send();
+
+将发送一封电子邮件寄给我,并抄送给我的客户,报告我想要的信息。
+
+如果你需要发送超过一封电子邮件, 在开始新的电邮前:
+
+$this->email->clear();
+
+只是确定你每次从一个干净的版式开始。
+
+你也能使用电子邮件类发送附件。 记住:附件一定要已经保存在发送电子邮件的服务器中, 而且你必须指定路径,而且是绝对路径
+.
+给出路径和文件名,如同下列:
+
+ $path = $this->config->item('server_root');
+ $file = $path.'/my_subdirectory/myfile.htm';
+
+然后仅仅增加这一行:
+
+ $this->email->attach($file);
+
+放在这一行的前面
+
+$this->email->send();
+
+这简单的 CI 函数比直接用PHP编写容易得多,它处理所有牵涉到的协议, 你甚至不需要知道他们的存在。
+
+如果你包含这行:
+
+$result = $this->email->print_debugger();
+
+在你的代码中,打印出$result, 你将会得到一个有用的屏幕信息,如:
+
+protocol: mail
+User-Agent: Code Igniter
+Date: Wed, 18 Apr 2007 13:50:41 +0100
+From:
+Return-Path:
+Bcc: fred@somewhere.com
+Reply-To: "david@mysite.com"
+X-Sender: david@mysie.com
+X-Mailer: Code Igniter
+X-Priority: 3 (Normal)
+Message-ID: <462614219c1a6@upton.cc>
+Mime-Version: 1.0
+Content-Type: multipart/mixed; boundary="B_ATC_462614219d14d"
+This is a multi-part message in MIME format.
+Your email application may not support this format.
+--B_ATC_462614219d14d
+Content-Type: text/plain; charset=utf-8
+Content-Transfer-Encoding: 8bit
+test message
+hello world
+--B_ATC_462614219d14d
+Content-type: text/html; name="myfile.html"
+Content-Disposition: attachment;
+Content-Transfer-Encoding: base64
+(等等)
+
+如果哪里出了问题,那么侦错数据也将会返回任何的服务器错误信息。 举例来说,如果在使用SMTP发送方式没有设定适当的主机或权限:
+
+ $config['protocol']='smtp';
+
+它不能发送信息,并且它会告诉我:
+
+You did not specify a SMTP hostname
+Unable to send email using PHP SMTP. Your server might not be configured to send mail using this method.
+
+不过请记住, 'sendmail'可能正在产生误导-如果它已经发送邮件到服务器上并返回一个成功信息, 但是这并不表示邮件实际上被发送。 (因为如果你设定错误的 'mailpath' 选项, 'sendmail' 可能报告它已经发出电子邮件, 但它实际上没有.) CI 依赖邮件改善程序返回的信息,因此,它可能被愚弄。 对电子邮件来说, 检查它们已发出的唯一的方法是确定他们已经到达-这就不是这里讨论的主题了。
+
+CI 的电子邮件类中还包括一些有用的选项,全部在用户手册中有说明。 举例来说,你能设定它发送本文或者 HTML 格式邮件-如果你选择HTML格式,这个函数还允许你设定一个备用的文本信息给那些不接受 HTML 电子邮件的人。
+
+你也能设定它使用不同的字符集, 而且处理单词回绕。 你能设定批处理的尺寸,如果你想要群发电子邮件到一个长长的电子邮件清单中, 你的服务器将不能接受。 (或者你的ISP会认为你是个垃圾邮件制造者.)
+
+
+摘要
+
+我们现在已经用 CI 为我们的网站提供丰富功能建立了一些非常复杂的工具。
+
+首先,我们用 CI 的FTP类简化了文件传输。 最初, 我们用这个类检查指定的文件是不是正常存放在指定的网站上, 或者意外地增加了文件,这是很有价值的检查, 因为网站可能因为文件的变更而遇到问题, 有时候是因为网站管理员造成的,有时候是由黑客造成的。这函数将定期地执行检查。CI的FTP类也提供远程维护并且更新网站信息的可能性。
+
+然后我们用 CI 的XML-RPC 类开发我们自己的'web services'。这样允许我们调用一个远程网站上的函数, 如有需要就传以参数, 并且把结果返回给我们-就好像我们登录到远程网站而不是我们的测试网站。我们把它用在了优化远程网站的数据库上并返回报告给我们。再一次,我们已经超越我们最初只是要监控远程网站的目标。现在我们能够教他们检查和优化他们自己。
+
+最后,我们使用了 CI 的电子邮件类,这让我们的测试网站能生成电子邮件。 CI的代码极其简单明了容易使用, 如果它认为出现了问题,我们的网站就会发电子邮件通知我们。 CI 使得建立并且发送电子邮件很简单, 包括发送附件。
+
+下一章我们将学习 CI 如何帮助提供动态数据
\ No newline at end of file
diff --git a/_docs/CodeIgniter.pdf b/_docs/CodeIgniter.pdf
new file mode 100644
index 0000000..1ad8a3c
Binary files /dev/null and b/_docs/CodeIgniter.pdf differ
diff --git a/_docs/codeigniter.txt b/_docs/codeigniter.txt
new file mode 100644
index 0000000..a7f82eb
--- /dev/null
+++ b/_docs/codeigniter.txt
@@ -0,0 +1,10828 @@
+CodeIgniter for Rapid PHP
+Application Development
+
+Improve your PHP coding productivity with the
+free compact open-source MVC CodeIgniter
+framework!
+
+
+
+
+David Upton
+
+
+
+
+BIRMINGHAM - MUMBAI
+
+
+CodeIgniter for Rapid PHP Application Development
+Improve your PHP coding productivity with the free compact
+open-source MVC CodeIgniter framework!
+
+
+Copyright 漏 2007 Packt Publishing
+
+
+
+All rights reserved. No part of this book may be reproduced, stored in a retrieval
+system, or transmitted in any form or by any means, without the prior written
+permission of the publisher, except in the case of brief quotations embedded in
+critical articles or reviews.
+
+Every effort has been made in the preparation of this book to ensure the accuracy of
+the information presented. However, the information contained in this book is sold
+without warranty, either express or implied. Neither the author, Packt Publishing,
+nor its dealers or distributors will be held liable for any damages caused or alleged to
+be caused directly or indirectly by this book.
+
+Packt Publishing has endeavored to provide trademark information about all the
+companies and products mentioned in this book by the appropriate use of capitals.
+However, Packt Publishing cannot guarantee the accuracy of this information.
+
+
+
+First published: July 2007
+
+
+
+Production Reference: 1160707
+
+
+
+Published by Packt Publishing Ltd.
+32 Lincoln Road
+Olton
+Birmingham, B27 6PA, UK.
+
+ISBN 978-1-847191-74-8
+
+www.packtpub.com
+
+Cover Image by Vinayak Chittar (vinayak.chittar@gmail.com)
+
+
+ Credits
+
+Author Project Manager
+ David Upton Abhijeet Deobhakta
+
+
+Reviewers Indexer
+ Rick Ellis Bhushan Pangoankar
+ Derek Allard
+ Proofreader
+Development Editor Chris Smith
+ Douglas Peterson
+ Production Coordinator
+Assistant Development Editor Shantanu Zagade
+ Nikhil Bangera
+ Cover Designer
+Technical Editor Shantanu Zagade
+ Ajay S
+
+
+Editorial Manager
+ Dipali Chittar
+
+
+ About the Author
+
+David Upton is a director of a specialized management consultancy company,
+based in London but working around the world. His clients include some of the
+world's largest companies. He is increasingly interested in web-enabling his work,
+and seeking to turn ideas into robust professional applications by the simplest and
+easiest route. He has so far written applications for two major companies in the UK.
+His other interests include simulation, on which he writes a weblog that takes up far
+too much of his time, and thinking.
+
+
+I鈥檇 like to thank Rick Ellis for writing CI and for making it available,
+free. This spirit of generosity with such valuable intellectual
+property is what makes the Open Source movement a success, and
+an example to the rest of us.
+
+I鈥檇 also like to thank Rick, and Derek Allard, for undertaking a
+technical review of the book and making many helpful suggestions.
+
+Mark Barker inspired and helped me to understand Object
+Orientation, during many Saturday evening 鈥榞eek-outs鈥.
+
+Lastly, but not least, my thanks to Julia, John, and James for their
+love, support, and patience.
+
+
+ About the Reviewers
+
+Rick Ellis is the founder and CEO of EllisLab.com, the company that develops
+CodeIgniter and several other widely used web applications. Rick Ellis has a diverse
+background in media technology, having worked in creative and technical capacities
+on interactive projects for Disney, to feature films for Oliver Stone, and almost every
+kind of web-based project in-between.
+
+
+
+Derek Allard is a programmer, author, and award-winning instructor based
+in Toronto, Canada. He builds web applications, is a standards and accessibility
+supporter, a scripting and database guy, and a PHP junkie. A sought-after educator
+and freelancer, Derek spends most of his time working with XHTML, PHP, XML
+and JavaScript.
+
+As a highly visible CodeIgniter community member, Derek was hired by EllisLab as
+the Senior Technical Support Specialist. He devotes time to ensuring CodeIgniter
+and their flagship content management system, ExpressionEngine, remain
+market leaders.
+
+He blogs about all things web at www.derekallard.com.
+
+
+
+
+ Table of Contents
+Preface 1
+Chapter 1: Introduction to CodeIgniter 7
+ What can CodeIgniter Do for You? 7
+ Save Time 8
+ Make Your Site More Robust 9
+ Keep Your Links Up-To-Date Automatically 9
+ Save Database Crashes: 'prep' Your Data Entry Forms 10
+ Make Your Code Bolder 11
+ Send Email Attachments without Hassles 11
+ Save Bandwidth by Zipping Files That Users Need to Download 12
+ Yes, But鈥hat is CodeIgniter? What are Frameworks? 12
+ And Who is That Man? 14
+ The 'Open Source' Business Model 15
+ What CI Doesn't Do 16
+ License 18
+ Summary 19
+Chapter 2: Two Minutes' Work: Setting up a CodeIgniter Site 21
+ Prerequisites 21
+ Installing CodeIgniter 22
+ Exploring the File Structure 23
+ The Configuration File 24
+ Does it Work? 25
+ Summary 25
+Chapter 3: Navigating Your Site 27
+ MVC鈥擩ust Another Acronym? 28
+ The Structure of a CI Site: Controllers and Views 29
+ The Welcome Controller 31
+ Working with Views 32
+
+
+Table of Contents
+
+ The Default Controller 33
+ CodeIgniter Syntax Rules 33
+ Controller 34
+ View 34
+ Types of Files or Classes on a CI Site 34
+ What are All Those Folders For? 37
+ Designing a Better View 37
+ Designing a Better Controller 39
+ Getting Parameters to a Function 40
+ Passing Data to a View 41
+ How CI Classes Pass Information and Control to Each Other 43
+ Calling Views 43
+ Calling Functions Directly 43
+ Interacting with Controllers 44
+ It's Just Like an Egg-Cup 45
+ An Example of a CI Helper: the URL Helper 46
+ A Simple Library Example: Creating a Menu 48
+ Summary 49
+Chapter 4: Using CI to Simplify Databases 51
+ Configuration Settings 51
+ Designing the Database for Our Site 52
+ Active Record 53
+ Advantages of Using the Active Record Class 54
+ Saving Time 54
+ Automatic Functionality 54
+ Read Queries 56
+ Displaying Query Results 58
+ Create and Update Queries 59
+ Delete Queries 61
+ Mixing Active Record and 'Classic' Styles 61
+ Summary 62
+ Chapter Appendix: MYSQL Query to Set Up 'websites' Database 63
+Chapter 5: Simplifying HTML Pages and Forms 67
+ Writing a View 67
+ Long and Short PHP Syntax 69
+ Nesting Views 70
+ Practical Issues of Site Architecture 73
+ CI's Form Helper: Entering Data 74
+ Form Helper Advantage One: Clarity 74
+ Form Helper Advantage Two: Automation 77
+
+ [ ii ]
+
+
+ Table of Contents
+
+ My 'Display' Model 78
+ CI's Validation Class: Checking Data Easily 79
+ Set Up Validation 80
+ Set Up the Controller 81
+ Set Up the Forms 81
+ Summary 83
+Chapter 6: Simplifying Sessions and Security 85
+ Starting to Design a Practical Site with CI 85
+ Moving Around the Site 86
+ Security/Sessions: Using Another CI Library Class 91
+ Turning Sessions into Security 94
+ Security 96
+ Summary 98
+Chapter 7: CodeIgniter and Objects 99
+ Object-Oriented Programming 99
+ Working of the CI 'Super-Object' 100
+ Copying by Reference 103
+ Adding Your own Code to the CI 'Super-Object' 105
+ Problems with the CI 'Super-Object' 106
+ Summary 109
+Chapter 8: Using CI to Test Code 111
+ Why Test, and What For? 111
+ CI's Error Handling Class 113
+ CI's Unit Test Class 115
+ When to Use Unit Tests 117
+ Example of a Unit Test 118
+ CI's Benchmarking Class 121
+ CI's Profiler Class 122
+ Testing with Mock Databases 123
+ Control and Timing 124
+ Summary 125
+Chapter 9: Using CI to Communicate 127
+ Using the FTP Class to Test Remote Files 127
+ Machines Talking to Machines Again鈥擷ML-RPC 129
+ Getting the XML-RPC Server and Client in Touch with Each Other 131
+ Formatting XML-RPC Exchanges 132
+ Debugging 134
+ Issues with XML-RPC? 135
+ Talking to Humans for a Change: the Email Class 136
+ Summary 139
+
+ [ iii ]
+
+
+Table of Contents
+
+Chapter 10: How CI Helps to Provide Dynamic Information 141
+ The Date Helper: Converting and Localizing Dates 142
+ Working with Text: the Text Helper and Inflector Helper 145
+ Going International: the Language Class 146
+ Making HTML Tables the Easy Way: the Table Class 150
+ Caching Pages 152
+ Summary 154
+Chapter 11: Using CI to Handle Files and Images 155
+ The File Helper 156
+ The Download Helper 158
+ The File Upload Class 160
+ CI's Image Class 165
+ Easy File Compression with the CI Zip Class 169
+ Summary 169
+Chapter 12: Production Versions, Updates, and Big Decisions 171
+ Connections: Check the Config Files 172
+ URLs 172
+ Databases 172
+ Other config Files 173
+ Look Out for PHP 4/5 and Operating System Differences 173
+ Diagnostic Tools 174
+ Coping with Changes in New CI Versions 177
+ How to Load Models, and What to Call Them 178
+ How to Initialize Your Own 'library' Classes 179
+ So Should I Update If a New CI Version Comes Out? 179
+ How to Add On to CI's Basic Classes 181
+ Summary 183
+Chapter 13: Instant CRUD鈥攐r Putting it All Together 185
+ The CRUD Model: Design Philosophy 186
+ The Standard Controller Format 187
+ The Database Tables 189
+ The Heart of the Model: the Array 189
+ Function by Function: the CRUD Model 192
+ Showall 192
+ Reading the Data 195
+ Delete and Trydelete 196
+ Insert 201
+ Insert2 208
+ The Test Suite 209
+ Summary 214
+
+ [ iv ]
+
+
+ Table of Contents
+
+Chapter 14: The Verdict on CI 215
+ Some Code: the 'do_test' Model 216
+ A Balance Sheet 225
+ Where CI Helped: Structure 225
+ Where CI Helped: Simplicity 226
+ Where CI Helped: Extra Functionality 226
+ Problems with CI 226
+ Completeness 227
+ Ease of Use 227
+ Summary 228
+Chapter 15: Resources and Extensions 229
+ CI's User Forums 230
+ Video Tutorials 232
+ Available Plug-ins and Libraries 232
+ AJAX/Javascript 233
+ Authentication 233
+ External Sites 235
+ Comparisons: Which Charting Library to Use? 235
+ CRUD: the Final Frontier 238
+ Resources for Other Programmes, e.g. Xampplite, MySQL, PHP 239
+ Summary 240
+Index 241
+
+
+
+
+
+
+Preface
+
+This book sets out to explain some of the main features of CI. It doesn't cover them
+all, or cover any of them in full detail. CI comes with an excellent on-line User Guide
+that explains most things. This is downloaded with the CI files.
+
+This book doesn't try to duplicate the User Guide. Instead it tries to make it easier for
+you to pick up how the CI framework works, so you can decide whether it is right
+for you, and start using it quickly.
+
+In some places, this book goes beyond the User Guide, though, when it tries to
+explain how CI works. (The User Guide is more practically oriented.) This means
+that there are some fairly theoretical chapters in between the "here's how" pages. I've
+found that it helps to understand what CI is doing under the hood; otherwise you
+sometimes get puzzling error messages that aren't easy to resolve.
+
+I've tried to use a 'real-world' example when showing sections of CI code. I want
+to show that CI can be used to develop a serious website with a serious purpose.
+I'm currently running several websites for clients, and I want a program that will
+monitor them, test them in ways I specify, keep a database of what it has done, and
+let me have reports when I want them.
+
+The examples in this book don't show it in full detail, of course: but they do, I hope,
+demonstrate that you can use CI to make pretty well any common coding simpler,
+and some uncommon stuff as well.
+
+This book steps you through the main features of CodeIgniter in a systematic way,
+explaining them clearly with illustrative code examples.
+
+
+What This Book Covers
+
+Chapter 1 explains what CodeIgniter can do, the 'framework', and how CodeIgniter
+fits in. It further talks about the open-source business model and gives some
+disadvantages of CodeIgniter, at the end.
+
+Chapter 2 explains what happens when you install the site, and which files will be
+created. It gives a detailed overview of the required software, and explains the basic
+configuration of CodeIgniter.
+
+Chapter 3 explains how MVC helps to organize a dynamic website. It goes further
+to explain the process by which CodeIgniter analyzes an incoming Internet request
+and decodes which part of your code will handle it. Then CodeIgniter syntax rules
+and the different types of files or classes you can find鈥攐r write for yourself鈥攐n a
+CodeIgniter site are explained. At the end of the chapter, some practical hints on site
+design are given.
+
+Chapter 4 looks at how you set up a database to work with CodeIgniter, and then
+how you use the Active Record class to manipulate the database.
+
+Chapter 5 covers various ways of building views, how to create HTML forms quickly,
+and how to validate your forms using CodeIgniter's validation class.
+
+Chapter 6 looks at one of the basic questions affecting any website i.e. session
+management and security; we also explore CodeIgniter's session class.
+
+Chapter 7 covers the way in which CodeIgniter uses objects, and the different ways in
+which you can write and use your own objects.
+
+Chapter 8 covers CodeIgniter classes to help with testing: Unit tests, Benchmarking,
+the 'profiler' and ways in which CodeIgniter helps you to involve your database in
+tests without scrambling live data.
+
+Chapter 9 looks at using CodeIgniter's FTP class and email class to make
+communication easier, and then we venture into Web 2.0 territory using XML-RPC.
+
+Chapter 10 talks about CodeIgniter classes that help in overcoming problems arising
+regularly when you are building a website, for example, the date helper, the text and
+inflector helpers, the language class, and the table class.
+
+Chapter 11 looks at several useful CodeIgniter functions and helpers: file helper,
+download helper, file upload class, image manipulation class, and the ZIP class.
+
+Chapter 12 covers exploring your config files, using diagnostic tools, and potential
+differences between servers, along with some notes on security.
+
+Chapter 13 shows you how to generalize CRUD operations so that you can do them
+with two classes: one for the controller, and one for the CRUD model.
+
+Chapter 14 looks at some coding examples, bringing together a lot of the functions
+that have been discussed bit by bit in the preceding chapters.
+
+Chapter 15 looks at some of the resources available to you when you start to code
+with CodeIgniter, such as the libraries for AJAX and JavaScript, authentication,
+charting, and CRUD.
+
+
+
+What You Need for This Book
+
+Throughout this book, we will assume that you have the following packages
+installed and available:
+
+ PHP 4.3.2 or above
+ 鈥
+ A working web server
+ 鈥
+ One of MySQL, MySQLi, MS SQL, Postgre, Oracle, SQLite, ODBC
+ 鈥
+
+
+
+Conventions
+
+In this book, you will find a number of styles of text that distinguish between
+different kinds of information. Here are some examples of these styles, and an
+explanation of their meaning.
+
+There are three styles for code. Code words in text are shown as follows: "We can
+include other contexts through the use of the include directive."
+
+A block of code will be set as follows:
+ $active_group = "default";
+ $db['default']['hostname'] = "";
+ $db['default']['username'] = "";
+ $db['default']['password'] = "";
+
+When we wish to draw your attention to a particular part of a code block, the
+relevant lines or items will be made bold:
+
+
+
+
+
+
+
+New terms and important words are introduced in a bold-type font. Words that you
+see on the screen, in menus or dialog boxes for example, appear in our text like this:
+"clicking the Next button moves you to the next screen".
+
+
+ Warnings or important notes appear in a box like this.
+
+
+
+
+Reader Feedback
+Feedback from our readers is always welcome. Let us know what you think about
+this book, what you liked or may have disliked. Reader feedback is important for us
+to develop titles that you really get the most out of.
+
+To send us general feedback, simply drop an email to feedback@packtpub.com,
+making sure to mention the book title in the subject of your message.
+
+If there is a book that you need and would like to see us publish, please send
+us a note in the SUGGEST A TITLE form on www.packtpub.com or
+email suggest@packtpub.com.
+
+If there is a topic that you have expertise in and you are interested in either writing
+or contributing to a book, see our author guide on www.packtpub.com/authors.
+
+
+
+Customer Support
+Now that you are the proud owner of a Packt book, we have a number of things to
+help you to get the most from your purchase.
+
+
+
+Downloading the Example Code for the
+Book
+Visit http://www.packtpub.com/support, and select this book from the list of titles
+to download any example code or extra resources for this book. The files available
+for download will then be displayed.
+
+The downloadable files contain instructions on how to use them.
+
+
+
+
+Errata
+Although we have taken every care to ensure the accuracy of our contents, mistakes
+do happen. If you find a mistake in one of our books鈥攎aybe a mistake in text or
+code鈥攚e would be grateful if you would report this to us. By doing this you can
+save other readers from frustration, and help to improve subsequent versions of
+this book. If you find any errata, report them by visiting http://www.packtpub.
+com/support, selecting your book, clicking on the Submit Errata link, and entering
+the details of your errata. Once your errata are verified, your submission will be
+accepted and the errata added to the list of existing errata. The existing errata can be
+viewed by selecting your title from http://www.packtpub.com/support.
+
+
+Questions
+You can contact us at questions@packtpub.com if you are having a problem with
+some aspect of the book, and we will do our best to address it.
+
+
+
+
+ Introduction to CodeIgniter
+Most of us just want to write applications that work well, and to do it as simply and
+easily as we can. This book is about CodeIgniter, a tool for making PHP easier to use.
+
+If you need to produce results, if you think that the details and intricacies of coding
+are for geeks, then you should look at CodeIgniter (CI to its friends).
+
+CI is free, lightweight, and simple to install, and it really does make your life much
+easier. Just read this chapter to find out how:
+
+ What CI can do for you
+ 鈥
+ What is a 'framework' and how does CI fit in?
+ 鈥
+ The open-source business model
+ 鈥
+ Some disadvantages of CI (no, it's not perfect)
+ 鈥
+
+
+
+What can CodeIgniter Do for You?
+If you are already writing code in PHP, CodeIgniter will help you to do it better, and
+more easily. It will cut down on the amount of code you actually type. Your scripts
+will be easier to read and update. It will help you to give large websites a coherent
+structure. It will discipline your coding and make it more robust, in some cases
+without you even knowing it.
+
+That's quite a big claim. You have already spent some time learning PHP, HTML,
+CSS, a database, and several other acronyms' worth of geek speak. You need a basic,
+but not necessarily an expert, knowledge of PHP to benefit from CI.
+
+
+Introduction to CodeIgniter
+
+CodeIgniter is not for you if:
+
+ You don't have a reasonable knowledge of PHP and HTML.
+ 鈥
+ You want to write a basic Content Management System (CMS) quickly and
+ 鈥
+ simply, with a minimum of coding. (Look at a product like
+ Expression Engine.)
+ You only want to write simple websites with a few standard features.
+ 鈥
+
+
+Save Time
+CI doesn't take long to learn, and it quickly pays for your effort in the time saved
+later on. Let's look at a simple measure:
+
+How CI cuts down the amount of code you need to type.
+
+This is not just good for the lazy. The less you type, the fewer mistakes you make,
+and the less time you spend debugging your code. The smaller your code is, the
+faster it loads and less space it takes up.
+
+Here are two examples (which are explained later on in this book, so don't worry
+now about how they work!).
+
+Imagine you are writing a database query. This is how you might write a function
+within your PHP programme to query a MySQL database:
+
+$connection = mysql_connect("localhost","fred","12345");
+mysql_select_db("websites", $connection);
+$result = mysql_query ("SELECT * FROM sites", $connection);
+while ($row = mysql_fetch_array($result, MYSQL_NUM))
+{
+ foreach ($row as $attribute)
+ print "{$attribute[1]} ";
+}
+
+Now see how a CI function would handle a similar query:
+
+$this->load->database('websites');
+$query = $this->db->get('sites');
+foreach ($query->result() as $row)
+{
+ print $row->url;
+}
+
+Compare the character counts: 244 for the traditional syntax; 112 for CI.
+
+
+Now let's imagine that you are writing a data entry form in HTML, and you want
+a drop-down query box. Let's say this drop-down query box shows three options
+and allows the user to select one of them. In HTML, a drop-down box can be created
+like this:
+
+
+
+CI's version is both shorter and, because it works from an array, more adapted to
+PHP processing:
+
+$urlarray = array(
+ '1' => 'www.this.com',
+ '2' => 'www.that.com',
+ '3' => 'www.theother.com',
+ );
+
+$variable .= form_dropdown('url', $urlarray, '3');
+
+In HTML, you need to type 154 characters; in CI, 128.
+
+
+Make Your Site More Robust
+Although you don't need to write as much code, CI provides a lot of the standard
+functionality for you, and remembers all those oddities and quirks. It keeps track
+of things you may have forgotten all about. (Those little touches that distinguish
+amateur sites from professional ones鈥)
+
+
+Keep Your Links Up-To-Date Automatically
+Imagine that you've just written a menu page, with lots of hyperlinks to other pages
+in your site. They are all in the traditional HTML format:
+
+say hello to Fred
+
+Then, you decide to move the site to another URL. That means you have to go
+painstakingly through your code, looking for each URL, and re-writing it, or else
+none of your links will work.
+
+CI gives you a simple function to write hyperlinks like this:
+
+echo anchor(start/hello/fred, Say hello to Fred);
+
+CI also encourages you to put the URL of your site in a configuration file that the
+rest of your site can access. CI's anchor function that we've used here automatically
+refers to that configuration file. So, when you come to move your site, you only
+need to change that one entry in the configuration file, and all your hyperlinks
+update automatically.
+
+
+Save Database Crashes: 'prep' Your Data Entry Forms
+
+Data entry is fraught with problems. Because of limitations of HTML and databases,
+data that contain certain symbols鈥 for example, apostrophes and quotation marks鈥
+may cause your database to crash or to give results you did not expect.
+The answer to this is to prepare or 'prep' your data in your data entry form, before it
+is submitted to the database. All this takes time and a certain amount of extra coding.
+CI's form helper does this, automatically. So, when you create an input box by typing:
+
+echo form_input('username', 'johndoe');
+
+You're also getting the hidden benefit of:
+
+ function form_prep($str = '')
+ {
+ if ($str === '')
+ {
+ return '';
+ }
+
+ $temp = '__TEMP_AMPERSANDS__';
+
+ // Replace entities to temporary markers so that
+ // htmlspecialchars won't mess them up
+ $str = preg_replace("/(\d+);/", "$temp\\1;", $str);
+ $str = preg_replace("/&(\w+);/", "$temp\\1;", $str);
+
+ $str = htmlspecialchars($str);
+
+ // In case htmlspecialchars misses these.
+ $str = str_replace(array("'", '"'), array("'",
+ """), $str);
+
+ // Decode the temp markers back to entities
+ $str = preg_replace("/$temp(\d+);/","\\1;",$str);
+ $str = preg_replace("/$temp(\w+);/","&\\1;",$str);
+
+ return $str;
+ }
+
+This is code that handles special characters like '&'; so that they don't cause confusion
+while your form is being submitted. As you can see, there is some quite tricky regex
+code in there.
+
+Possibly you like typing out regexes. Some people like lying on beds of nails, some
+like listening to ABBA; it's a free country. (Well, it is where I'm writing this.) But
+if you don't like these things, you can let CI do them for you (the regexes, I mean,
+not ABBA), and you needn't even be aware of the code that's working away in the
+background for you, every time you write that one simple line of code:
+
+echo form_input('username', 'johndoe');
+
+
+
+Make Your Code Bolder
+CI also makes it easy to do things you might not have tried before. Of course, PHP
+users can always integrate libraries from PEAR and other sources, but these aren't
+always easy to integrate, or use, and their syntax and standards differ greatly. CI has
+a common set of standards, and once you've mastered its syntax, all its parts work
+together without complication. All its code is well-written and reliable, and is tested
+out by its user community. It puts much more sophistication in your hands.
+
+Let's look at two examples to illustrate this point.
+
+
+Send Email Attachments without Hassles
+Sending emails is a complex business. CI's code for doing it looks easy to follow:
+
+$this->load->library('email');
+$this->email->from('your@your-site.com', 'Your Name');
+$this->email->subject('Email Test');
+$this->email->message('Testing the email class.');
+$this->email->send();
+
+There are a number of issues involved in sending emails: setting word-wrapping
+(and escaping it so long URLs don't get wrapped and broken up) for example, or
+sending attachments. The standard PHP functions can get quite complex here, and
+the result is that many code writers are tempted to avoid using these functions if
+they possibly can.
+
+CI's email class makes it simple to send an attachment. You write:
+
+$this->email->attach('/path/to/photo1.jpg');
+
+CI does the rest. Working behind the scenes, for example, is a function that sorts out
+MIME types for nearly hundred different types of attachment. So it knows that
+your photo, photo1.jpg, is an 'image/jpeg' MIME type. It remembers to generate
+
+boundary delimiters in the right places around your attachments. It takes care of
+wrapping your text, and it allows you to easily mark out chunks of text you don't
+want wrapped.
+
+
+Save Bandwidth by Zipping Files That Users Need
+to Download
+To save bandwidth, it's a fairly common practice to compress or 'ZIP' files before
+you download them. That's not something I've ever done, and I wouldn't know how
+to go about it. On the other hand, CI has a nice facility that allows you to produce
+zipped files with four lines of code:
+
+$name = 'mydata1.txt';
+$data = 'the contents of my file..........';
+$this->zip->add_data($name, $data);
+$this->zip->archive('c:/my_backup.zip');
+
+Run this, and you find a ZIP archive on your C drive containing one file. Your ZIP
+filer reader will unzip it and produce the original data for you.
+
+People who use your site won't know that you've produced this impressive result
+so easily. They'll be impressed! Your site will save bandwidth. You did it in minutes
+rather than hours.
+
+
+
+Yes, But鈥hat is CodeIgniter? What are Frameworks?
+Shortly after programming was invented, someone noticed that it involved many
+repetitive operations. And shortly after that, someone else鈥攎aybe it was Ada
+Lovelace, spanner in hand, adjusting Babbage's differential engine, or maybe it was
+Alan Turing at Bletchley Park鈥攄ecided to modularize code, so you only had to
+write certain chunks once, and could then re-use them. PHP programmers are used
+to writing separate chunks of code in functions, and then storing those functions in
+include files.
+
+At one level, a framework is just that: lots of chunks of code, stored in separate files,
+which simplify the coding of repetitive operations.
+
+
+In the examples above, connecting to the database or building HTML form elements
+are abstracted and simplified for you. You call a function in the framework, which is
+easier to handle than the original code.
+
+It goes beyond that. Writing code involves continuous choices between the many
+ways of tackling the same problem; so most frameworks also impose a set of choices
+on you. They've started to handle the problem one way, so you have to go that way
+as well. If these are sensible choices, this makes your life much simpler too. (If not,
+it's like trying to write a sales brochure using Excel, or do cash-flow projections using
+Word. Both can probably be done, but neither is the best use of your time.)
+
+Sensible design decisions make sure that the things you need are accessible, but
+prevent them from spilling over into each other. A good framework makes those
+decisions for you, starting you off with a sensible foundation for your program and
+guiding you through the next steps.
+
+Mention frameworks nowadays, and people think of Ruby on Rails.
+
+Rails has become the success story of the last year or so, because it apparently offers
+effortless and rapid website development, with a minimum amount of coding.
+Essentially, it is a structure and a set of tools, built for use with the Ruby language,
+that allow you to build certain types of Ruby programs more quickly. It's not the
+only framework for Ruby, but it is very effective and, deservedly, very popular. On
+the other hand, if you have put in the time and effort to learn PHP, starting over
+again in Ruby is a long haul.
+
+There are several frameworks available for PHP as well. CI is only one of about 40.
+They include the Zend framework, Cake, Trax, and others. There's a handy chart
+at http://www.phpit.net/article/ten-different-php-frameworks/ that
+compares ten of the most popular.
+
+If you look at them, you'll notice that postings on their user forums get very
+heated about which framework is the best. The truth seems to be that each has its
+strengths, and none is without its own weaknesses. My touchstone is that I'm busy;
+so frameworks should save me time, and having found one that works for me, I am
+sticking to it. That's why this book is just about CI.
+
+
+And Who is That Man?
+
+CI was written by Rick Ellis, rock musician turned programmer. Rick makes his
+living as CEO of pMachine, which sells an excellent content management system
+called Expression Engine. In January 2006, he wrote on his blog, http://www.
+ellislab.com :
+
+"鈥 I spent a couple weeks researching and installing PHP frameworks, really
+banging on quite a few of them, and I was absolutely dismayed. I discovered that
+most frameworks suffer from these problems:
+
+ They have horrid, terrible documentation, if it exists at all.
+ 鈥
+ They make an endless number of assumptions regarding your knowledge
+ 鈥
+ and skill level, and generally expect you to figure it all out.
+ They are written for people who have root server privileges and can change
+ 鈥
+ system settings.
+ They assume that you have access to the command line. In fact, many do not
+ 鈥
+ work if you can't bash out commands.
+ They tend to require lots of dependencies, like the PEAR libraries or various
+ 鈥
+ open source ones.
+ They tend to be needlessly complex to use, with obtuse syntax, XML based
+ 鈥
+ templates, and other features that are simply not necessary for most web
+ applications.
+ They are either ponderously big, or too minimalist to be useful.
+ 鈥
+ The most current frameworks only run on PHP 5, which at present only has a
+ 鈥
+ 5% adoption rate.
+
+I have yet to find a single PHP framework that is truly, really, actually simple to use,
+is thoroughly documented from top to bottom, natively includes all the tools needed
+to build robust applications, has a browser-based interface, and is designed for your
+average PHP coder, without admin privileges, who uses a standard hosting account.
+Not one. Which leads me to think there's a market for just such a product. 鈥"
+
+The result was CI, written as a spare time project. Rick generously decided to make
+it available, free of charge. In between running his business, he also updates CI from
+time to time. He's also created an excellent forum, where CI users can raise issues
+and share tips, as well as finding (and sometimes solving) bugs in his code. All this is
+available on the CI website at http://www.codeigniter.com/.
+
+Did he meet his own objectives? Read on and judge for yourself鈥
+
+
+
+The 'Open Source' Business Model
+There can be something disconcerting about this sort of software. If you like your
+software with expensive support contracts and a 'big company' name, then CI is not
+for you. (But then, what are you doing using PHP, anyway? PHP users know that
+support, and the development of PHP software, depends partly on the unpaid efforts
+of the 'community'鈥攈undreds or thousands of users.)
+
+There are some problems with community support. Consistency and high quality are
+not 'guaranteed'鈥攁nyone can post to the forum, and sometimes these postings are
+just plain wrong. (Note that if you read the small print on the licence for expensive
+commercial software, quality isn't guaranteed there either.) But with 'open source'
+products, you do have to take an intelligent interest rather than accepting everything
+you read on forums at face value. CI is a framework for people who are able to take
+an intelligent interest.
+
+However, any sensible developer has to wonder if it's wise to invest time and energy
+in a product that is a 'one man band'. Rick Ellis wrote it as a spare time project,
+with some help from his pMachine colleague Paul Burdick. It's free. He makes no
+commitment to maintain it or develop it. He might go back to being a rock musician.
+
+On the other hand, once you've downloaded it, the version you downloaded will
+continue to work. You don't have to rely on upgrades and patches. Rick's coding is
+excellent and there have been few serious bugs in it. If it works for you, then there
+is no reason why it shouldn't continue to work. So far I've only found two cases in
+which my code failed to work, and the fault was a bug in the framework rather than
+in my own coding on top of it. (Both bugs have since been solved.)
+
+
+
+The CI website is the gateway to the community and forums.
+
+
+
+
+What CI Doesn't Do
+There are some things that CI doesn't do. Rick intended CI to be a small and
+'lightweight' framework. (The zipped download for version 1.5 is only 737 KB and
+downloads in seconds. The Zend framework is 10 megabytes.) It's not the answer to
+all the problems you will ever have. But it does:
+
+ Make it easier and quicker to programme in PHP
+ 鈥
+ Structure your site and help you through the architectural decisions.
+ 鈥
+
+
+
+
+One result of being 'lightweight' is that it does not have as many features as some
+of its rivals. Rails has achieved prominence partly because it contains 'scaffolding'
+and 'generators'. These are tools that automatically write certain basic scripts for
+you. So, for example, once you have set up a database, Rails creates 'out-of-the-box'
+web pages to do basic Create, Read, Update, and Delete (CRUD) operations on the
+database tables.
+
+In addition, Rails allows you to write 'generators'鈥攑ieces of code that automatically
+write other basic scripts. The Rails community has created quite a lot of these; so you
+can automatically generate scripts that do all sorts of clever things.
+
+CI doesn't do this. (There is rudimentary 'scaffolding'鈥攕caffolds are templates that
+describe how the application database maybe used鈥 in CI, but as the online manual
+puts it: "Scaffolding is intended for development use only. It provides very little
+security鈥. If you use scaffolding make sure you disable it immediately after you are
+through using it. DO NOT leave it enabled on a live site." Enough said.)
+
+Instead CI concentrates on making basic things easy. Some of the things it handles are:
+
+ Session management and cookies (see Chapter 6)
+ 鈥
+ Database access and queries (see Chapter 4)
+ 鈥
+ Building HTML stuff, like pages and forms, and validating form entries
+ 鈥
+ (see Chapter 5)
+ Testing (Chapter 8)
+ 鈥
+ Communicating on the Internet, using FTP or XMLRPC (Chapter 9)
+ 鈥
+
+Sound familiar? All of these are basic processes, which you will have to go through if
+you're building a dynamic website. CI makes these processes easier, and makes your
+code more likely to work.
+
+
+
+License
+If you are building a commercial application, the license terms for any software you
+are using become critical. (If you are raising venture capital, expect the VC's lawyers
+to go over them in detail.) No problems with CI. It has a very generous licence that is
+downloaded with your files.
+
+Unlike some commercial software I could think of, CI's license even fits on one
+screen. Here it is, in the following screenshot:
+
+
+Summary
+If you already know some PHP and are writing intelligent websites, the CodeIgniter
+framework is all about making your life easier. It helps you
+
+ Save time
+ 鈥
+ Make your site more robust
+ 鈥
+ Achieve more sophisticated coding
+ 鈥
+
+It makes coding fun again, rather than a chore.
+
+There are quite a few frameworks, and not just for the PHP language. All of them
+offer you chunks of pre-written code that make the repetitive or complex processes
+of coding easier, and impose a helpful structure on your site development.
+
+This book does not make any comparisons between frameworks. I've found CI
+works for me, and I want to explain how and why. I hope that's useful to you, and
+that you will be able to save as much time as I did, and enjoy the coding process
+more, as a result.
+
+This book takes you through some of the framework's main features, and tries to
+explain some of what goes on 'under the hood'.
+
+I've used a real-world example for the code illustrations in this book to try
+to show that CI is a serious tool that can be quickly and easily used in a
+demanding environment.
+
+Enjoy!
+
+
+
+
+ [ 19 ]
+
+
+
+
+ Two Minutes' Work: Setting
+ up a CodeIgniter Site
+Setting up the CI package on your web server is easy. This small chapter explains
+what happens when you install the site, and which files will be created. Let's look at:
+
+ What software you require for your development site
+ 鈥
+ Installing the CI files: a simple download and unzip operation
+ 鈥
+ The basic configuration of CI: what the folders are and how they
+ 鈥
+ are organized
+ The initial controller and view that CI installs
+ 鈥
+ Some basic modifications to show how these work
+ 鈥
+
+
+
+Prerequisites
+CodeIgniter is very flexible. It will work equally well with PHP 4.3.2 and above, or
+PHP 5. Since the majority of ISPs still don't support PHP 5, this is useful, and keeps
+down the hosting costs.
+
+You will also need a database. CI's online user guide says: "Supported databases are
+MySQL, MySQLi, MS SQL, Postgre, Oracle, SQLite, and ODBC."
+
+In order to develop and test a dynamic website, you need a web server. Normally,
+you would develop and test your site on a local server, i.e., one that runs on your
+own machine (with the loopback address 127.0.0.1 or localhost) rather than on a
+remote site on the Internet.
+
+
+Two Minutes' Work: Setting up a CodeIgniter Site
+
+If you aren't familiar with the process of setting up a web server, it's easiest to install
+a package such as Xampplite, which installs Apache, PHP, and MySQL on to a
+Windows machine with minimum configuration by you. Xampplite is free, comes
+with comprehensive instructions, and is almost always easy to install. Alternatively,
+some versions of Windows come with their own web server.
+
+It also helps to have a good PHP editor on your system. You can do it all on a
+text editor, but I find that the syntax highlighting feature of a good editor saves
+me from making lots of simple mistakes with unclosed brackets or mismatched
+quotation marks.
+
+Once you've reached this far, I estimate it will take you two minutes to have CI
+running on your system.
+
+
+
+Installing CodeIgniter
+One thing you don't need is your credit card: CI is completely free!
+
+Once your server is set up, go to the CodeIgniter site at
+http://www.codeigniter.com/ and download the latest version of the framework.
+Version 1.5.3, the latest, is only 737KB when zipped, so the download doesn't take
+that long
+
+Unzip the folder, and install the CodeIgniter files in your web root folder. If you are
+using Xampplite, this is usually the htdocs folder within the Xampplite folder.
+
+The CodeIgniter index.php file should be in the root directory. The root folder is the
+folder that you would point at if you navigated to the site鈥攊n this case, by accessing
+http://127.0.0.1. Of the two minutes we need to set up the site, one minute is up!
+
+Included with CI is a comprehensive user guide (in the user_guide folder). You'll
+use this a lot. It is usually clear, and often goes into more detail than this book can.
+So, try it if you get stuck.
+
+When these files are on your machine, you can access them in two ways:
+
+ As a URL鈥攅.g., http://127.0.0.1
+ 鈥
+ Through the normal directory path: e.g.,
+ 鈥
+ C:/xampplite/htdocs/index.php
+
+You should be able to see the CI welcome screen by simply navigating to your URL
+with the browser. It's that simple! The welcome page tells you that what you are
+seeing is built by two files, a view and a controller.
+
+
+
+ [ 22 ]
+
+
+ Chapter 2
+
+
+Exploring the File Structure
+Once you have installed the CI files, have a look at the new directories that have been
+created. Understanding what the different types of files do is critical.
+Your root folder should now look something like the diagram below. If you've ever
+looked at Rails, this structure will look fairly familiar.
+You can divide these folders into three groups:
+ The ones you will populate (e.g., controllers, models, and views: all in the
+ 鈥
+ application folder). Apart from the welcome view and controller that you
+ have just seen, these folders are empty.
+ The files in the system folder are the system code for CI (system/libraries,
+ 鈥
+ system/codeigniter, system/drivers, etc.). You can read them, and alter
+ them if you wish鈥攂ut don't do this until you understand how CI works.
+ And if you alter the base code, remember that you may have to alter it again
+ when you download an update of CodeIgniter, since the new version may
+ overwrite your alterations. You may also find that the new code no longer
+ works with your amendments. Lastly, you may find that what Rick wrote is
+ pretty good as it is.
+ The ones that are half written already, but may need additions or changes
+ 鈥
+ (language, config, errors.) These folders are set to defaults, but you will
+ need to alter your config files right away; so let's get that over with.
+
+
+
+
+ [ 23 ]
+
+
+Two Minutes' Work: Setting up a CodeIgniter Site
+
+
+The Configuration File
+Remember we were going to take two minutes to set our site up? The second minute
+is spent doing some basic configuration.
+
+The config folder contains a group of files that set basic configurations for your site.
+Open the config/config.php file and tell the site where to find itself. The first few
+lines of the file should say something like:
+
+/*
+|------------------------------------------------
+| Base Site URL
+|------------------------------------------------
+|
+| URL to your Code Igniter root. Typically this
+| will be your base URL, WITH a trailing slash:
+|
+| http://www.your-site.com/
+|
+*/
+$config['base_url'] = "http://127.0.0.1/";
+/*
+
+Notice how well CI files are commented!
+
+Alter the values in quotes to match your own web root. If you have a problem, more
+detailed setup instructions are given in the online manual.
+
+As a basic principle, use the config.php file to store information about your site
+rather than scattering it around your files. Firstly, it is easier to update if it's all in
+one place. Secondly, when you transfer your site from the development server to the
+production server, you'll only have to make only one set of changes. Lastly, many CI
+functions assume that certain information is to be found there.
+
+There are other config files in the config folder, but you can safely leave them at
+their default settings for now.
+
+Of the two minutes we needed to set up the site that was the second one. In the rest
+of this chapter, we'll play around with our site.
+
+
+
+
+ [ 24 ]
+
+
+ Chapter 2
+
+
+Does it Work?
+The easy way to see if your site is working is to navigate to it using your
+browser. Assuming you're running it in the root folder of a local server, type in
+http://127.0.0.1 and you should see this:
+
+
+
+
+That means CI is up and running. Did it take you more than two minutes?
+
+
+
+Summary
+In this chapter, we've seen how easy it is to install CI. Once you have a development
+web server set up, all you need to do is download the CI code, unzip it, and copy
+it over.
+
+Then, we looked quickly at the shape of the files we've installed and did some basic
+configuration, and there we were: a working CI site.
+
+If this chapter is disappointingly short, it's because CI is easy to install. Like
+everything else in this book, it's about saving time and making life easier.
+
+
+
+
+ [ 25 ]
+
+
+
+
+ Navigating Your Site
+Now that we've installed CI, we need to understand how it works.
+
+Readers familiar with design patterns will have recognized by now that CI
+implements the Model鈥擵iew鈥擟ontroller (MVC) pattern. This is a method of
+organizing the files that make up a website, or, if you like, of splitting the site into
+sensible parts rather than having one huge lump of code.
+
+In this chapter, we'll look briefly at the theory behind MVC, and then at the way CI
+organizes itself internally. In particular, what goes in those different folders and how
+do they communicate? How is a site structured? And how does CI navigate
+around it?
+
+This chapter looks at:
+
+ How MVC helps to organize a dynamic website
+ 鈥
+ The process by which CI analyzes an incoming Internet request and decides
+ 鈥
+ which part of your code will handle it
+ What the code does then
+ 鈥
+ CodeIgniter syntax rules
+ 鈥
+ The different types of files or classes you can find鈥攐r write for yourself鈥攐n
+ 鈥
+ a CI site
+ How to pass parameters to controllers using the URL
+ 鈥
+ How to write better views and pass dynamic data to them
+ 鈥
+ How a reply is returned to the surfer
+ 鈥
+ How the files or classes pass information and control to each other
+ 鈥
+ How useful code is kept inside helper and library files
+ 鈥
+ Some practical hints on site design.
+ 鈥
+
+
+Navigating Your Site
+
+
+MVC鈥擩ust Another Acronym?
+MVC is a means of organizing a dynamic website. The design pattern has been
+around since 1979 when it was first described by the Norwegian, Trygve Reenskaug.
+Here's an outline of the different types of files:
+
+ Models are objects, which represent the underlying data. They hover above
+ 鈥
+ the database and access it as required. They can also perform operations on
+ data to add meaning to it.
+ Views show the state of the model. They are responsible for displaying
+ 鈥
+ information to the end user. (Although they are normally HMTL views, they
+ might be any form of interface. They might be views specially adapted for
+ small PDA screens or WAP telephones, for example.)
+ Controllers offer options to change the state of the model. They are
+ 鈥
+ responsible for consulting models. They provide the dynamic data to views.
+
+CI has subfolders for models, views, and controllers. Each file within them is a
+.php file, usually in the form of a class that follows certain naming conventions.
+
+CI helps you to follow the MVC pattern, and as a result makes it much easier to lay
+your code out. CI allows you a lot of flexibility, and you get all the advantages of the
+MVC structure.
+
+Try to think in MVC terms as you write. As far as possible, try to keep your 'views'
+focused purely on presentation, and your 'controllers' purely on controlling
+application flow. Keep the application logic in the data models and the database.
+
+This way, if you do decide to create a new set of views for a new display method,
+you don't have to alter much code in any of the controllers or the models. If you
+update some of your 'business logic', you only have to change code in the models.
+
+On the other hand, while this is a very interesting and useful division, it's important
+not to take it too seriously. MVC is intended to help you and not to be a straitjacket.
+Different programs and frameworks implement MVC in slightly different ways.
+The CI forums contain many anguished queries about the 'right' way to implement
+MVC. (Should I do database queries from controllers, or should this only be done
+in models? Can I return a view directly from a model, or should I go through a
+controller first?)
+
+
+
+
+ [ 28 ]
+
+
+ Chapter 3
+
+Rather than trying to achieve the theoretically 'right' result, just keep in mind the two
+useful principles. These are set out in the Design and Architectural Goals section of CI's
+User Guide:
+
+ Loose Coupling: Coupling is the degree to which the components of a
+ 鈥
+ system rely on each other. The less the components depend on each other,
+ the more re-usable and flexible the system becomes. Our goal was a very
+ loosely coupled system.
+ Component Singularity: Singularity is the degree to which components have
+ 鈥
+ a narrowly focused purpose. In CodeIgniter, each class and its functions are
+ highly autonomous in order to allow maximum usefulness.
+
+These were Rick Ellis's objectives in building CI, and they are good objectives for
+your own sites too. Provided that you meet these objectives, it doesn't matter very
+much what your code sections are called.
+
+It does work. My own experience is that 'loose coupled' helpers or libraries
+written for one site can be very easily dropped in to another, saving hours of
+development time.
+
+So, if your controller queries the database directly, or your model calls a view, the
+CI code will work properly鈥攖here's usually no technical issue鈥攂ut your MVC
+interpretation may not be 'correct'. Don't worry, be happy!
+
+
+
+The Structure of a CI Site: Controllers
+and Views
+Your entire CI site is dynamic. That is, there are probably no 'static' pages that you
+can look at as simple HTML code. (You can add some if you wish, but they'll be
+outside the CI framework.) So, where is your site?
+
+When we installed CI, we noticed that the application folder includes subfolders
+called models, views, and controllers. Each response that a CI site produces is
+assembled by these three types of files.
+
+Let's look at the process in detail.
+
+To emphasize the point that we are not dealing with static pages, each with its own
+URL, we will show you the 'URL' request and show how CI interprets it. First of all,
+consider a normal Internet request. A connection is made to the URL of your site,
+www.example.com, and then through the socket comes an HTTP request like:
+
+ GET /folder/file.html HTTP/1.0
+
+
+ [ 29 ]
+
+
+Navigating Your Site
+
+GET is the type of request, HTTP/1.0 is the version of HTTP being used, and
+everything in between is the relative path, and name of your file. But on your site,
+there are no simple static HTML files to be found. Instead, all incoming requests are
+intercepted by the index.php file.
+
+If the user is requesting a page on your site with the correct URL鈥攕ay by clicking on
+a hyperlink on one of your pages鈥攖he request will look more like this:
+ GET /index.php/tests/showall HTTP/1.0
+
+If the user doesn't know the exact URL and just requests www.example.com, then CI
+has a system for setting a default address (We'll see how to do that in a moment.). In
+either case, the steps are:
+
+ Internet request comes in:
+ 鈥淕ET http://127.0.0.1鈥
+
+
+ Router: decides which controller
+ should handle this request
+
+
+ Controller: analyzes the
+ request and responds: maybe Model: provides data
+ by getting data from the model
+
+
+ View: formats the response
+ (in this case as an HTML page)
+
+
+ Page is served up to enquirer
+
+
+
+A request coming in from the Internet to your web root is intercepted by the index.
+php file, which acts as a 'router'. That is, it calls a 'controller', which then returns
+a 'view'.
+
+How does the router know which controller to call? As we've seen, sometimes the
+incoming request tells it. For instance, if the request says:
+ GET http://127.0.0.1/index.php/welcome/index
+
+and if you have a controller called welcome, that's where the request goes.
+
+
+
+
+ [ 30 ]
+
+
+ Chapter 3
+
+The Welcome Controller
+So, let's look at the welcome controller. It's the one controller that has already been
+written and the code is at system/application/controllers/welcome.php. Here's
+what it says:
+
+load->view('welcome_message');
+ }
+}
+?>
+
+You'll see from the second line that this file is a class. Every controller inherits from
+an original Controller class鈥攈ence extends Controller. Within the class are two
+functions or methods鈥擶elcome() and index().
+
+
+ CI requires controller names to start with an uppercase letter (class
+ Welcome), though the file is saved as /system/application/
+ controllers/welcome.php鈥攖he same name but with a
+ lowercase letter.
+
+
+The next three lines make up the constructor function. Notice that CI uses the older
+PHP 4 convention for naming constructor functions, which is also acceptable on
+PHP 5鈥攊t doesn't require you to use PHP 5 and is happy with either version of
+the language. The constructor function is used to set up the class each time you
+instantiate it. In here, you put any instructions to load other libraries or models, or
+any definitions of class variables.
+
+So far the only thing inside the constructor is the parent::Controller() statement.
+This is just way of making sure that you inherit the functionality of the Controller
+class. If you want to understand the parent CI Controller class in detail, you can
+look at the file /system/libraries/controller.php.
+
+(One of the reassuring things about CI is that all the code is there for you to inspect,
+though you don't often need to.)
+
+
+Working with Views
+
+Let's go back to the incoming request for a moment. The router needs to know, not
+only which controller should handle the request, but also which function within that
+controller. That is why the request is specified GET http://127.0.0.1/welcome/
+index. So the router looks for a function inside the welcome controller called index.
+And here it is!
+
+Then comes the index() function. This function simply loads a view
+('welcome_view') using CI's loader function (this->load->view). At this stage,
+it doesn't do anything cool with the view, such as passing it dynamic information.
+That comes later.
+
+The 'welcome_view' it wants to load is in the views folder that you have just
+installed: system/application/views/welcome_view.php. This particular view
+is only a simple HTML page, but it is saved as a PHP file because most views have
+PHP code in them. (No point in doing all this if we're only going to serve up plain
+old static HTML.)
+
+Here's the (slightly shortened) code for the view:
+
+
+Welcome to Code Igniter
+
+
+
+
Welcome to Code Igniter!
+
The page you are looking at is being generated dynamically by
+ Code Igniter.
+
If you would like to edit this page you'll find it located at:
+system/application/views/welcome_message.php
+
The corresponding controller for this page is found at:
+system/application/controllers/welcome.php
+
If you are exploring Code Igniter for the very first time, you
+should start by reading the User
+Guide.
+
+
+
+As you can see, it consists entirely of HTML, with an embedded CSS stylesheet. In
+this simple example, the controller hasn't passed any variables to the view.
+
+
+The Default Controller
+I mentioned earlier that CI routes requests to a default controller if the request
+doesn't specify where it wants to go. You set the default controller from a config
+file鈥攊n this case it's /system/application/config/routes. This contains:
+
+$route['default_controller'] = "welcome";
+
+If you don't set a default, the site users who don't know the exact URL to
+request鈥攖hat's most of them, when you think about it鈥攚ill get '404 not
+found' pages.
+
+In this case, the default route is to your welcome controller.
+
+If no function is specified, the route defaults to the /index function of whatever
+controller is selected, so make sure you include an index function, if only to
+prevent '404' pages. Please note that the index function is not the same as the
+constructor function.
+
+You can alter this default if you like, by including in the controller(s) you want to
+alter, a function called _remap($function), where $function is the function
+you want to intercept and redirect. _remap always gets called first, whatever the
+URL says.
+
+
+CodeIgniter Syntax Rules
+
+Before we start, let's just summarize the syntax rules that CI uses. The framework
+expects files to be set out in certain ways, otherwise it may have difficulty
+indentifying your files properly, or using them.
+
+
+Controller
+
+This is a class (i.e. OO code). It is called directly by the URL, e.g., 'www.example.
+com/index.php/start/hello'. Controllers are used to call functions by name, e.g.,
+mainpage(); however, you cannot call functions inside another controller.
+
+Syntax: Controllers begin with class Start extends Controller (where the name
+of the controller has the first letter in uppercase) and are saved as a .php file in the
+/system/application/controllers folder. When saved, they should not have
+the first letter in uppercase; as in start.php and not Start.php. Also, they should
+include constructor containing at least:
+
+function display()
+ {parent::Controller();}
+
+All other code must be written as separate functions within the class
+e.g., hello() function
+
+
+View
+Views are HTML files that can contain PHP 'islands'. They are loaded by
+$this->load->view('testview', $data). Loading and using the view are done in
+the same action.
+
+Syntax: The view is written in HTML. The PHP code is included within
+tags as with any HTML file. It is saved as a .php file in the views folder.
+
+
+
+Types of Files or Classes on a CI Site
+There are several different sub-folders within the application folder. We have
+already looked at the controller, config, and views folders.
+
+But what are libraries, models, and scripts? This is one area where CI seems rather
+confusing. (If you have used versions of CI before version 1.5, you'll realize why.
+Rick Ellis wasn't happy with the earlier versions and has changed the structure quite
+a lot. However, for compatibility reasons, some anomalies remain.)
+
+In a technical sense, these folders are treated in much the same way. There's no
+reason why you shouldn't put your code in any of these folders, though you'll have
+to make it slightly different in each.
+
+Let's say that you have written a block of code called display, for example, which
+contains a function called mainpage. There are four ways you might have done this:
+as a model, a library, a helper, or a plug-in. The following table summarizes the
+differences between each approach, and shows you how to load and use each type.
+
+File type How to use it
+model This is a class (i.e. it's object-oriented or OO code)
+ Load it like this: $this->load->model('display');
+ Use it like this: $ this->display->mainpage();
+ Notes on syntax:
+ It must begin with class Display extends Model
+ It must include a constructor containing at least:
+ function display()
+ {parent::Model();}
+ and contain a separate mainpage() function.
+ Conceptually: The User Guide says, "Models are PHP classes that are
+ designed to work with information in your database."
+library It is present in both the system and the application folder.
+ Again, this is a class. (Note: your own libraries are not automatically
+ included in the CI super-object, so you need to call CI resources in a
+ different way. See Chapter 7 for details)
+ Load it like this: $this->load->library('display');
+ Use it like this: $this->display->mainpage();
+ Notes on syntax:
+ No need to extend a base class, or for a constructor function.
+ This is enough:
+ class Display()
+ {
+ function mainpage()
+ { //code here }
+ }
+ Conceptually: Intended to hold your own code to extend CI
+ functionality, or to create site-specific functionality.
+helper It can be in the system/helpers folder or in an application/
+ helpers folder. This is a script (procedural code, not an OO class)
+ Load it like this: $this->load->helper('display');
+ Use a function from it like this: mainpage();
+ Notes on syntax:
+ The file should be saved as display_helper.php鈥攊.e.,
+ add _helper to the file name.
+ mainpage() should be a function included in the file, which is
+ simply a collection of separate functions, not a class. As a result you
+ can't directly access CI's other resources any more.
+ Conceptually: 'helpers' are intended as a collection of low-level
+ functions to help you perform specific tasks.
+
+
+ File type How to use it
+ plug-in It is present in the system/plugins folder but can also be created in
+ an applications/plugins folder. This is a script (not an OO class)
+ Load it like this: $this->load->plugin('display');
+ Use a function from it like this: mainpage();
+ Notes on syntax:
+ The file should be saved as display_pi.php鈥攊.e. add _pi to end
+ of filename.
+ mainpage() should be a function included in the file, which is
+ simply a collection of separate functions, not a class. As a result, you
+ can't directly access CI's other resources any more.
+ Conceptually: The User Guide says, "鈥he main difference is that
+ a plug-in usually provides a single function, whereas a Helper is
+ usually a collection of functions鈥.. plug-ins are intended to be
+ created and shared by our community." (See Chapter 15 for an
+ example plug-in.)
+
+You could put your piece of new code in any of these folders, though you'd have to
+write it as an object-oriented class in the first two, and as a procedural script in the
+second, and in the latter case, you wouldn't be able to draw directly on other CI classes.
+Otherwise, the difference between the types of folder is largely a conceptual one.
+
+You'll notice that CI can have two sets of helpers, plug-ins, and libraries, though not
+of models. There can be one set of each in the application folder, and another set in
+the system folder. The difference, again, is largely conceptual.
+
+ Those in the system folder are intended to be part of the core CI code and to
+ 鈥
+ be shared by all applications. If you update to a later version of CI, then you
+ will overwrite the system folder and these files may be modified.
+ Those in the application folder will only to be available to that one
+ 鈥
+ application. If you update to a new version of CI, the application folder
+ will not be overwritten.
+ When you try to load a helper, plug-in, or library, CI sensibly looks in both
+ 鈥
+ paths. If you attempt to load a library called display, for example, CI will
+ look first in your system/application/libraries directory. If the directory
+ does not exist or the display library is not there, CI will then look in the
+ system/libraries folder.
+ This means that it is possible to effectively over-write CI's core libraries,
+ 鈥
+ helpers, and plug-ins by introducing your own with the same names in the
+ applications folder. Don't do this accidentally. However, this flexibility is
+ a great advantage for experienced CI users if you want to extend the basic
+ classes and scripts that come with CI鈥攕ee Chapter 13.
+
+
+What are All Those Folders For?
+
+Now that we've looked at the key types of folder in some detail, here is a reference
+table of the structure of a CI site.
+
+ application config Configuration files: hold basic information about your site
+ that persists between sessions
+ controllers Controllers
+ errors Contains templates for error announcements. You may
+ never need to touch these.
+ hooks Empty when first installed, use this for any 'hooks' you
+ create. Hooks are a way of controlling the way other
+ files load.
+ libraries Collections of your code, intended to work with this
+ specific application
+ models Collections of your code, again intended to work with this
+ specific application
+ views Templates for showing information to the user
+ cache Empty when first installed: if you enable caching (see Chapter 10) data is
+ stored here
+ codeigniter Basic system files.
+ database Library files for CI's database class.
+ fonts Not explained in the user guide, except as a place to store fonts for
+ watermarking images
+ helpers System level 'helpers'
+ language You can store your own lists of key phrases here鈥攕ee Chapter 11
+ libraries System level libraries
+ logs If you set the system to log errors, log files are created here
+ by default
+ plugins More system-level code blocks
+ scaffolding System-level library to enable rudimentary 'scaffolding'.
+
+
+
+Designing a Better View
+
+At this stage, you might ask why we are going through so much effort to serve up
+a simple HTML page. Why not put everything in one file? For a simple site, that's
+a valid point鈥攂ut whoever heard of a simple site? One of the coolest things about
+CI is the way it helps us to develop a consistent structure, so that as we add to and
+develop our site, it is internally consistent, well laid out, and simple to maintain.
+
+
+At the start, we need three common steps:
+
+ Write a view page
+ 鈥
+ Write a stylesheet
+ 鈥
+ Update our config file to specify where the stylesheet is
+ 鈥
+
+After this is done, we need to update our controller to accept parameters from the
+URL, and pass variables to the view.
+
+First, let's redesign our view and save it as:
+system/application/views/testview.php.
+
+
+
+
+
+ Web test Site
+ >
+ ">
+
+
+
+
+
+
+
+It's still mostly HTML, but notice the PHP 'code islands' in the highlighted lines.
+
+You'll notice that the first bits of PHP code build in a link to a stylesheet. Let's save a
+simple stylesheet as mystyles.css, in the site's root folder. It just says:
+
+h1 {
+margin: 5px;
+padding-left: 10px;
+padding-right: 10px;
+background: #ffffff;
+color: blue;
+width: 100%;
+font-size: 36px;
+}
+.test{
+margin: 5px;
+padding-left: 10px;
+padding-right: 10px;
+background: #ffffff;
+color: red;
+width: 100%;
+font-size: 36px;
+}
+
+That gives us two styles to play with, and you'll see we've used them both in
+the view.
+
+Firstly, let's add an entry to the config file:
+ $config['css'] = "mystyles.css";
+
+This is simply to tell the site the name and address of the CSS file that we've
+just written.
+
+But note that the link to the stylesheet is referenced at $base/$css鈥攚here do those
+variables $base and $css, get their values? And come to think of it, those variables
+$mytitle and $mytext at the end of the code? We need a new controller!
+
+
+
+Designing a Better Controller
+Now, we need a new controller. We'll call it Start and save it as
+/system/ application/controllers/start.php.
+
+This controller has to do several things:
+
+ Call a view
+ 鈥
+ Provide the view with the base URL and the location of the css file we
+ 鈥
+ just wrote
+ Provide the view with some data: it's expecting a title ($mytitle) and some
+ 鈥
+ text ($mytext)
+ Lastly, we want the controller to accept a parameter from the user (i.e. via the
+ 鈥
+ URL request)
+
+In other words, we have to populate the variables in the view. So let's start with our
+Start controller. This is an OO class:
+
+config->item('name_of_config_variable');
+
+as in:
+
+function Start()
+{
+ parent::Controller();
+ $this->base = $this->config->item('base_url');
+ $this->css = $this->config->item('css');
+}
+
+and CI recovers whatever we entered in the config file against that name.
+
+Using this system, however many controllers and functions we write, we'll only have
+to change these fundamental variables once, even if our site becomes so popular that
+we have to move it to a bigger server.
+
+
+Getting Parameters to a Function
+Now, within the Start controller class, let's define the function that will actually do
+the work.
+
+function hello($name)
+{
+ $data['css'] = $this->css;
+ $data['base'] = $this->base;
+ $data['mytitle'] = 'Welcome to this site';
+ $data['mytext'] = "Hello, $name, now we're getting dynamic!";
+ $this->load->view('testview', $data);
+}
+
+This function expects a parameter, $name, (but you can set a default value鈥
+myfunction($myvariable = 0)), which it uses to build up the string assigned to
+the $mytext variable. Well, as we had just asked, where does that come from?
+
+In this case, it needs to come from the URL request, where it will be the third
+parameter. So, it comes via the HTTP request:
+ GET /index.php/start/hello/fred HTTP/1.0
+
+Or in other words, when you type in the URL:
+ http://www.mysite.com/index.php/start/hello/fred
+
+
+ Note that this example code doesn't 'clean' the passed variable fred,
+ or check it in any way. You might want to do this in production code.
+ We'll look at how to check form inputs in Chapter 7. Normally, variables
+ passed by hyperlinks in this way are generated by your own site. A
+ malicious user could easily add his or her own, just by sending a URL
+ like: http://www.mysite.com/index.php/start/hello/my_
+ malicious_variable. So, you might want to check that the variables
+ you receive are within the range you expect before handling them.
+
+The last segment of the URL is passed to the function as a parameter. In fact, you
+can add more segments of extra parameters if you like, subject to the practical limits
+imposed by your browser.
+
+Let's recap on how CI handles URLs, since we've covered it all now:
+
+ URL segment What it Does
+ The base URL that finds your site.
+ http://www.mysite.com
+ Finds the CI router that sets about reading the rest of the
+ /index.php
+ URL and selecting the correct route into your site.
+ The name of the controller CI will call. (If no name is set, CI
+ /start
+ will call whichever default controller you've specified.)
+ The name of a function that CI will call, inside the selected
+ /hello
+ controller. (If no function is specified, defaults to the index
+ function, unless you've used _remap.)
+ CI passes this to the function as a variable.
+ /fred
+ If there is a further URL CI passes this to the function as a second variable.
+ segment, e.g. /bert
+ More variables CI will pass further URL segments on as further variables.
+
+
+Passing Data to a View
+
+Let's go back to the hello function:
+
+function hello($name)
+{
+ $data['css'] = $this->css;
+ $data['base'] = $this->base;
+ $data['mytitle'] = 'Welcome to this site';
+ $data['mytext'] = "Hello, $name, now we're getting dynamic!";
+ $this->load->view('testview', $data);
+}
+
+Notice how the hello() function first creates an array called $data, taking a
+mixture of object properties set up by the constructor and text.
+
+Then it loads the view by name, with the array it has just built as the
+second parameter.
+
+Behind the scenes, CI makes good use of another PHP function: extract(). This
+takes each value in the $data array and turns it into a new variable in its own
+right鈥攕o the $data array that we've just defined is received by the view as a series
+of separate variables: $text (equal to "Hello, $name, now we're getting
+dynamic"), $css (equal to the value from the config file), and so on.
+
+In other words, when it is built, the $data array looks like this:
+
+Array
+(
+ [css] => mystyles.css
+ [base] => http://127.0.0.1/packt
+ [mytitle] => Welcome to this site
+ [mytext] => Hello, fred, now we're getting dynamic!
+)
+
+But on the way to the view, it is unpacked, and the following variables are created in
+the view to correspond to each key/value pair in the array:
+
+ $css = 'mystyles.css';
+ $base = 'http://127.0.0.1/packt';
+ $mytitle = 'Welcome to this site';
+ $mytext = 'Hello, fred, now we're getting dynamic!';
+)
+
+Although you can only pass one variable to a view, you can pack a lot of information
+into that one variable. Each value in the $data array can itself be another array,
+and so on, so you can pass pieces of information to the view in a tightly
+structured manner.
+
+Now navigate to http://127.0.0.1/packt/index.php/index/start/fred (note
+that the URL is different鈥攊t is looking for the start function we wrote in the index
+controller) and you'll see the result: a dynamic page written using MVC architecture.
+(Well, VC at least! We haven't really used the M yet.)
+
+
+Here's what it should look like now:
+
+
+
+
+You can see (at least I hope you can!) that the parameter fred is the last segment of
+the URL. It has been passed into the function, and then through to the view.
+
+Please remember that your view must be written in parallel with your controller.
+If the view does not expect and make a place for a variable, it won't be displayed.
+If the view is expecting a variable to be set and it isn't, you are likely to get an error
+message. (Your view can of course accept variables conditionally.)
+
+
+
+How CI Classes Pass Information and Control to Each Other
+
+As you write your controllers, models, etc., you will need to pass control and data
+between them. Let's look at some of the ways in which we can do this.
+
+
+Calling Views
+
+We have seen how the controller calls a view and passes data to it:
+
+First it creates an array of data ($data) to pass to the view; then it loads and calls the
+view in the same expression:
+
+$this->load->view('testview', $data);
+
+
+
+Calling Functions Directly
+If you want to use code from libraries, models, plug-ins, or helpers, of course, you
+have to load them first, and then call them as described in the previous table. So, if
+'display' is a model and I want to use its mainpage function, my controller might call:
+
+$this->display->mainpage();
+
+If the function requires parameters, we can pass them to the function like this:
+
+$this->display->mainpage('parameter1', $parameter2);
+
+
+Interacting with Controllers
+
+You can call libraries, models, plug-ins, or helpers from within any controller, and
+models and libraries can also call each other as well as plug-ins and helpers.
+
+However, you can't call one controller from another, or call a controller from a
+model or library. There are only two ways that a model or a library can refer back to
+a controller:
+
+Firstly, it can return data. If the controller assigns a value like this:
+
+$fred = $this->mymodel->myfunction();
+
+and the function is set to return a value, then that value will be passed to the variable
+$fred inside the controller.
+
+Secondly, your model or library can create (and send to a view) a URL, which
+allows a human user to call the controller functions. Controllers are there to receive
+human interactions.
+
+You can't, of course, hyperlink directly to a model or library. Users always talk
+to controllers, never to anything else鈥攂ut you can write a calling function in
+the controller. In other words, your view might contain a hyperlink to a
+controller function:
+
+echo anchor('start/callmodel', 'Do something with a model');
+
+but the callmodel function would exit only to call a function in the model:
+
+function callmodel()
+{
+ $this->load->model(mymodel);
+ $this->mymodel->myfunction();
+}
+
+
+It's Just Like an Egg-Cup
+
+ humans
+
+
+
+
+ controllers views
+
+
+
+
+ models and
+ libraries
+
+
+
+
+This diagram shows the different ways in which components can address each other.
+
+Unbroken lines represent direct function calls such as:
+
+$this->mymodel->myfunction();
+
+These can take place from controllers to views, and from controllers to libraries or
+models. (Models can also call views, but probably shouldn't.) They can't take place in
+reverse: views etc. can't call controllers. However, libraries and models can call each
+other and be called by each other.
+
+Broken lines represent passing information by returning values. Models and libraries
+can do this to controllers, or to each other. Views don't return values.
+
+The dotted lines represent passing information or control via a human user鈥攊n other
+words, the view will show the user something on the screen and may invite the user
+to click on a hyperlink (which sets off a controller).
+
+Any resemblance to an egg-cup is purely coincidental. It just came out that way.
+
+
+An Example of a CI Helper: the URL Helper
+
+As an example of the way you can split your code up into neat, focussed, chunks,
+CI's URL helper contains a set of functions that help you to manipulate URLs. You
+load it like this:
+
+$this->load->helper('url');
+
+And then, you can use it to find and return the site and/or base URLs that you set in
+your config file:
+
+echo site_url();
+echo base_url();
+
+You can also use it to create hyperlinks. We saw in the last section how you can
+access the hello function in the start controller, and pass it the parameter fred,
+with a URL like:
+http://www.mysite.com/index.php/start/hello/fred
+
+If you want your own code to create a hyperlink to a URL like this, you can use the
+URL helper to do it. The syntax is:
+
+echo anchor('start/hello/fred', 'Say hello to Fred');
+
+This generates a hyperlink to the same URL, and displays the words Say hello to
+Fred to the user to be clicked on. In other words, it's the equivalent of:
+
+say hello to Fred
+
+Remember, there are two advantages to using the CI helper. Firstly, less typing
+(49 characters as opposed to 82, both including spaces. If you include loading
+the URL helper鈥攁nother 27 characters, which you only have to do once per
+controller鈥攊t still comes to 76 rather than 82.)
+
+Secondly, the URL helper automatically looks up the site URL in the config files
+(and the index file name). This means that if you change your site location, you only
+need to alter the config file once, you don't have to hunt through your code for
+hyperlinks that don't work any more.
+
+The URL helper has other useful functions. For instance, it can create a
+'mailto' hyperlink.
+
+echo mailto('me@example.com', 'Click Here to Email Me');
+
+has the same effect as typing this HTML:
+
+click here to email me
+
+
+If you are worried about robots harvesting the email addresses from your website
+and using them for spamming, change mailto in the CI code to safe_mailto. What
+appears on your viewer's screen is exactly the same, and works the same way.
+
+However, if you examine the actual HTML code, this has now become a complex
+heap of JavaScript, which the robot cannot (easily) read:
+
+
+
+You, and your users, need never see this code. It's only there to confuse the robots
+and keep your email addresses safe from spam. You put it there by adding four
+letters and an underscore: you wrote safe_mailto instead of mailto, and CI did
+the rest.
+
+There are several other useful functions in the URL helper. See the User Guide for
+more about them and how to use them.
+
+Just consider the URL helper as a whole. Let's go back to the touchstones for coding
+we discussed earlier in this chapter:
+
+ This code has high 'component singularity'. It does a limited range of things,
+ 鈥
+ and it's clear what they are.
+ It is 'loosely coupled'鈥攊t has a simple interface and no other dependency on
+ 鈥
+ any code that's calling it. You can use the URL helper in any CI project you're
+ writing. Most of your projects will need some sort of hyperlinks, for instance.
+ You can use this helper over and over again to create them.
+
+
+If you look at the URL helper's code (in system/application/helpers/
+url_helper.php) you'll see that it is procedural code鈥攖hat is, it is simply a set of
+functions, not an OO class. It doesn't load any other CI classes or helpers. (Not
+being an object, it can't do this.)
+
+
+A Simple Library Example: Creating a Menu
+
+Now let's look at some code that does use the CI classes.
+
+For instance, here is a simple library file that creates a menu with three choices:
+
+1 load->helper('url');
+7 $menu = anchor("start/hello/fred","Say hello to Fred |");
+8 $menu .= anchor("start/hello/bert","Say hello to Bert |");
+9 $menu .= anchor("start/another_function","Do something else |");
+10 return $menu;
+11 }
+12 }
+13 ?>
+
+(For the moment, don't worry about the unusual syntax鈥$obj-> rather than
+$this-> in line 6. This is exaplained in Chapter 7.)
+
+Note that this code is now OO code, in which the function show_menu() is contained
+in a single class, 'Menu'. It can access other CI classes and helpers: in this case it is
+using the URL helper, which we just examined.
+
+Firstly, it loads the URL helper, and then it creates a string ($menu), consisting of
+HTML code for hyperlinks to the three controllers and functions specified to it. Then
+it returns the $menu string.
+
+You might call it from a controller like this:
+
+$mymenu = $this->menu->show_menu();
+
+and then the controller can use the $menu variable to call a view:
+
+$data['menu'] = $mymenu;
+$this->load->view('myview', $data);
+
+This class produces a menu, which is site-specific. For this reason, I would save it in
+the /system/application/libraries, rather than the system/libraries, folder.
+It's not as loosely coupled as the URL helper, which I can use on any site.
+
+It does have high singularity: it creates a menu, and that's all it does. I can call it
+from any controller in my site and know that it will show the standard menu in
+my models.
+
+
+
+Summary
+The MVC framework is a widely used and very effective way of organizing a
+complex website. CI uses it to help you sort out your own code, but it is also fairly
+flexible about how it does so.
+
+Try to keep in mind the two principles of 'loose coupling' and 'component
+singularity' when you write your own code. Don't worry too much whether your
+application conforms to the strict theory of MVC or not. The crucial thing is to
+understand what the different types of file are, and how they relate to each other.
+Then, you can decide whether to write your own code in library or model files, or as
+helpers or plug-ins.
+
+We've looked at the CI file structure, and seen how you can, if you want, inspect all
+the CI code, but (thankfully!) you don't have to.
+
+We did tinker with one of the original files: the config file, which holds critical site
+information in one place to make it easier for us to upgrade or change later on.
+
+We've seen the basic object structure of a controller, and used a simple constructor
+to get some data from our config file and put it into a class property. And we've
+dynamically passed information from a new controller we wrote, to a new view. So
+far, the main thing CI has done for us is to encourage us to use a basic structure as
+we start to define our site. As we go on, it will become clear just how important that
+structure is.
+
+Also, we looked a the way that CI's components pass data and control between
+themselves. It's useful to understand this when you start to write your code.
+
+Lastly, we looked at CI's own URL helper as a good example of a chunk of code, and
+we wrote our own rudimentary 'menu' library class.
+
+
+ Using CI to Simplify
+ Databases
+You're looking at CI because you want to make coding easier and more productive.
+This chapter is about CI's Active Record class. If CI offered nothing more than its
+Active Record class, it would still be worth every penny of the purchase price.
+All right, it's free. I'll rephrase that鈥攊t would still be a major tool to increase
+your productivity.
+
+Active Record allows you to handle databases with a minimum of fuss and a
+maximum of clarity. It's simple to use and maintain.
+
+This chapter looks at how you set up a database to work with CI, and then how you
+use the Active Record class to manipulate the database. You'll see:
+
+ How Active Record code compares with 'classic' PHP锟 MySQL
+ 鈥
+ interface code
+ How to write 'read' queries, and display the results
+ 鈥
+ How to do create, update, and delete queries
+ 鈥
+
+CI allows you to write queries in the traditional 'classic' PHP style as well, but I won't
+go into detail on that. It's fully covered in the online User Guide. I started off doing it
+the old way, but once I tried Active Record, I never looked back.
+
+
+
+Configuration Settings
+You have probably noticed that most chapters in this book keep going back to the
+system/application/config folder and the configuration files inside it. These are
+pretty essential for controlling the way CI works. And while you can leave most of
+them safely set at the defaults, the database config file does need tweaking before
+anything will work at all.
+
+
+Using CI to Simplify Databases
+
+Basically, you just have to tell it where your database is, and what type it is. The
+default file simply says:
+
+$active_group = "default";
+$db['default']['hostname'] = "";
+$db['default']['username'] = "";
+$db['default']['password'] = "";
+$db['default']['database'] = "";
+$db['default']['dbdriver'] = "";
+
+along with a few other options that you can leave at the default, for now. The options
+you must fill in are:
+
+ hostname: The location of your database, e.g., 'localhost' or an IP address
+ 鈥
+ username and password: The username and password of a database user
+ 鈥
+ with sufficient permissions to do whatever you may want your site to do.
+ This is not (usually) the same username and password as your site or your
+ ISP's control panel.
+ database: The name of your database, e.g., 'websites'
+ 鈥
+ dbdriver: The type of database you're using鈥攁t the time of writing, the
+ 鈥
+ options CI offers were MySQL, MySQLi, Postgre SQL, ODBC, and MS SQL.
+
+In my experience, one of the most difficult things to set up on a new CI site can be
+the link to the database. You may need to consult your ISP if in doubt鈥攕ometimes
+their database runs at a different address to their web servers. If you are using
+MySQL, they may offer phpMyAdmin, which usually tells you the hostname鈥攖his
+may be 'localhost' or it may be an IP address.
+
+You'll note that this part of the config file is actually a multi-dimensional array.
+Within $db is an array called default, and you're adding key锟絭ariable pairs like
+hostname = 127.0.0.1 to that array. This is so that you can set up other databases,
+as other secondary arrays, and swap between them easily by simply changing the
+$active_group setting to the name of another array.
+
+This makes it possible to run a site with several database options鈥攆or instance, a
+test database and a production database鈥攁nd to swap between them easily. Or you
+might need to draw information from two separate databases.
+
+
+
+Designing the Database for Our Site
+I want to show that CI can be used to develop a serious website with a serious
+purpose. I am currently running several websites for clients, and I want a program
+that will monitor them, test them in ways I specify, keep a database of what it has
+
+ [ 52 ]
+
+
+ Chapter 4
+
+done, and let me have reports when I want them. So let's try to build it. Firstly let's
+set some objectives. These are:
+
+ 1. To manage one or more remote websites with a minimum of
+ human intervention
+ 2. To run regular tests on the remote sites
+ 3. To generate reports on demand, giving details of the site and of
+ tests conducted
+
+So, the first thing we will need is a database of websites to check. Set up a database
+called websites in MySQL or whatever RDBMS you're using.
+
+Now, we need to add some tables to hold various types of data. Let's add to our
+websites database a table for sites, which includes fields for their URLs, their names
+and password锟絬sernames, and their types. We'll also include an id field for each
+site鈥攁nd in MySQL at least, which can be set to generate a unique new ID for each
+entry, using the auto-increment field type.
+
+Each site may be hosted with a different host, or host machine, and we need another
+hosts table to store data about this. It most probably has a domain associated with
+it, so we need a domains table to keep track of data about the domain, like when it's
+due for renewal, the registrar, and our username锟絧assword on the registrar site.
+Then of course, we have those tiresome people, clients, some of whom may own
+more than one site, so we need a separate people table in which to store their names,
+email addresses, snail mail addresses, mobile numbers, plus pet's names, and all the
+other stuff so vital to good CRM.
+
+So our site table needs to include fields for a domain ID, a host ID, and perhaps a
+couple of people IDs, one for the site owner or client and one for the site manager.
+(That's you, or one of the staff you'll have to hire to keep pace when this app hits
+the market.)
+
+As you can see, this is a full relational database, and we've only just started! (Fuller
+details of this database are set out as an appendix to this chapter, in the form of a
+MySQL query, if you want to set one up yourself.)
+
+We're going to want a simple flexible way of accessing all this. So, let's turn to what
+CI can offer, and in particular to its Active Record class.
+
+
+
+Active Record
+'Active Record' is a 'design pattern'鈥攁nother of those highly abstract systems like
+MVC, which provide templates for solving common coding problems and also
+generate some of the dullest books on the planet. In itself, it isn't code, just a pattern
+ [ 53 ]
+
+
+Using CI to Simplify Databases
+
+for code. There are several different interpretations of it. At its core is the creation
+of a relationship between your database and an object, every time you do a query.
+Typically, each table is a class, and each single row becomes an object. All the things
+you might want to do with a table row鈥攃reate it, read it, update it, or delete it,
+for example鈥攂ecome 'methods', which that object inherits from its class. Ruby on
+Rails is built around the Active Record pattern, and so is CI鈥攁lthough the exact
+implementation in the two frameworks seems to have subtle differences.
+
+Enough theory鈥攚hat does it mean? Well, simple and clear code statements, if you
+don't mind putting arrows in them.
+
+
+Advantages of Using the Active Record Class
+Active record saves you time, brings in automatic functionality that you don't have
+to think about, and makes SQL statements easy to understand.
+
+
+Saving Time
+When you write a normal database query in PHP, you must write a connection to
+the database each time. With CI, you connect once to the database, by putting the
+following line in the constructor function of each controller or model:
+
+$this->load->database();
+
+Once you've done this, you don't have to repeat the connection, how many ever
+queries you then make in that controller or model.
+
+You set up the database details in the config files as we saw earlier in this chapter.
+Once again, this makes it easier to update your site, if you ever change the database
+name, password, or location.
+
+
+Automatic Functionality
+Once you've connected to the database, CI's active record syntax brings hidden code
+with it. For instance, if you enter the following 'insert' query:
+
+$data = array(
+ 'title' => $title,
+ 'name' => $name,
+ 'date' => $date
+ );
+$this->db->insert('mytable', $data);
+
+the values you are inserting have been escaped behind the scenes by this code:
+
+function escape($str)
+{
+ switch (gettype($str))
+ {
+ case 'string':
+ $str = "'".$this->escape_str($str)."'";
+ break;
+ case 'boolean': $str = ($str === FALSE) ? 0 : 1;
+ break;
+ default : $str = ($str === NULL) ? 'NULL' : $str;
+ break;
+ }
+ return $str;
+}
+
+In other words, the CI framework is making your code more robust. Now, let's look
+at how it works.
+
+Firstly, connecting to the database is very simple. In classic PHP, you might say
+something like this:
+
+$connection = mysql_connect("localhost","fred","12345");
+mysql_select_db("websites", $connection);
+$result = mysql_query ("SELECT * FROM sites", $connection);
+while ($row = mysql_fetch_array($result, MYSQL_NUM))
+{
+ foreach ($row as $attribute)
+ print "{$attribute[1]} ";
+}
+
+In other words, you have to re-state the host, username, and password, make a
+connection, then select the database from that connection. You have to do this each
+time. Only then, do you get on to the actual query. CI replaces the connection stuff
+with one line:
+
+$this->load->database();
+
+which you put once, in each controller or model or class constructor that you write.
+After that, in each function within those controllers, etc., you just go straight into
+your query. The connection information is stored in your database config file, and
+CI goes and looks it up there each time.
+
+So, in each CI function, you go straight to your query. The query above written in CI
+comes out as:
+
+$query = $this->db->get('sites');
+ foreach ($query->result() as $row)
+ {
+ print $row->url
+ }
+
+Simple, isn't it?
+
+The rest of this chapter sets out ways of making different queries, making them
+more specific.
+
+
+Read Queries
+The most common query that we'll write simply retrieves information from the
+database according to our criteria. The basic instruction to perform a read query is:
+
+$query = $this->db->get('sites');
+
+This is a 'SELECT *' query on the sites table鈥攊n other words, it retrieves all the
+fields. If you prefer to specify the target table (sites) in a separate line, you can do
+so in this way:
+
+$this->db->from('sites');
+$query = $this->db->get();
+
+If you want to 'SELECT' or limit the number of fields retrieved, rather than get them
+all, use this instruction:
+
+$this->db->select('url','name','clientid');
+$query = $this->db->get('sites');
+
+You may want to present the results in a particular order鈥攕ay by the site name鈥攊n
+which case you insert (before the $this->db->get line):
+
+$this->db->orderby("name", "desc");
+
+desc means in descending order. You can also choose asc (ascending) or
+rand (random).
+
+You may also want to limit the number of results your query displays; say you want
+only the first five results. In this case insert:
+
+$this->db->limit(5);
+
+Of course, in most queries, you're not likely to ask for every record in the table. The
+power of databases depends on their ability to select鈥攖o pick out the one piece of
+data you want from the piles of stuff you don't. This is usually done by a where
+statement that CI expresses in this way:
+
+$this->db->where('clientid', '1');
+
+This statement would find all websites linked to the client whose ID was 1. But that's
+not much help to us. We don't want to remember all the ID's in our people table.
+As humans, we prefer to remember human names. So we need to link in the
+people table:
+
+$this->db->from('sites');
+$this->db->join('people', 'sites.peopleid = people.id');
+
+For each people ID in the sites table, look up the information against that ID in the
+people table as well.
+
+
+ Note the SQL convention that if a field name may be ambiguous between
+ two tables, you reference it with the table name first, then a period, then
+ the field name. So sites.peopleid means the peopleid field in the
+ sites table. In fact, there isn't a field called peopleid in both tables,
+ but there is an id field in both sites and people, so the RDBMS will
+ protest if you try to run the query without resolving the ambiguity for it.
+ In any case, it's a good habit to make your meaning explicit, and CI syntax
+ happily accepts the fuller names.
+
+
+You can play around with the syntax of where statements. For instance, add
+negation operators:
+
+$this->db->where('url !=','www.mysite.com' );
+
+or comparison operators:
+
+$this->db->where('id >','3' );
+
+or combine statements ("WHERE鈥 AND鈥"):
+
+$this->db->where('url !=','www.mysite.com');
+$this->db->where('id >', '3');
+
+or use $this->db->orwhere() to search for alternatives ("WHERE 鈥 OR"):
+
+$this->db->where('url !=','www.mysite.com' );
+$this->db->orwhere('url !=','www.anothersite.com' );
+
+So let's say we've built up a query like this:
+
+$this->db->select('url','name','clientid','people.surname AS client');
+$this->db->where('clientid', '3');
+$this->db->limit(5);
+$this->db->from('sites');
+$this->db->join('people', 'sites.clientid = people.id');
+$this->db->orderby("name", "desc");
+$query = $this->db->get();
+
+This should give us the first five (ordered by name) websites belonging to client
+number 3, and fetch the client's surname as well as his or her ID number!
+
+A hidden benefit of using Active Record is that data that may have come in from
+users is automatically escaped, so you don't have to worry about putting quotes
+around it. This applies to functions like $this->db->where(), and also to the data
+creation and update statements described in the next sections. (Security warning: this
+is not the same thing as preventing cross-scripting attacks鈥攆or that you need CI's
+xss_clean() function. It's also not the same as validating your data鈥攆or this you
+need CI's validation class. See Chapter 5.)
+
+
+Displaying Query Results
+
+Showing database query results in CI is quite simple. We define our query as above,
+ending in:
+
+$query = $this->db->get();
+
+Then, if there are multiple results, they can be returned as a $row object through
+which you iterate with a foreach loop:
+
+foreach ($query->result() as $row)
+{
+ print $row->url;
+ print $row->name;
+ print $row->client;
+}
+
+or if we only want a single result, it can be returned as an object, or here as a
+$row array:
+
+if ($query->num_rows() > 0)
+{
+ $row = $query->row_array();
+
+ print $row['url'];
+ print $row['name'];
+ print $row['client'];
+}
+
+Personally, I prefer the object syntax to the array鈥攍ess typing!
+
+When you follow the MVC pattern, you will usually want to keep your queries and
+database interactions in models, and display the information through views.
+
+
+Create and Update Queries
+
+Active Record has three functions that help you create new entries in your database.
+They are $this->db->insert(), $this->db->update(), and $this->db->set().
+
+The difference between a 'create' and an 'update' query is that when you create a new
+record, there is no reference to any existing record, you are writing a new one. When
+you update, there is an existing record, and you are changing it. So in the second
+case, you have to specify which record you are changing. In both cases, you have to
+set the values you want to leave in the database after your query. Values you don't
+set will be left unaltered鈥攐r, if they didn't exist before, they will still be 'null' after
+your query.
+
+CI allows you to set your values either with an array, or with $this->db-set(); the
+difference is only one of syntax.
+
+So, let's add a line to our sites table in the websites database. We've already
+connected to this database in our controller. The controller's constructor function
+included the line:
+
+$this->load->database();
+
+We want to add a new site, which has a URL, a name, a type, and a client ID number.
+As an array, this might be:
+
+$data = array(
+ 'url' => 'www.mynewclient.com',
+ 'name' => 'BigCo Inc',
+ 'clientid' => '33',
+ 'type' => 'dynamic'
+ );
+
+To add that to the sites table, we follow it with:
+
+$this->db->insert('sites', $data);
+
+Alternatively, we could set each value using $this->db->set():
+
+$this->db->set('url', 'www.mynewclinet.com');
+$this->db->set('name', 'BigCo Inc');
+$this->db->set('clientid', '33');
+$this->db->set('type', 'dynamic');
+$this->db->insert('sites');
+
+If we are updating an existing record, then again we can either create an array, or use
+$this->db->set(). But there are two differences.
+
+Firstly, we have to specify the record we want to update; and second, we need to
+use $this->db->update(). If I want to update a record (say the record with its 'id'
+field set to 1) in my sites table, using the data set out in my $data array above, the
+syntax is:
+
+$this->db->where('id', '1');
+$this->db->update('sites', $data);
+
+Or I can set out the information using $this->db->set(), as above.
+
+CI gives you several functions to check what the database has done. Most usefully:
+
+$this->db->affected_rows();
+
+should return '1' after my insert or update statement鈥攂ut might show more rows
+if I was altering several rows of data at one time. You can use it to check that the
+operation has done what you expected.
+
+You've noticed that I didn't set an ID field when I created a new record. That's
+because we set the database to populate the ID field automatically when a new
+record is added. But I do have to specify an ID when I update an existing record,
+otherwise the database doesn't know which one to alter.
+
+If I'm generating a new record, however, I don't know the ID number until I've
+generated it. If I then need to refer to the new record, I can get the new ID
+number with:
+
+$new_id_number = $this->db->insert_id();
+
+(This code has to go, as soon as possible, after the operation that generated the
+record, or it may give a misleading result.)
+
+For a little more peace of mind, remember that CI Active Record functions, including
+$this->db->insert() and $this->db->update() automatically escape data
+passed to them as input.
+
+From version 1.5, CI also includes support for transactions鈥攍inking two or more
+database actions together so that they either all succeed, or all fail. This is essential
+in double-entry book-keeping applications and many commercial sites. For instance,
+say you are selling theatre tickets. You record receiving a payment in one transaction,
+and then allocate a seat to the customer in another. If your system fails after doing
+the first database operation, but before doing the second, you may end up with an
+angry customer鈥攚ho has been charged, but has not had a seat reserved.
+
+CI now makes it much simpler to link two or more database operations into one
+transaction, so that if they all succeed, the transaction is 'committed', and if one or
+more fails, the transaction is 'rolled back'. We don't need to use this in our example
+site, but if you want more information see the CI online User Guide.
+
+
+Delete Queries
+
+Delete queries are perhaps the simplest to describe. All you need is the name of the
+table and the ID number of the record to delete. Let's say I want to delete record in
+my sites table with the ID number 2:
+
+$this->db->where('id', '2');
+$this->db->delete('sites');
+
+I get slightly nervous around 'delete' queries because they are so powerful. Please
+remember to make sure that there is a valid value in the 'where' clause, or you may
+delete your whole table! Neither the author nor Packt Publishing will accept any
+liability if鈥.
+
+
+Mixing Active Record and 'Classic' Styles
+
+CI doesn't insist that you use Active Record. You can also use CI to issue straight
+SQL queries. For instance, assuming you loaded the database in your constructor,
+you can still write queries like this:
+
+$this->db->query("SELECT id, name, url FROM sites WHERE 'type' = 'dynamic'");
+
+Personally, I find Active Record easier to use. Conceptually, setting out my query
+in an array makes it easier to see and manipulate as an entity than writing it in SQL
+syntax. It's slightly more verbose, but clearly structured; it automatically escapes
+data; and it may be more portable. It also minimizes typing errors with commas
+and quotes.
+
+There are a few cases, however, where you may have to resort to the original SQL.
+You might want to do complex joins, or another example is if you need to use
+multiple 'where' conditions. If you want to find the websites associated with client 3,
+but only those of two specific types, you may need to put brackets around the SQL to
+make sure the query is correctly interpreted.
+
+In cases like these, you can always write out the SQL as a string, put it in a variable,
+and use the variable in CI's $this->db->where() function, as follows:
+
+$condition = "client ='3' AND (type ='dynamic' OR type='static')";
+$this->db->where($condition);
+
+Without the brackets this is ambiguous. Do you mean:
+
+(client='3' AND type = 'dynamic') OR type = 'static'
+
+or
+
+client='3' AND (type = 'dynamic' OR type = 'static')
+
+Well, yes of course, it's obvious, but the machine usually guesses wrong.
+Incidentally, be careful with the syntax of $condition. The actual SQL query is:
+
+client='3' AND (type = 'dynamic' OR type = 'static')
+
+The double quotes come from the variable assignment:
+
+$condition = " ";
+
+It's easy to get your single and double quotes confused.
+
+Some of the CI expressions I've quoted above, like $this->db->affected_rows(),
+are not a part of its Active Record model. But they can be mixed in easily.
+
+The only time you might run into problems is if you try to mix Active Record and
+straight SQL in the same query. (I haven't tried this. If you have a lot of time on your
+hands, you could test it out, but frankly, I think that would indicate a sad lifestyle.
+Try train-spotting instead. At least it gets you out into the fresh air! I use CI because
+I'm too busy not to!)
+
+
+
+Summary
+We've looked at CI's Active Record class and seen how easy it is to:
+
+ Set up connections to one or more databases
+ 鈥
+ Do standard SQL read, update, create, and delete queries
+ 鈥
+ Perform other functions that we need, to use a database properly
+ 鈥
+
+CI's Active Record function is clean and easy to use, and makes coding much clearer
+to read. It automates database connections, allowing you to abstract the connection
+information to one config file.
+
+It can do pretty well anything that you can do with 'classic' SQL鈥攎ore than I have
+space to explain here. See the online User Guide for fuller details.
+
+
+Chapter Appendix: MYSQL Query to Set Up 'websites' Database
+
+DROP TABLE IF EXISTS `ci_sessions`;
+CREATE TABLE IF NOT EXISTS `ci_sessions` (
+ `session_id` varchar(40) NOT NULL default '0',
+ `peopleid` int(11) NOT NULL,
+ `ip_address` varchar(16) NOT NULL default '0',
+ `user_agent` varchar(50) NOT NULL,
+ `last_activity` int(10) unsigned NOT NULL default '0',
+ `left` int(11) NOT NULL,
+ `name` varchar(25) NOT NULL,
+ `status` tinyint(4) NOT NULL default '0'
+) ENGINE=MyISAM DEFAULT CHARSET=latin1;
+
+DROP TABLE IF EXISTS `domains`;
+CREATE TABLE IF NOT EXISTS `domains` (
+ `id` int(10) NOT NULL auto_increment,
+ `url` varchar(100) NOT NULL,
+ `name` varchar(100) NOT NULL,
+ `registrar` varchar(100) NOT NULL,
+ `dateregd` int(11) NOT NULL default '0',
+ `cost` float NOT NULL default '0',
+ `regdfor` int(11) NOT NULL default '0',
+ `notes` blob NOT NULL,
+ `pw` varchar(25) NOT NULL,
+ `un` varchar(25) NOT NULL,
+ `lastupdate` int(11) NOT NULL default '0',
+ `submit` varchar(25) NOT NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=10 ;
+
+DROP TABLE IF EXISTS `events`;
+CREATE TABLE IF NOT EXISTS `events` (
+ `id` int(10) NOT NULL auto_increment,
+ `name` varchar(50) NOT NULL default 'not set',
+ `type` enum('test','alert','report') NOT NULL,
+ `testid` int(10) NOT NULL,
+ `siteid` int(10) NOT NULL,
+ `userid` int(10) NOT NULL,
+ `reported` int(11) NOT NULL,
+ `result` blob NOT NULL,
+ `time` int(11) NOT NULL,
+ `timetaken` float NOT NULL,
+ `isalert` varchar(2) NOT NULL,
+ `emailid` int(11) NOT NULL,
+ `submit` varchar(25) NOT NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=69 ;
+
+DROP TABLE IF EXISTS `frequencies`;
+CREATE TABLE IF NOT EXISTS `frequencies` (
+ `id` int(10) NOT NULL,
+ `name` varchar(16) NOT NULL,
+ `submit` varchar(25) NOT NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=MyISAM DEFAULT CHARSET=latin1;
+
+DROP TABLE IF EXISTS `hosts`;
+CREATE TABLE IF NOT EXISTS `hosts` (
+ `id` int(11) NOT NULL auto_increment,
+ `cost` float NOT NULL,
+ `name` varchar(100) NOT NULL,
+ `hosturl` varchar(100) NOT NULL,
+ `un` varchar(50) NOT NULL,
+ `pw` varchar(50) NOT NULL,
+ `ns1url` varchar(36) NOT NULL,
+ `ns1ip` varchar(36) NOT NULL,
+ `ns2url` varchar(36) NOT NULL,
+ `ns2ip` varchar(36) NOT NULL,
+ `ftpurl` varchar(100) NOT NULL,
+ `ftpserverip` varchar(36) NOT NULL,
+ `ftpun` varchar(50) NOT NULL,
+ `ftppw` varchar(50) NOT NULL,
+ `cpurl` varchar(36) NOT NULL,
+ `cpun` varchar(36) NOT NULL,
+ `cppw` varchar(36) NOT NULL,
+ `pop3server` varchar(36) NOT NULL,
+ `servicetel` varchar(50) NOT NULL,
+ `servicetel2` varchar(50) NOT NULL,
+ `serviceemail` varchar(100) NOT NULL,
+ `webroot` varchar(48) NOT NULL,
+ `absoluteroot` varchar(48) NOT NULL,
+ `cgiroot` varchar(48) NOT NULL,
+ `booked` int(11) NOT NULL,
+ `duration` int(11) NOT NULL,
+ `lastupdate` int(11) NOT NULL default '0',
+ `submit` varchar(25) NOT NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=6 ;
+
+DROP TABLE IF EXISTS `people`;
+CREATE TABLE IF NOT EXISTS `people` (
+ `id` int(11) NOT NULL auto_increment,
+ `uname` varchar(25) NOT NULL,
+ `pw` varchar(25) NOT NULL,
+ `status` smallint(3) NOT NULL default '1',
+ `name` varchar(50) NOT NULL,
+ `firstname` varchar(50) NOT NULL,
+ `surname` varchar(50) NOT NULL,
+ `email` varchar(120) NOT NULL,
+ `lastupdate` int(11) NOT NULL default '0',
+ `submit` varchar(25) NOT NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=5 ;
+
+DROP TABLE IF EXISTS `sites`;
+CREATE TABLE IF NOT EXISTS `sites` (
+ `id` int(10) NOT NULL auto_increment,
+ `name` varchar(100) NOT NULL,
+ `url` varchar(100) NOT NULL,
+ `un` varchar(50) NOT NULL,
+ `pw` varchar(50) NOT NULL,
+ `client1` int(10) NOT NULL default '0',
+ `client2` int(10) NOT NULL default '0',
+ `admin1` int(10) NOT NULL default '0',
+ `admin2` int(10) NOT NULL default '0',
+ `domainid` int(10) NOT NULL default '0',
+ `hostid` int(10) NOT NULL default '0',
+ `webroot` varchar(50) NOT NULL,
+ `files` text NOT NULL,
+ `filesdate` int(11) NOT NULL default '0',
+ `lastupdate` int(11) NOT NULL default '0',
+ `submit` varchar(25) NOT NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=15 ;
+
+DROP TABLE IF EXISTS `tests`;
+CREATE TABLE IF NOT EXISTS `tests` (
+ `id` int(11) NOT NULL auto_increment,
+ `siteid` int(11) NOT NULL default '0',
+ `name` varchar(250) NOT NULL,
+ `type` varchar(25) NOT NULL,
+ `url` varchar(120) NOT NULL,
+ `regex` varchar(250) NOT NULL,
+ `p1` varchar(250) NOT NULL,
+ `p2` varchar(250) NOT NULL,
+ `p3` varchar(250) NOT NULL,
+ `p4` varchar(250) NOT NULL,
+ `p5` varchar(250) NOT NULL,
+ `p6` varchar(250) NOT NULL,
+ `frequency` int(10) NOT NULL default '0',
+ `lastdone` int(10) NOT NULL default '0',
+ `isalert` varchar(2) NOT NULL,
+ `setup` int(10) NOT NULL default '0',
+ `lastupdate` int(10) NOT NULL default '0',
+ `notes` varchar(250) NOT NULL,
+ `submit` varchar(25) NOT NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=11 ;
+
+DROP TABLE IF EXISTS `types`;
+CREATE TABLE IF NOT EXISTS `types` (
+ `id` varchar(7) NOT NULL,
+ `name` varchar(50) NOT NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=MyISAM DEFAULT CHARSET=latin1;
+
+
+
+
+
+ Simplifying HTML Pages and Forms
+
+
+This chapter covers yet another way in which CI helps save your time and make
+your coding more rigorous and logical.
+
+Firstly, we'll cover various ways of building views鈥攖he pages that control how you
+see the results prepared by your controllers and models.
+
+Next, you'll see how to create HTML forms quickly, and with built-in safeguards;
+and you'll also see how to validate your forms.
+
+I'm assuming that readers of this book are familiar with HTML and CSS. The
+following examples are very simplified, so we can focus on the CI code. And I have
+assumed that we have already written a CSS file and tucked it away somewhere
+on our site.
+
+
+
+Writing a View
+Views control how the user sees your website. They make it easy for you to present a
+consistent interface, and to change it if you need to. One of the advantages of MVC is
+that you separate presentation from logic, keeping everything much cleaner.
+
+So far, all we've done is to look at the very simple 'Welcome' view that installs out
+of the box when you first load CI. (See Chapter 3.) Now let's look at how to make it
+more elaborate.
+
+A view is just a set of HTML shelves to hold your content. The shelves may be one
+color or another; there may be lots of little ones, or just a few widely-spaced, elegant
+ones. But the view doesn't know or care what data is on those shelves. Like certain
+politicians, it is only interested in presentation.
+
+
+Simplifying HTML Pages and Forms
+
+To create a view, firstly you need to create a skeleton HTML web page as a PHP file.
+Let's call it basic_view.php. Save it in your application/views folder. (The reason
+for saving it in this folder is simply because the loader file looks for it there.)
+
+
+
+
+
Hello world!
+
+
+
+Then you just load it from a controller when you want to use it, using $this->load
+->view() inside an appropriate function:
+
+function index()
+{
+ $this->load->view('basic_view');
+}
+
+Note that if this were a model or a helper, you'd load it first, and then call it
+separately when you wanted to use it. With a view, calling it loads it as well, so you
+only need one line of code.
+
+Of course, that's an empty view. To make it useful, we need content. Say we want to
+add a title and some text. First we define them in the controller:
+
+function index()
+{
+ $data['mytitle'] = "A website monitoring tool";
+ $data['mytext'] = "This website helps you to keep track of the
+ other websites you control.";
+}
+
+Notice how we have defined them not as separate scalar variables, but as elements of
+the array $data (or any other name we care to give it.). For the first array entry, the
+'key' is 'mytitle' and the 'value' is "A website monitoring tool".
+
+Next, we call the view loading function:
+
+function index()
+{
+ $data['mytitle'] = "A website monitoring tool";
+ $data['mytext'] = "This website helps you to keep track of the
+ other websites you control.";
+ $this->load->view('basic_view', $data);
+}
+
+We have made the $data array into a second parameter of the
+$this->load->view() function, after the name of the view itself. Once the
+$data array reaches the view, CI uses PHP's extract() function to turn each
+element of the $data array into a separate variable, with the 'key' as the variable
+name, and the 'value' as the variable value. These variables can then be referenced
+directly in our view:
+
+
+
+
+
+
+
+
+
+
+You can only pass one variable of data to a view, but by building up an array of
+arrays, you can pack large amounts of information neatly into that one variable. It
+seems complicated, but is actually a tightly structured and elegant way of
+passing information.
+
+
+
+Long and Short PHP Syntax
+
+Before we go on, a note about different forms of PHP syntax.
+
+The normal way to include a PHP 'code island' in the midst of HTML code is
+like this:
+
+
+
+However, if you don't like this, CI also supports a shorter version:
+
+=$somevariable?>
+
+In this case, the external brackets delimiting the code island have lost the letters PHP
+(they are just ?>); and echo has been replaced by =. You can also use shorter
+syntax for if, for, foreach, and while loops. Complete instructions are in the online
+User Guide.
+
+Personally, I prefer to stick to the standard format because I'm used to it. If you
+use the short format, note that some servers won't interpret the abbreviated format
+correctly. If you still wish to use the short tags, then go to your config file, and alter
+the line:
+
+$config['rewrite_short_tags'] = FALSE;
+
+
+to TRUE. CI will then rewrite short tags to the normal form before it sends them to
+the server. However, if there is a PHP error, using this re-writing function makes
+the error messages less meaningful, so debugging may be more difficult. As I say, I
+prefer to stick to the standard format.
+
+For the record, CI also has a 'template parser' class, which allows you to put variables
+in your HTML code without any actual PHP coding at all. I haven't covered this. It
+is largely useful if you are working with HTML designers who may be confused by
+PHP code. Details of this class are available in the User Guide.
+
+
+
+Nesting Views
+
+So far, whether we use long or short PHP formats, this is pretty crude HTML.
+It would be nice, for instance, to put more information in the section of
+the page. Better still if this could be a standard chunk of each page. Once again,
+something we only have to write (or alter) once, and can then re-use, nesting this
+view inside other views whenever we need the boring HTML header stuff.
+
+Let's create a page header 'view' for our site, which displays a standard page header,
+as well as HTML declarations and meta information.
+
+First, we type out the code for our 'nested' header view:
+
+
+ >
+
+">
+
+Save this as views/header_view. It introduces new variables:
+
+ $mywebtitle, which is the page title (the meta tag; this won't show up on the
+
+ screen, but search engines will read it. It may vary from page to page, so I've
+ made it a variable.)
+ $myrobots, which I'm using for the standard instruction to robots that this
+
+ site is not to be indexed.
+ $base and $css, which describe the base URL and the extra URL for our
+
+ .css file, the stylesheet we're pretending we've already written, which lets
+ us apply formatting consistently. These variables will be generated from data
+ we have already stored in the CI.config file. (I could also have used the CI
+ config variable site_url instead of base.)
+
+What we need to know now is:
+
+ How do we call the second 'nested' view?
+
+ How do we assign values to its variables?
+
+
+There are two options. Firstly, calls to views can be made from within other views.
+So our main view, basic_view, just needs a new line:
+
+load->view('header_view'); ?>
+
+
+
+
+
+
+As for the variables, they can be assigned by two new lines in the original controller:
+
+function index()
+{
+ $data['mytitle'] = "A website monitoring tool";
+ $data['mytext'] = "This website helps you to keep track
+ of the other websites you control.";
+ $data['myrobots'] = '';
+ $data['mywebtitle'] = 'Web monitoring tool';
+ $data['base'] = $this->config->item('base_url');
+ $data['css'] = $this->config->item('css');
+ $this->load->view('basic_view', $data);
+}
+
+Here the new variables $myrobots, $css, $base, and $mywebtitle are created as
+new elements of the existing $data array, passed to basic_view, which unpacks
+them, and then made available to header_view when this is called by basic_view.
+(Just remember not to use the same variable name in two views that you are nesting,
+or one will overwrite the other.)
+
+A second way is to add the view from inside the controller, by assigning it to
+a variable:
+
+function index()
+{
+ $data['mytitle'] = "A website monitoring tool";
+ $data['mytext'] = "This website helps you to keep track of the other websites you control.";
+ $data['myrobots'] = '';
+ $data['mywebtitle'] = 'Web monitoring tool';
+ $data['base'] = $this->config->item('base_url');
+ $data['css'] = $this->config->item('css');
+ $data['header'] = $this->load->view('header_view', '', TRUE);
+ $this->load->view('basic_view', $data);
+}
+
+This is probably more correct from a strict MVC perspective.
+
+There are actually three parameters you can pass with the load->view function.
+
+ The first, header_view, is the name of the view to be loaded. This is essential.
+
+ The second, which is optional, is the data to load into it.
+
+ The third is a Boolean value. If you don't specify it, this defaults to FALSE,
+
+ and the view is sent to the browser. However, if you are nesting the view
+ this way, you want it returned as a string to nest inside the variable you are
+ passing to the host view. Setting the third parameter to TRUE achieves this.
+
+Now we've got a reference to the stylesheet built in, we can update the view to use
+display classes that we might have defined there:
+
+
+load->view('header_view'); ?>
+
+
+
+
+
+
+Notice again how CI's MVC system allows you to separate display from content. The
+views only provide 'shelves' for the content, and even the styling of those shelves is
+derived from a .css stylesheet.
+
+The view doesn't care what $mytext says, it just displays it on the right shelf with
+the right formatting. The controller that defines $mytext doesn't even know (or care)
+how the information it generates is displayed.
+
+So, if we need to change the look of our pages, or display them on a different system
+(say WAP), then we only need to change one view and one CSS stylesheet. We don't
+need to mess around inside the code of several controllers.
+
+And if we want to change the information displayed on the page, we don't need to
+touch the views, and remind ourselves to change some variable in each page we've
+written. We just change what the controller pushes out.
+
+Remember the 'loose coupling' principle? Once again, this makes it easier to design,
+upgrade, and maintain your sites.
+
+
+
+Practical Issues of Site Architecture
+
+Wait a moment, you're saying in our header_view, we generated the CSS stylesheet
+address dynamically:
+
+" />
+
+This means that the controller had to produce this data, which is only relevant to
+how the information is displayed, and we've just said the controller shouldn't know
+or care about that. Isn't that going right in the face of the 'loose coupling' principle
+that we had just set out? What's more, generating this information dynamically
+requires several operations: First, the controller has to look it up in the config file,
+then the controller has to package it in the $data array and pass it to the view, then
+the view has to extract the single variables $base and $css and look up their values.
+
+Seems like a roundabout way of doing things. Why not just embed the data statically
+in the view?
+
+
+
+The advantage of building this variable dynamically, despite breaking the MVC
+'rule', and despite the overhead of creating variables and passing them around, is
+that the code is then much more portable. If you move the site, or move your CSS
+file, you only have to change the code once in the config file, and every controller
+and view will reflect the change at once. If you hard-code the address in to each
+view, you'll have to spend time hunting through them for all those absolute URIs
+you wrote months ago. So which is best?
+
+There isn't a right answer. It depends on what your priorities are. The key is to apply
+MVC principles sensibly鈥攁s a tool rather than a straitjacket. CI allows you a lot of
+freedom to do this.
+
+A third option鈥攚hich I use鈥攊s to create a special 'display' model. This exists to
+build the standard page. It puts in headers and CSS file references and so on, and it
+accepts as a parameter the unique information the file will require. I'll show you this
+later on in this chapter.
+
+
+CI's Form Helper: Entering Data
+
+Let's move on to how you use your HTML pages. One of the most important parts of
+any dynamic site is interaction with the users, and this usually means HTML forms.
+Writing forms is repetitive and boring.
+
+The CI form helper is a very useful piece of code. It introduces a slightly different
+syntax, which makes form creation easier. Let's build a form to allow ourselves to
+enter data on our site about a new website. In a sites table, we want to enter the
+name, type, and URL of the website, and the date it was updated.
+
+You can build the form as a simple HTML file, or you can build it inside a controller,
+pack it into a variable, then call a view, and pass the variable to the view. I'm doing
+the second.
+
+Firstly, we have to load the form helper into the controller where we need to use it.
+Then, we put the following line in the controller's constructor function:
+
+$this->load->helper('form');
+
+and of course, we have to start the form.
+
+Now, for the form fields, instead of typing:
+
+$variable .= '';
+
+CI allows you to enter:
+
+$variable .= form_input('name', '');
+
+(Remember that 'name' is the title of the field, 'value' is what you want to go into
+it. Putting something in here gives you a default value, or you can dynamically fetch
+the existing value from the table.)
+
+Hmm, you say. 33 characters instead of 48, not much of a saving, particularly as I
+have to load the helper first (another 28 characters). Why bother? Well:
+
+
+Form Helper Advantage One: Clarity
+
+The first advantage of using the CI form helper is sheer clarity in your code. If you
+want a more elaborate input box, then in HTML you'd type:
+
+$variable = '';
+
+(Remember that type is the sort of box you want鈥攖ext, hidden, whatever.
+name is the name that will be used as the key for this value in the POST array.
+
+id is so that the box can be referenced on the page, if you're doing neat things with
+JavaScript.
+
+value is the existing or default value that you want the box to show when it comes
+up on the page.
+
+maxlength and size are obvious; style can be a set of HTML formatting or a
+reference to a .css style defined in your stylesheet.)
+
+CI uses an array instead:
+
+$data = array(
+ 'name' => 'url',
+ 'id' => 'url',
+ 'value' => 'www.mysite.com',
+ 'maxlength' => '100',
+ 'size' => '50',
+ 'style' => 'yellow'
+ );
+
+$variable = form_input($data);
+
+It looks longer, and actually it is: 145 characters as opposed to 110 for the simple
+HTML. However, it is much clearer, and easier to understand and maintain. This
+becomes even more obvious if you start to generate some of the values dynamically.
+
+Hidden form fields are very simple. Let's say we want to automatically record the
+date that our database was updated. We put the date in a $date variable, then:
+
+form_hidden('updated', $date);
+
+If you want a 'text area' box, to give your user more than one line to enter data, say
+for URLs, which may be quite long, use CI's form_textarea() function. If you're
+happy with a default size, this would simply read:
+
+$data = array(
+ 'name' => 'url',
+ 'id' => 'url',
+ 'value' => 'www.mysite.com'
+ );
+
+$variable = form_textarea($data);
+
+CI's form helper is a great help when you write dropdowns and checkboxes or radio
+boxes. Let's say we want to change our 'url' field to a drop-down box, to allow the
+reader to select one URL from a list of several. First, list the options in an array, then
+use the form_dropdown() function:
+
+$urlarray = array(
+ '1' => 'www.this.com',
+ '2' => 'www.that.com',
+ '3' => 'www.theother.com'
+ );
+
+$variable = form_dropdown('url', $urlarray, '1');
+
+The first value passed to the form, url, is the field name in the site table which we
+intend to update; the second is the array of options, the third is the key of the option
+you want to set as default. In other words, if the user accepts the default value, your
+$_POST array will contain the value 'url => 1', but your user will see the option
+'www.this.com'.
+
+Compare this to the plain vanilla HTML, you would otherwise have to write:
+
+
+
+Now CI's code is actually shorter (128 characters as opposed to 154), as well as much
+easier to follow.
+
+If you store your list of possible URLs in a separate database table (say it's called
+'urls'), then generating a dynamic drop-down box is easy. First generate an array
+of all possible values:
+
+$urlarray = array();
+$this->db->select('id, url');
+$query = $this->db->get('urls');
+if ($query->num_rows() > 0)
+{
+ foreach ($query->result() as $row)
+ {
+ $urlarray[$row->id] = $row->url;
+ }
+}
+
+then repeat the CI form_dropdown() function we used before:
+
+echo form_dropdown('type', $urlarray, '1');
+
+Only the contents of $urlarray have changed; this line of code remains the same.
+
+If you are updating an entry rather than creating a new one, you don't want to show
+your user a default value. You want to show the already existing value for that entry.
+You should already know the id number of the entry you want to update, so you
+need to do a database lookup of the 'sites' file first. Make sure you use a different
+variable name for the second query and the second 'row' variable, or they may
+overwrite the first set you wrote:
+
+$this->db->select('id, url, name');
+$this->db->where('id','$id')
+$sitequery = $this->db->get('sites');
+$siterow = $sitequery->row();
+
+Then your CI form drop-down function will read:
+
+echo form_dropdown('url', $urlarray, $siterow->url);
+
+There isn't room here to go through all the options that the form helper offers. It can
+handle checkboxes, hidden fields, radio boxes, and others. It is fully explained in CI's
+online User Guide.
+
+
+Form Helper Advantage Two: Automation
+
+The second advantage of using the form helper to create your HTML forms is that it
+automates some things you would otherwise have to script yourself.
+
+Firstly, it intercepts HTML and characters such as quotes, which the user may enter,
+and escapes them to stop them from breaking the form.
+
+Secondly, it automates links. When you open a form, you have to specify the target
+page, which will receive the form data and process it. (In CI, this is a function within
+a controller rather than an actual static page. Let's say the update function of the
+websites controller.) So, if you were using plain HTML code, you'd write:
+
+
+
+Whereas, if you open your form with CI, you only need to use:
+
+form_open('websites/update');
+
+CI automatically works out the base URL from the value in your config file and
+points the form there. Once again, if you move your site, you won't find that your
+forms are broken, and have to hunt through for hard-coded URLs to update.
+
+Note incidentally, that CI assumes your forms will always send POST data rather
+than GET data. CI makes extensive use of the URLs itself, so this avoids confusion.
+
+
+
+My 'Display' Model
+
+As promised (and slightly simplified) here is my display model:
+
+load->helper('form');
+ $this->load->library('user_agent');
+ $this->load->library('errors');
+ $this->load->library('menu');
+ $this->load->library('session');
+/*now set the standard parts of the array*/
+ $this->data['css'] = $this->config->item('css');
+ $this->data['base'] = $this->config->item('base_url');
+ $this->base = $this->config->item('base_url');
+ $this->data['myrobots'] = '';
+/*note that CI's session stuff doesn't automatically recall the extra
+variables you have added, so you have to look up the user's status in
+the ci_sessions table*/
+ $sessionid = $this->session->userdata('session_id');
+ $this->db->select('status');
+ $this->db->where('session_id', $sessionid);
+ $query = $this->db->get('ci_sessions');
+ if ($query->num_rows() > 0)
+ {
+ $row = $query->row();
+ $this->status = $row->status;
+ }
+ }
+/*function to assemble a standard page. Any controller can call this.
+Just supply as $mydata an array, of key/value pairs for the contents
+you want the view to display. Available variables in this view are:
+mytitle. menu, mytext, diagnostic
+*/
+ function mainpage($mydata)
+ {
+ $this->data['mytitle'] = 'Monitoring website';
+ $this->data['diagnostic'] = $diagnostic;
+ foreach($mydata as $key => $variable)
+ {
+ $this->data[$key] = $variable;
+ }
+/*here's the menu class we looked at in Chapter 3*/
+ $fred = new menu;
+ $this->load->library('session');
+ $mysess = $this->session->userdata('session_id');
+ if(isset($this->status) && $this->status > 0)
+ {
+ $this->data['menu'] = $fred->show_menu($this->status);
+ }
+ $this->load->view('basic_view', $this->data);
+ }
+}
+?>
+
+I can call the main page from any controller with the lines:
+
+$this->load->model('display');
+$this->display->mainpage($data);
+
+and I then know that my view is being assembled dynamically, exactly as I want it.
+
+
+CI's Validation Class: Checking Data Easily
+
+One chore when you write HTML forms is validating user input. We all know that
+we should do it, but鈥 So far we've written a simple form, which will trustingly
+accept any data the user enters. You should always assume that a small minority of
+your users are malicious, and all the others are stupid. (Just don't tell them.) If they
+can make a simple mistake, they will. Validating your form tests the information
+they submit, to make sure it fits rules you specify.
+
+You can do it on the client side, using JavaScript but this is of limited value as a
+security precaution, as the user can easily circumvent it. Validating the data on the
+server side means an extra round trip to the server, but it's worth it for the added
+peace of mind.
+
+It's also quite complex to write the code, but鈥攜ou guessed it鈥擟I has a validation
+class that works hand-in-glove with the forms helper to make validation easier.
+
+Let's change our own form submission process to reflect this. You need to make
+some changes in the form, and also in the function to which it points.
+
+If your form begins with form_open('sites/update'), the function you need to
+modify is the 'update' function in the 'sites' controller. If you aren't using the CI
+form helper, the HTML equivalent is:
+
+
+
+You need to do three things:
+
+ 1. Set up validation
+ 2. Set up the controller
+ 3. Set up the forms
+
+
+Set Up Validation
+
+In the function to which your form points, load the validation library and declare
+your validation rules:
+
+$this->load->library('validation');
+
+$rules['url'] = "required";
+$rules['name'] = "required";
+
+$this->validation->set_rules($rules);
+
+Each entry must have something in the 'url' and 'name' fields. CI gives us various
+options for specifying what that something should be, and the User Guide explains
+them in full. They're fairly self-evident: min_length[6] obviously means a valid
+entry in the field must have 6 characters or more, numeric means it must not contain
+letters, etc. You can also combine rules, using the 'pipe' character to separate them:
+
+$rules['name'] = "required|alpha|max_length[12]";
+
+would require a name of 12 alphabetical characters or less. You can even write your
+own rules.
+
+Set Up the Controller
+
+Still in the same function, create an 'if/else' loop:
+
+if ($this->validation->run() == FALSE)
+{
+ $this->load->view('myform');
+}
+else
+{
+ $this->load->view('success');
+}
+
+You run the validation test, and if the entries don't pass the test, you go back to the
+entry form. (If you're generating your view in a function inside a controller, say
+because it has dynamic drop-down fields, then point back to that function instead: :
+$this->myfunction rather than $this->load->view('myform');
+
+If your validation run is successful, create another view ("success") to tell the user
+that the entry has been accepted, and to give whatever navigation options you want
+him or her to have to move on.
+
+
+Set Up the Forms
+
+The entry form that we have built up also needs to be tweaked. Simply returning the
+user to the form each time an entry doesn't pass the validation tests will make your
+users really cross! You have to say which field failed, and why. So you have to echo
+out an extra line somewhere in the form:
+
+$this->validation->error_string;
+
+This prints out the appropriate messages, and saves the user a lot of frustration.
+
+You also need to arrange for the form fields that were correctly entered to be
+re-populated with the correct values鈥攐therwise, the user will have to re-enter all the
+fields each time she or he makes a mistake in one of them. Another way to get her or
+him really cross!
+
+Firstly, you need to go back to the controller and add some more code. Immediately
+after the validation rules you set up, create an array of each field you want to
+re-populate. The array key is the field name as actually used in your table; the
+array value is what you want the field to be called in the error message:
+
+$fields['url'] = 'The URL of your site';
+
+Afterwards, add the line:
+
+$this->validation->set_fields($fields);
+
+Now you've set up an array in the controller to store the existing values, you only
+need to add lines in your form to echo them back to the user. For a simple text line,
+this would be:
+
+
+
+or, if you're using the CI form helper:
+
+$variable .= form_input('url', $this->validation->url);
+
+If this is a new entry form, that should be enough. If you are using the form to
+update an existing entry, then, the first time the form appears, its value should be
+whatever it was set to beforehand. (Remember the code example above, where we
+looked that up and called it $siterow->url?)
+
+But say you are using your form to update an existing entry, and it bounces back
+because one field doesn't validate, you want the value of every other field in your
+form to be whatever you had changed it to. Otherwise, you'd have to retype the
+whole form because of one validation error.
+
+This is easily accomplished with an 'either/or' loop. Something like:
+
+if(isset($_POST['url']))
+{
+ $myvalue = $this->validation->url;
+}
+else
+{
+ $myvalue = $siterow->url;
+}
+
+The first time the form appears, there will be nothing in the post array; so you take
+values from the underlying table. But once you submit it, the post array will be
+populated, so you take the values from the validation function.
+
+See the CI User Guide for several other things you can do with form validation. You
+can use it to:
+
+ Automatically prepare your data, e.g., by trimming it or removing potential
+
+ cross-site scripting attacks
+ Write your own complex validation criteria, e.g., that the value the user has
+
+ entered must not already exist in your database
+ Write your own error messages
+
+The validation class is a very useful and powerful part of CI and well worth the time
+it takes to understand it.
+
+
+Summary
+
+We've looked at ways in which CI generates 'views', and how it allows you to create
+'mini-views', which you can nest inside other views. This means that you can set
+up a title page, or a part of your display, once, and then use it over and over again,
+keeping your display separated from your content.
+
+We've also seen how CI helps you through the chore of writing HTML forms, with a
+set of helpers that simplify the process and cut down on actual coding.
+
+Lastly, we've looked at CI's validation class, which is a powerful tool for keeping an
+eye on what your users actually try to enter. Nothing's perfect, but this goes a long
+way towards stopping users form entering rubbish, or trying to exploit security holes
+in your site. It also looks much more professional when your site politely but firmly
+catches out user mistakes, rather than silently accepting meaningless entries.
+
+On the way, we've also looked at the MVC process again, and made a choice
+between the strict application of MVC principles, deliberately breaking these 'rules'
+to make life easier. CI has a very flexible philosophy: use the tools if you want to,
+but鈥攑rovided you understand the issues鈥攆eel free to do it some other way if that
+suits your priorities better.
+
+
+
+
+Simplifying Sessions and Security
+
+Enough theory! Now let's begin to write our own application. In this chapter, we'll
+outline an application that we're going to build, and we'll look at one of the basic
+questions affecting any website i.e. session management and security.
+
+In this chapter, we'll see:
+
+ How to make your pages secure
+ 鈥
+ How to use CI's sessions class
+ 鈥
+
+
+
+Starting to Design a Practical Site with CI
+We've looked at the CI welcome page and seen how it's built up by a controller file
+and a view file. That's the equivalent of 'hello world'.
+
+Once upon a time, hobby sites written by amateurs used open-source code and were
+often regarded as inferior to large 'enterprise' sites written by teams of programmers
+using complex procedures.
+
+The landscape has changed. Major companies now use open-source technology.
+For example, NASA and Associated Press use MySQL, the US GAO and even
+Yahoo are using PHP for certain applications. And I believe that the market for the
+flexible 'medium sized' application is growing, as large companies realize that their
+legacy apps can't handle new tasks. It's sometimes easier to build a small, flexible
+programme than to revise the old one.
+
+CI offers a bridge between 'home-made' code and the structured reliability of
+'enterprise' sites. It holds your hand and helps you to program better and produce
+more consistent and reliable results.
+
+
+Simplifying Sessions and Security
+
+To demonstrate the flexibility of CI, let's build our own application.
+
+To re-state my requirements, I want to build something for a specific purpose. I run
+several websites, some of them for myself, and some for quite demanding clients. I
+need to maintain these sites, test them, and generally keep a track of them. Mostly,
+this is routine stuff. I could hire someone to do it for me, but it would be cheaper to
+write a website to automate as much of the process as possible.
+
+So my requirements are:
+
+ 1. To manage one or more remote websites with a minimum of
+ human intervention
+ 2. To run regular tests on the remote sites
+ 3. To generate reports on demand, giving details of the site and of
+ tests conducted
+
+I want to be able to set the site to run on a Cron, if my ISP allows it; if not, to run it
+myself twice a day or once an hour (as I please), and let it conduct a pre-arranged
+pattern of tests.
+
+I don't want to know the details, unless something goes wrong (then I'd like an email
+telling me exactly what happened and exactly where), but I do want to be able to
+print out management reports for my clients to impress them with the regular and
+comprehensive checks I'm doing, and the (hopefully!) flawless performance of
+their sites.
+
+ To avoid making the code too long and repetitive the code in this book
+ is not very secure, so please bear that in mind if you use it for real.
+ This chapter covers a basic means of securing your site's pages against
+ unauthorized users, but other PHP security issues, which aren't unique to
+ CodeIgniter, are outside the scope of this book.
+
+At this stage, we're going to look at CI's approach to things that are generic to most
+dynamic websites. So we'll leave the detailed design of our site until later. Let's start
+with some of the very basic items.
+
+
+
+Moving Around the Site
+Any website is a collection of separate programmes, and it's essential that they are
+able to talk to each other. As we saw in Chapter 3, CI links them by their URLs.
+Typically, URLs take the pattern:
+
+
+
+
+ base url http://www.mysite.com. This is the plain vanilla address
+ everyone uses to access your site. Readers don't need to know all
+ the rest of the URL structures because the site builds them up as it
+ needs them.
+ index file Segment 1: index.php
+ This is the main file that CI starts off with every time the site is hit.
+ class (or controller) Segment 2: start
+ If no controller is set, CI defaults to the controller you specified in
+ the config file鈥攕ee below.
+ method (or function) Segment 3: assessme
+ If no method is set, CI defaults to the index function of the
+ controller, if there is one. If not, you get a '404' page.
+ plus any parameters Segment 4: fred (and Segment 5: 12345, Segment 6: hello, etc.)
+
+So, to call the assessme method in the start controller with the parameters fred
+and 12345, your URL will be:
+ http://www.mysite.com.index.php/start/assessme/fred/12345.
+
+This code expects your site to contain a controller called start.php that includes
+a method assessme, which expects two parameters. A URL like this will call any
+function in any controller on your site. So it's ideal for a menu-based page.
+
+For a practical example of how this can work, let's set up the first page the user sees
+on our site. We'll set up a controller called start, and make it our default controller.
+
+Well, first of all, CI, as it comes out of the box, is set to go to the welcome
+controller by default, so I need to change this. CI's default route is held in the
+/system/application/config/routes file. At the moment this reads:
+
+$route['default_controller'] = "welcome";
+
+So I'll change it to:
+$route['default_controller'] = "start";
+
+(Just remember, from the table above, if your default controller doesn't have a
+default index method, you'll get a 404 error every time anyone logs on to your plain
+vanilla base URL, which is not a good idea!)
+
+Now I need to write my new start controller. Remember the basic format:
+
+mainpage();}
+ }
+}
+?>
+
+Save this in the /system/application/controllers folder as start.php. (Note the
+cases: Start has an upper-case letter in the class name and constructor function, but
+not in the saved file name.)
+
+The second line tells you that this is a controller. Then the constructor function starts
+and loads the parent controller class methods. The assessme function expects two
+variables $name and $password. CI (from version 1.5 onwards) automatically assigns
+any URL segments after the second as parameters, so fred and 12345 will become
+the $name and $password parameters, respectively.
+
+So, if I type in the URL on the previous page, I'll be re-directed to the mainpage()
+function. We'll set this up later in the start controller. (If not, then the code will
+just die.)
+
+For those more used to procedural PHP than OO classes, please note a function
+within a class has to be addressed as $this->xxxx. So, if I'm calling the mainpage()
+function of the start controller from another function within the start controller, I
+have to call it $this->mainpage(). Otherwise, CI won't be able to find it.
+
+Of course, it's unlikely that anyone would type in a URL like:
+http://www.mysite.com.index.php/start/assessme/fred/12345.
+
+Mostly, they will just enter
+http://www.mysite.com
+
+and expect the site to sort out all the internal navigation. So let's start that now.
+
+Often, the first thing you see on a site is a log-in form. So let's prepare one of those.
+First, I add a new function to my start controller. I want the site to default to this
+function, so I'll call it index():
+ function index()
+ {
+ $data['mytitle'] = "My site";
+
+ [ 88 ]
+
+
+ Chapter 6
+
+ $data['base'] = $this->config->item('base_url');
+ $data['css'] = $this->config->item('css');
+ $data['mytitle'] = "A website to monitor other websites";
+ $data['text'] = "Please log in here!";
+
+ $this->load->view('entrypage', $data);
+ }
+
+This is calling a view, entrypage. The view includes a form, and the form allows the
+user to submit a password and username. HTML forms must point to a page that
+will handle the data in the $_POST array. We've already written the function in our
+start controller to receive this: it's assessme(). In plain old HTML, the form on our
+view should begin:
+
+
+I've explained the assessme function a little. There's not much point in a function
+that only has one username锟絧assword combination. I need some way to look it up
+in a database. To make the structure more modular, I'm going to hand that over to
+another function, checkme().
+
+So, as you will see, assessme() calls checkme(). Checkme() does some sort of test
+on the password and username (we haven't written that yet) and returns 'yes' or 'no'
+to assessme(). If it's yes, assessme() calls another function, mainpage(), which
+returns a view.
+
+Notice the advantage of the modular approach. Each function has a role. If I need
+to alter the way the system checks a password, I only have to alter the checkme()
+function. If I need to alter the page it displays on a correct response, then I go to the
+mainpage() function.
+
+Let's just look at the structure of the code and the way the sections interact. (Note
+that in order to make the example simpler to follow, we are not 'cleaning' the input
+from our form here. Of course, this leaves your code open to problems. CI's form
+class automatically sanitizes entered data.
+ /*receives the username and password from the POST array*/
+ function assessme()
+ {
+ $username = $_POST['username'];
+ $password = $_POST['password'];
+ /*calls the checkme function to see if the inputs are OK*/
+
+
+
+
+ [ 89 ]
+
+
+Simplifying Sessions and Security
+
+ if($this->checkme($username, $password)=='yes')
+ {
+ /*if the inputs are OK, calls the mainpage function*/
+ $this->mainpage();
+ }
+ /*if they are not OK, goes back to the index function, which
+ re-presents the log-in screen */
+ else{
+ $this->index();
+ }
+ }
+ /*called with a u/n and p/w, this checks them against some list. For
+ the moment, there's just one option. Returns 'yes' or 'no'*/
+ function checkme($username='', $password='')
+ {
+ if($username == 'fred' && $password == '12345')
+ {return 'yes';
+
+ else{return 'no';}
+ }
+
+On lines 5-6, assessme() receives the output of the form from the $_POST array. This
+will contain something like:
+ [username] => fred [password] => 12345
+
+The assessme() function passes these two variables to another function, checkme().
+This simply tests if they are fred and 12345, respectively, and if they are, it returns
+'yes'. Obviously, on a real site this would be more complex. You would probably do
+a database lookup for valid username锟絧assword pairs. Making it a separate function
+means I can test the rest of my code now, and improve the checkme() function later,
+at my leisure.
+
+If the username and password are a valid combination, the assessme() function
+calls another function, mainpage(), which lets me into the site. Otherwise, it goes
+back to showing me the index() function鈥攖hat is, the log-in form again.
+
+The next problem we have is how to manage state. In other words, how to recognize
+the logged-in user when (s)he makes another page request.
+
+
+
+
+ [ 90 ]
+
+
+ Chapter 6
+
+
+Security/Sessions: Using Another CI
+Library Class
+If I want to build a session mechanism that will keep unwanted users from accessing
+my files, how many lines of code will it take?
+
+The Internet works by a series of requests. Your browser makes a request to my
+server to see a particular page. My browser passes the page back to your server. You
+look at it, and perhaps need to make another request, so you click on a hyperlink,
+which makes a request to my server. And so on.
+
+The Internet is 'stateless'鈥攖hat is, each request made by your browser to my website
+is treated as a separate event, and the HTTP protocol, which underlies the Internet,
+has no direct way of linking your request to any other requests (that you may have
+made). It's as if you were in a restaurant, the waiter takes your order, and brings you
+your meal, but then forgets all about you. That's fine, until he needs to bring you a
+bill, or to remember that you are entitled to a special discount, or simply remember
+that you wanted him to ring for a taxi for you after you've finished your meal.
+
+If you want your website to connect one page request with another, you have to
+manage the 'state' of the relationship: somehow to let the website know that some
+requests are coming from the same browser, and should be treated specially.
+
+PHP offers two ways of managing state: using cookies, or a specially generated
+session ID. The PHP session function automatically checks if the website is accepting
+cookies; if not, it uses the session ID method which is passed via the URL.
+
+ Cookies are small strings of data that websites pass back to any browser
+ that accesses the site. The browser automatically stores it away. Once
+ the cookie is there, the website can check for it when the browser next
+ attempts to access the site. If it finds the right cookie, it can use the
+ information in it to configure itself appropriately. This may mean closing
+ off certain pages to unauthorized users, or adding personal information.
+ In our restaurant analogy, the waiter would leave your bill on the table,
+ and next time he saw you, that would remind him that you were entitled
+ to 15% discount, so he could take that into account when working out
+ your bill.
+
+Because some people set their browsers not to accept cookies, PHP offers an
+alternative approach. Each time a browser requests access, the site generates a
+random string called the 'session ID', and returns it to the browser. Browsers then
+add this to the URL when they make their next request, so that the site can recognize
+the browser. (Instead of the waiter leaving the bill on your table, you make him carry
+it back and forth with him to the kitchen.)
+ [ 91 ]
+
+
+Simplifying Sessions and Security
+
+CI has a session class that handles much of the same stuff. In fact, it reduces a lot
+of coding to one line. We saw in the last chapter that CI has a wide range of 'library
+classes', which simplify most of the common tasks that a website deals with. These
+are the core of frameworks: pre-written chunks of highly abstracted code, which
+perform essential functions for you. All you need to know is where they are, how to
+address them and use their methods, and what parameters they expect. In return,
+you get to use professional code without having to write it!
+
+If you want to use the functionality inside a class from within your controller or
+model, you must remember to first load the class into the controller or model. (A few
+classes, such as config are always automatically loaded, which is why we haven't
+loaded it in any of our code so far.)
+
+You load a library class simply:
+ $this->load->library('newclass');
+
+Normally, put this line in the constructor of your controller or model.
+
+If you think you will use a library class in every controller, you can have it load
+automatically just as the config class does. Go to the /system/application/
+config/autoload file, and add the name of the class you want into the line:
+
+ $autoload['libraries'] = array();
+
+So that it looks like this:
+ $autoload['libraries'] = array('newclass','oldclass');
+
+The library class that we're going to use first is the session class, which helps you to
+maintain state, and to identify users. It's quite simple to do this. Here's our enlarged
+assessme() function from our start controller with the new lines highlighted:
+
+ function assessme()
+ {
+ $this->load->library('session');
+ $username = $_POST['username'];
+ $password = $_POST['password'];
+
+ if($this->checkme($username, $password)=='yes')
+ {
+ $this->mainpage();
+ }
+ else{$this->index();}
+ }
+
+
+
+
+ [ 92 ]
+
+
+ Chapter 6
+
+(I've loaded the session library at the start of the function so you can see it, but
+normally, I'd load it in the controller's constructor, so it is loaded for all the other
+functions in this class.)
+
+Just loading the session class immediately gives you a huge chunk of functionality
+in exchange for the one line of code. It will automatically read, create, and
+update sessions.
+
+Well, to be frank, it's not quite one line of code. You have to make some changes to
+the config file first, to tell the session class what you want it to do.
+
+Check your system/applications/config/config.php file, and you'll find a
+section like this:
+ ---------------------------------------------------------------------
+ -----
+ | Session 锟絘riables
+ |--------------------------------------------------------------------
+ ------
+ |
+ | 'session_cookie_name' = the name you want for the cookie
+ | 'encrypt_sess_cookie' = TRUE/FALSE (boolean). Whether to encrypt
+ the cookie
+ | 'session_expiration' = the number of SECONDS you want the session
+ to last.
+ | by default sessions last 7200 seconds (two hours). Set to zero for
+ no expiration.
+ |
+ */
+ $config['sess_cookie_name'] = 'ci_session';
+ $config['sess_expiration'] = 7200;
+ $config['sess_encrypt_cookie'] = FALSE;
+ $config['sess_use_database'] = FALSE;
+ $config['sess_table_name'] = 'ci_sessions';
+ $config['sess_match_ip'] = FALSE;
+ $config['sess_match_useragent'] = FALSE;
+
+For now, make sure sess_use_database is set to FALSE.
+
+Now, every time your users connect, the site will save a cookie on your machine,
+containing the following information:
+
+ A unique Session ID generated by CI (not to be confused with a PHP session
+ 鈥
+ ID string, which isn't generated in this instance). This is a random string
+ created by CI for this session.
+ The user's IP Address
+ 鈥
+
+ [ 93 ]
+
+
+Simplifying Sessions and Security
+
+ The user's User Agent data (the first 50 characters of the browser data string)
+ 鈥
+ Timestamps for "last activity"
+ 鈥
+
+If you set sess_encrypt_cookie to FALSE, you can read the cookie on your browser
+and see what has been saved (it's partly encoded, but you can make it out) e.g.:
+ ip_address%22%3Bs%3A9%3A%22127.0.0.1%22%3Bs%3A10%3A%22
+
+includes the user's URL鈥攊n this case, 127.0.0.1). If you set it to TRUE, the cookie
+is encrypted, just a string of random gunk. Your browser can't even distinguish
+separate sections of the cookie, which means that the user can't meaningfully alter it
+without invalidating it.
+
+When the user makes another page request, the site can then check whether the
+session ID has been saved on the user's browser as part of the cookie. If it has, you
+know they are part of an existing session. If not, you know they are a new session.
+Provided I remember to load the CI session class on all my other controllers as well,
+CI will make the same checks for them too. All I have to do is tell each controller how
+to behave if there isn't a cookie.
+
+
+Turning Sessions into Security
+This in itself doesn't make a security system. Anyone who visits the site starts a
+session. The code just records whether they are a new visitor or not. One way of
+preventing unauthorized access to some pages involves adding something else to
+their cookie if they are 'logged in', so that I can test for that. Then, if they enter the
+correct username and password once, that will be recorded in the cookie, and the
+session mechanism will find it when it checks for cookies as each new request comes
+through. I can then test for that, and if I find it, the site will let them into protected
+pages for the rest of the session. They won't have to keep on logging in.
+
+Adding something to the cookie is easy. In my assessme() controller, once I have
+decided if the password and username are acceptable, I add:
+ if($this->checkme($username, $password)=='yes')
+ {
+ $newdata = array(
+ 'status' => 'OK',
+ );
+ $this->session->set_userdata($newdata);
+ $this->mainpage();
+ }
+
+
+
+
+ [ 94 ]
+
+
+ Chapter 6
+
+That takes the contents of my $newdata array鈥攋ust one variable in this case鈥攁nd
+adds it to the cookie string. Now, whenever the password锟絬sername combination is
+acceptable, assessme() will add 'OK' to the cookie, and I can start each controller
+with this code:
+ /*remember to load the library!*/
+ $this->load->library('session');
+ /*look for a 'status' variable in the contents of the session cookie*/
+ $status = $this->session->userdata('status');
+ /*if it's not there, make the user log in*/
+ if(!isset($status) || $status != 'OK')
+ { /*function to present a login page again鈥*/}
+ /*otherwise, go ahead with the code*/
+
+Here, you have the basis of a security fence around your site. You can easily make
+it more elaborate. For instance, if some of your users have higher access levels than
+others, you can store a level in the status variable rather than 'OK'鈥攖hen you can
+use this in conditional tests to control access to functions.
+
+Saving this sort of data in a cookie is frowned upon because the user can easily
+rewrite the cookie on their machine between visits to your site. Given that CI's
+session class encrypts it, you're fairly safe. However, the alternative is to create a
+database of users, and after one has logged in, to write the 'OK' to the database
+against that session ID. Then, for subsequent accesses, you check the session ID (in
+the cookie) against the database, to see whether it has 'OK' or a level against it.
+
+It is very simple to save the session data in your database. First, create the database
+table. If you're using MySQL, use this SQL query:
+ CREATE TABLE IF NOT EXISTS `ci_sessions` (
+ session_id varchar(40) DEFAULT '0' NOT NULL,
+ ip_address varchar(16) DEFAULT '0' NOT NULL,
+ user_agent varchar(50) NOT NULL,
+ last_activity int(10) unsigned DEFAULT 0 NOT NULL,
+ status varchar(5) DEFAULT 'no',
+ PRIMARY KEY (session_id)
+ );
+
+Then, alter the connection parameters in the system/application/config/
+database.php file to tell CI where the database is. See Chapter 4 for more details
+on databases.
+
+If all works, you will see the data build up in your database table as you connect
+and disconnect. If you have sessions stored in a database table, as each user connects
+to your site, the site tests for a cookie. If it finds one, you can then have it read the
+session id, and match this against the session ids stored in the database.
+ [ 95 ]
+
+
+Simplifying Sessions and Security
+
+You now have a robust session mechanism. And all of this came from one line
+of code!
+
+Just one caveat. The native PHP session class can cope with users who turn off
+cookies on their browsers. (Instead of storing a cookie, it adds session data to the
+URL string.) The CI class does not do this. If the user has turned off cookies, then
+(s)he can't log on to your site. Whether this is a problem for you depends on the
+people you expect to use your site. This is one enhancement I hope Rick Ellis will
+soon make to CI.
+
+
+
+Security
+Notice that the session class automatically stores information about the IP address
+and user agent of the user making a page request. You can use these to give
+additional security.
+
+There are two settings you can change in your config file for additional security:
+
+ sess_match_ip: If you set this to true, CI will attempt to match the user's
+ 鈥
+ IP address when it reads the session data. This is to prevent users from
+ 'hijacking' a log-in. However, some servers (both ISPs and large corporate
+ servers) may issue requests by the same end user over different IP addresses.
+ If you set this value to true, you may exclude them unintentionally.
+ sess_match_useragent: If you set this to true, CI will try to match the User
+ 鈥
+ Agent when reading the session data. This means that someone who tried to
+ hijack a session would have to ensure that the 'user agent' setting returned
+ by his or her system matched that of the genuine user. It makes hijacking a
+ little more difficult.
+
+CI also has a user_agent class, which you load like this:
+ $this->load->library('user_agent');
+
+Once loaded, you can ask it to return various information about any agent browsing
+your site, for instance, the type of browser and operating system, and in particular
+whether it is a browser, mobile, or robot. If you want to list the robots that visit your
+site, you might do it like this:
+ $fred = $this->agent->is_robot();
+ if ($fred == TRUE)
+ {$agent = $this->agent->agent_string();
+ /*add code here to store or analyse the name the user agent is
+ returning*/
+ }
+
+
+ [ 96 ]
+
+
+ Chapter 6
+
+The class works by loading, and comparing against, the array of user agents,
+browsers, and robots contained in another of the config files: system/
+application/config/user_agents.
+
+If you wished, you could easily develop this to enable your site to lock out certain
+types of browser or certain robots. However, remember that it is easy for an attacker
+to write robot user agents, and have them return whatever user_agent string you
+want, so they can easily masquerade as common browsers. Many robots, including
+ones like the Googlebot listed in CI's user_agents array, are 'well-behaved'. This
+means that if you set your robots.txt file to exclude them, they won't trespass.
+There is no easy way of excluding robots that don't obey this file, unless you know
+their names in advance!
+
+In CI, the session mechanism stores the IP of the requesting site, so you could use
+this to operate a black-list of sites. Retrieve the IP from the session like this:
+ /*remember to load the library!*/
+ $this->load->library('session');
+ /*look for an 'ip_address' variable in the contents of the session
+ cookie*/
+ $ip = $this->session->userdata('ip_address');
+
+Then you can test the $ip variable against a blacklist.
+
+You could also develop CI's session mechanism to limit the damage from repeated
+requests鈥攕uch as denial of service attacks where a robot is set to overload your
+site by repeatedly asking for pages. Or you could use this mechanism to handle
+'dictionary' attacks, where a robot is set up to call your log-in form repeatedly, and
+try hundreds or thousands of password/username combinations until it finds the
+right one.
+
+You can do this because CI's sessions class stores the last_activity time for each
+session. Each time a page is requested, you can check how long ago the last request
+was made by this user. While one time interval doesn't tell you very much, you can
+set the system to store more data and to develop usage patterns. A dictionary attack
+relies on getting a speedy reply, otherwise it will take too long. If you detected too
+many requests in rapid succession, you could either end the session, or slow down
+the response.
+
+
+
+
+ [ 97 ]
+
+
+Simplifying Sessions and Security
+
+
+Summary
+We've outlined an application that we'd like to build, and attacked the first issue that
+almost any application raises: session management and (if we want to protect parts
+of our site from unauthorized users) security.
+
+To do this, we've looked at the CI sessions class in some detail, and seen how it
+creates session records and leaves cookies on the visitor's browser.
+
+It can then look for cookies when subsequent requests are made, and you can use the
+response to control the way your site responds.
+
+
+
+
+ [ 98 ]
+
+
+ CodeIgniter and Objects
+This is the geek chapter. It describes the way CodeIgniter actually works, 'under the
+hood'. If you are new to CI, you may want to skip it. However, sooner or later, you
+may want to understand why things happen in certain ways鈥攁s opposed to just
+knowing that they do.
+
+Objects confused me when I started to use CodeIgniter. I came to CodeIgniter via
+PHP 4, which is a procedural language, not really an Object-Oriented (OO) language.
+I duly looked up objects and methods, properties and inheritance, and encapsulation,
+but my early attempts to write CI code were plagued by the error message "Call to
+a member function on a non-object". I saw it so often that I was thinking of having it
+printed on a t-shirt: it has a mysteriously libertarian, anarchist tone, and I could see
+myself wearing it at a modern art exhibition.
+
+To save the world from a lot of boring t-shirts, this chapter covers the way in
+which CI uses objects, and the different ways you can write and use your own
+objects. Incidentally, I've used 'variables锟絧roperties', and 'methods锟絝unctions'
+interchangeably, as CI and PHP often do. You write 'functions' in your controllers for
+instance, when the OO purist would call them 'methods'. You define class 'variables'
+when the purist would call them 'properties'.
+
+
+Object-Oriented Programming
+I'm assuming you鈥攍ike me鈥攈ave a basic knowledge of OOP, but may have learned
+it as an afterthought to 'normal' PHP 4. PHP 4 is not an OO language, though some
+OO functionality has been tacked on to it. PHP 5 is much better, with an underlying
+engine that was written from the ground up with OO in mind.
+
+But you can do most of the basics in PHP 4, and CI manages to do everything it
+needs internally, in either language.
+
+
+CodeIgniter and Objects
+
+The key thing to remember is that, when an OO program is running, there is always
+one current object (but only one). Objects may call each other and hand over control
+to each other, in which case the current object changes; but only one of them can be
+current at any one time. The current object defines the 'scope'鈥攊n other words, which
+variables (properties) and methods (functions) are available to the program at that
+moment. So it's important to know, and control, which object is current. Like police
+officers and London buses, variables and methods belonging to objects that aren't
+current just aren't there for you when you most need them.
+
+PHP, being a mixture of functional and OO programming, also offers you the
+possibility that no object is current! You can start off as a functional program, call
+an object, let it take charge for a while, and then let it return control to the program.
+Luckily, CI takes care of this for you.
+
+
+Working of the CI 'Super-Object'
+CI works by building one 'super-object': it runs your whole program as one big
+object, in order to eliminate scoping issues.
+
+When you start CI, a complex chain of events occurs. If you set your CI installation to
+create a log, you'll see something like this:
+ 1 DEBUG - 2006-10-03 08:56:39 --> Config Class Initialized
+ 2 DEBUG - 2006-10-03 08:56:39 --> No URI present. Default controller
+ set.
+ 3 DEBUG - 2006-10-03 08:56:39 --> Router Class Initialized
+ 4 DEBUG - 2006-10-03 08:56:39 --> Output Class Initialized
+ 5 DEBUG - 2006-10-03 08:56:39 --> Input Class Initialized
+ 6 DEBUG - 2006-10-03 08:56:39 --> Global POST and COOKIE data
+ sanitized
+ 7 DEBUG - 2006-10-03 08:56:39 --> URI Class Initialized
+ 8 DEBUG - 2006-10-03 08:56:39 --> Language Class Initialized
+ 9 DEBUG - 2006-10-03 08:56:39 --> Loader Class Initialized
+ 10 DEBUG - 2006-10-03 08:56:39 --> Controller Class Initialized
+ 11 DEBUG - 2006-10-03 08:56:39 --> Helpers loaded: security
+ 12 DEBUG - 2006-10-03 08:56:40 --> Scripts loaded: errors
+ 13 DEBUG - 2006-10-03 08:56:40 --> Scripts loaded: boilerplate
+ 14 DEBUG - 2006-10-03 08:56:40 --> Helpers loaded: url
+ 15 DEBUG - 2006-10-03 08:56:40 --> Database Driver Class Initialized
+ 16 DEBUG - 2006-10-03 08:56:40 --> Model Class Initialized
+
+
+
+
+ [ 100 ]
+
+
+ Chapter 7
+
+On startup鈥攖hat is, each time a page request is received over the Internet鈥擟I goes
+through the same procedure. You can trace the log through the CI files:
+
+ 1. The index.php file receives a page request. The URL may indicate
+ which controller is required, if not, CI has a default controller (line 2).
+ Index.php makes some basic checks and calls the codeigniter.php file
+ (\codeigniter\codeigniter.php).
+ 2. The codeigniter.php file instantiates the Config, Router, Input, URL, (etc.)
+ classes (lines 1, and 3 to 9). These are called the 'base' classes: you rarely
+ interact directly with them, but they underlie almost everything CI does.
+ 3. codeigniter.php tests to see which version of PHP it is running on, and
+ calls Base4 or Base5 (/codeigniter/Base4(or 5).php). These create a
+ 'singleton' object: one which ensures that a class has only one instance. Each
+ has a public &get_instance() function. Note the &:, this is assignment by
+ reference. So if you assign to the &get_instance() method, it assigns to the
+ single running instance of the class. In other words, it points you to the same
+ pigeonhole. So, instead of setting up lots of new objects, you are starting
+ to build up one 'super-object', which contains everything related to
+ the framework.
+ 4. After a security check, codeigniter.php instantiates the controller that
+ was requested, or a default controller (line 10). The new class is called $CI.
+ The function specified in the URL (or a default) is then called, and life as
+ we know it starts to wake up and happen. Depending on what you wrote
+ in your controller, CI will then initialize any other classes you need, and
+ 'include' functional scripts you asked for. So in the log above, the model class
+ is initialized. (line 16) The 'boilerplate' script, on the other hand, which is also
+ shown in the log (line 13), is one I wrote to contain standard chunks of text.
+ It's a .php file, saved in the scripts folder, but it's not a class: just a set of
+ functions. If you were writing 'pure' PHP you might use 'include' or 'require'
+ to bring it into the namespace: CI needs to use its own 'load' function to bring
+ it into the super-object.
+
+The concept of 'namespace' or scope is crucial here. When you declare a variable,
+array, object, etc., PHP holds the variable name in its memory and assigns a further
+block of memory to hold its contents. However, problems might arise if you define
+two variables with the same name. (In a complex site, this is easily done.) For this
+reason, PHP has several sets of rules. For example:
+
+ Each function has its own namespace or scope, and variables defined
+ 鈥
+ within a function are usually 'local' to it. Outside the function, these
+ are meaningless.
+
+
+
+ [ 101 ]
+
+
+CodeIgniter and Objects
+
+ You can declare 'global' variables, which are held in a special global
+ 鈥
+ namespace and are available throughout the program.
+ Objects have their own namespaces: variables exist inside the object for as
+ 鈥
+ long as the object exists, but can only be referenced through the object.
+
+So $variable, global $variable, and $this->variable are three different things.
+
+Particularly, before OO, this could lead to all sorts of confusion: you may have too
+many variables in your namespace (so that conflicting names overwrite each other),
+or you may find that some variables are just not accessible from whatever scope you
+happen to be in. CI offers a clever way of sorting this out for you.
+
+So, now you've started CI, using the URL www.mysite.com/index.php/welcome/
+index, which specifies that you want the index function of the welcome controller.
+
+If you want to see what classes and methods are now in the current namespace and
+available to you, try inserting this 'inspection' code in the welcome controller:
+ $fred = get_declared_classes();
+ foreach($fred as $value)
+ {$extensions = get_class_methods($value);
+ print "class is $value, methods are: ";
+ print_r($extensions);}
+
+When I ran this just now, it listed 270 declared classes. Most are other libraries
+declared in my installation of PHP. The last 11 came from CI: ten were the CI base
+classes (config, router, etc.) and last of all came the controller class I had called.
+Here's the last 11, with the methods omitted from all but the last two:
+ 258: class is CI_Benchmark
+ 259: class is CI_Hooks,
+ 260: class is CI_Config,
+ 261: class is CI_Router,
+ 262: class is CI_Output,
+ 263: class is CI_Input,
+ 264: class is CI_URI,
+ 265: class is CI_Language,
+ 266: class is CI_Loader,
+ 267: class is CI_Base,
+ 268: class is Instance,
+ 269: class is Controller, methods are: Array ( [0] => Controller [1]
+ => _ci_initialize [2] => _ci_load_model [3] => _ci_assign_to_models
+ [4] => _ci_autoload [5] => _ci_assign_core [6] => _ci_init_scaffolding
+ [7] => _ci_init_database [8] => _ci_is_loaded [9] => _ci_scaffolding
+ [10] => CI_Base )
+ 270: class is Welcome, methods are: Array ( [0] => Welcome [1] =>
+ index [2] => Controller [3] => _ci_initialize [4] => _ci_load_model
+ [5] => _ci_assign_to_models [6] => _ci_autoload [7] => _ci_assign_core
+ [8] => _ci_init_scaffolding [9] => _ci_init_database [10] => _ci_is_
+ loaded [11] => _ci_scaffolding [12] => CI_Base )
+
+Notice鈥攊n parentheses as it were鈥攖hat the Welcome class (number 270: the
+controller I'm using) has all the methods of the Controller class (number 269). This
+is why you always start off a controller class definition by extending the controller
+class鈥攜ou need your controller to inherit these functions. (And similarly, models
+should always extend the model class.) Welcome has two extra methods: Welcome
+and index. So far, out of 270 classes, these are the only two functions I wrote!
+
+Notice also that there's an Instance class. If you inspect the class variables of the
+'Instance' class, you will find there are a lot of them! Just one class variable of the
+Instance class, taken almost at random, is the array input:
+ ["input"]=> &object(CI_Input)#6 (4) { ["use_xss_clean"]=> bool(false)
+ ["ip_address"]=> bool(false) ["user_agent"]=> bool(false) ["allow_get_
+ array"]=> bool(false) }
+
+Remember when we loaded the input file and created the original input class? Its
+class variables were:
+ use_xss_clean is bool(false)
+ ip_address is bool(false)
+ user_agent is bool(false)
+ allow_get_array is bool(false)
+
+As you see, they have now all been included within the 'instance' class.
+
+All the other CI 'base' classes (router, output, etc.) are included in the same way. You
+are unlikely to need to write code referencing these base classes directly, but CI itself
+needs them to make your code work.
+
+
+Copying by Reference
+You may have noticed that the CI_Input class is assigned by reference (["input"]=>
+&object(CI_Input)). This is to ensure that as its variables change, so will the
+variables of the original class. As assignment by reference can be confusing, here's a
+short explanation. We're all familiar with simple copying in PHP:
+
+ $one = 1;
+ $two = $one;
+ echo $two;
+
+produces 1, because $two is a copy of $one. However, if you re-assign $one:
+
+ $one = 1;
+ $two = $one;
+ $one = 5;
+ echo $two;
+
+This code still produces 1, because changes to $one after $two has been assigned
+aren't reflected in $two. This was a one-off assignment of the value that happened
+to be in variable $one at the time, to a new variable $two, but once it was done, the
+two variables led separate lives. (In just the same way, if I alter $two, $one
+doesn't change.)
+
+In effect, PHP creates two pigeonholes: one called $one, one called $two. A separate
+value lives in each. You may, on any one occasion, make the values equal, but after
+that they each do their own thing.
+
+PHP also allows copying 'by reference'. If you add just a simple & to line 2 of
+the code:
+ $one = 1;
+ $two =& $one;
+ $one = 5;
+ echo $two;
+
+Then the code now echoes 5: the change we made to $one has also happened to $two.
+
+Changing the = to =& in the second line means that the assignment is 'by reference'.
+Now, it's as if there was only one pigeonhole, which has two names ($one and $two).
+Whatever happens to the contents of the pigeonhole happens both to $one and to
+$two, as if they were just different names for the same thing.
+
+The principle works for objects as well as simple string variables. You can copy or
+clone an object using the = operator, in which case you make a simple one-off new
+copy, which then leads an independent life. Or, you can assign one to the other by
+reference: now the two objects point to each other, so any changes made to the one
+will also happen to the other. Again, think of them as two different names for the
+same thing.
+
+
+Adding Your own Code to the CI 'Super-Object'
+
+You contribute to the process of building the 'super-object' as you write your own
+code. Suppose you have written a model called 'status', which contains two class
+variables of its own, $one and $two, and a constructor that assigns them values of 1
+and 2 respectively. Let's examine what happens when you load this model.
+
+The 'instance' class includes a variable 'load', which is a copy (by reference) of the
+object CI_Loader. So the code you write in your controller is:
+
+$this->load->model($status)
+
+In other words, take the class variable 'load' of the current CI super-class ('this') and
+use its method 'model'. This actually references the 'model' function in the 'loader'
+class (/system/libraries/loader.php) and that says:
+
+function model($model, $name = '')
+{
+ if ($model == '')
+ return;
+
+ $obj =& get_instance();
+ $obj->_ci_load_model($model, $name);
+}
+
+(The $name variable in this code is there in case you want to load your model under
+an alias. I don't know why you should want to do this; perhaps it's wanted by the
+police in several other namespaces.)
+
+As you can see, the model is loaded by reference into the Instance class. Because
+get_instance() is a singleton method, you're always referencing the same instance
+of the Instance class.
+
+If you run the controller again, using our 'inspect' code modified to show class
+variables, you'll now see that the instance class contains a new class variable:
+ ["status"]=> object(Status)#12 (14) { ["one"]=> int(1) ["two"]=>
+ int(2) ... (etc)
+
+In other words, the CI 'super-object' now includes an object called $status that
+includes the class variables you defined in your original status model, assigned to
+the values we set.
+
+
+
+
+ [ 105 ]
+
+
+CodeIgniter and Objects
+
+So we are gradually building up the one big CI 'super-object', which allows you to
+use any of its methods and variables without worrying too much about where they
+came from and what namespace they might be in.
+
+This is the reason for the CI arrow syntax. To use the methods of (say) a model, you
+must first load the model in your controller:
+ $this->load->model('Model_name');
+
+This makes the model into a class variable of $this->, the current (controller) class.
+You then call a function of that class variable from the controller, like this:
+ $this->Model_name->function();
+
+and off you go.
+
+
+Problems with the CI 'Super-Object'
+There was one big problem for Rick Ellis when he wrote the original code. PHP 4
+handles objects less elegantly than PHP 5, so he had to introduce a 'really ugly hack'
+(his words) into the Base4 file. Ugly or not, the hack works, and so we don't need to
+worry about it. It just means that CI works as well on PHP 4 systems as it does
+on PHP 5.
+
+There are two other issues worth mentioning here:
+
+ You can find yourself trying to work with an object that isn't available.
+ 鈥
+ You have to structure your site carefully, because you can't call methods of
+ 鈥
+ one controller from inside another.
+
+Let's look at these two problems in turn. You remember the t-shirt I mentioned
+above: "Call to a member function on a non-object"? This annoying error message
+ Call
+often means that you tried to use a function from a class (say a model class that you
+wrote) but forgot to load the class. In other words, you wrote:
+ $this->Model_name->function();
+
+but forgot to precede it by:
+ $this->load->model('Model_name');
+
+Or some variation of this: for instance, you loaded the model inside one function of a
+class, which loads the model, but only inside that function, and then you tried to use
+its methods from inside another function, albeit in the same class. It's usually best to
+load models, etc., from the class constructor function: then they are available to all
+the other functions in the class.
+
+ [ 106 ]
+
+
+ Chapter 7
+
+The problem can also be more subtle. If you write your own classes, for instance, you
+may wish to use them to access the database, or to look up something in your config
+files鈥攊n other words, to give them access to something that is part of the CI 'super-
+object'. (There's a fuller discussion of how to add your own classes or libraries in
+Chapter 13.) To summarize, unless your new class is a controller, a model, or a view,
+it doesn't get built in to the CI super-object. So you can't write things inside your new
+class like this:
+ $this->config->item('base_url);
+
+This just won't work, because to your new class, $this-> means itself, not the CI
+super-object. Instead, you have to build your new class into the super-class by calling
+the Instance class (sound familiar?) using another variable name (usually $obj)
+ $obj =& get_instance();
+
+Now you can write that call to the CI superclass as:
+ $obj->config->item('base_url);
+
+and this time it works.
+
+However, as you write your new class, remember that it still has its own identity.
+Let's use a short outline example to make this clearer.
+
+You want to write a library class that prepares a URL based on the location of the
+server that requests the page. So you write some code to look up the geographic
+location of the IP address that is calling your page (using a library like the netGeo
+class available from http://www.phpclasses.org/browse/package/514.html).
+Then, using a switch function, you select one of several alternative functions, and
+you serve up an English page to US or British requests, a German page to German or
+Austrian requests, and so on. Now, the full URL to your country-specific page will
+be made up of two parts: the base URL of your site (www.mysite.com/index.php/),
+plus the URL of the individual page (mypage/germanversion).
+
+You need to get the base URL of the site from CI's config file. The second half of
+the URL is being generated by a switch statement in the constructor of your new
+class鈥攊f this client is in Germany, serve up the German page function, etc. As this is
+being done in the constructor calls, you need to put the result into a class variable, so
+it can be used in other functions within the same class. This means that:
+
+ The first half of your URL comes from the CI config file, which can
+ 鈥
+ only be referenced through the superobject, to which you have linked
+ using $obj =& get_instance(). In other words, you call it using
+ $obj->config->item('base_url);
+
+
+
+ [ 107 ]
+
+
+CodeIgniter and Objects
+
+ But the second half of your URL is generated inside the constructor of your
+ 鈥
+ new class and assigned to a class variable, $base. It has nothing to do with
+ the CI super-object; it belongs to your new class, and is referenced as
+ $this->base
+
+This can lead to using both $this-> and $obj-> references in the same line鈥攅.g.:
+ class my_new_class{
+ var $base;
+ My_new_class()
+ {
+ $obj =& get_instance();
+ // geolocation code here, returning a value through a switch statement
+ //this value is assigned to $local_url
+ $this->base = $obj->config->item('base_url);
+ $this->base .= $local_url;
+ }
+
+Getting these confused is another fruitful source of, "Call to a member function on
+a non-object". In our example, you'd get that error message if you tried to call either
+$obj->base, or $this->config->item().
+
+Turning to the remaining problem, you can't call methods of one controller
+from inside another. Why would you want to do this? Well, it depends. In one
+application, I wrote a series of self-test functions inside each controller. If I called
+$this->selftest() inside the controller, it did various useful tests. But it seemed
+against the principle programming virtue of laziness to have to repeatedly call the
+self-test method in each controller separately. I tried to write one function, in one
+controller, that would go through all the controllers, call the self-test method in each,
+amalgamate all the results while I stared out of the window, and then give me a
+comprehensive report in exchange for only one mouse click. Alas, no. Can't be done.
+
+As a general rule, if you have code that may be needed by more than one controller,
+put it in a model or a separate script of some sort. Then they can both use it. (Of
+course, this doesn't help with my self-test problem, because the code to test the
+controllers has to be in the controllers!)
+
+But these are minor problems. As Rick Ellis put it to me:
+
+"I wanted to arrive at something more simple so I decided to make one big controller
+ I
+object containing lots of other object instances:鈥hen a user creates their own
+ 鈥hen
+ when
+controllers they can easily access anything that has been instantiated just by calling
+it, without worrying about scope".
+
+
+
+
+ [ 108 ]
+
+
+ Chapter 7
+
+That's pretty well how it works, most of the time, efficiently, and completely in the
+background. So I never did get that t-shirt printed.
+
+
+
+Summary
+We've looked at the way CI builds up one 'super-object' to make sure that all the
+methods and variables you need are automatically available to you without you
+having to manage them and worry about their scope.
+
+CI makes extensive use of assignment by reference, instantiating one class after
+another and linking them all together so that you can access them through the
+'super-class'. Most of the time, you don't need to know what the 'super-class' is
+doing, provided that you use CI's 'arrow' notation correctly.
+
+We've also looked at how you can write your own classes and still have access to the
+CI framework.
+
+Lastly, we looked at a few problems that can arise, particularly if you're not used to
+OO programs, and suggested a few solutions.
+
+
+
+
+ [ 109 ]
+
+
+
+
+ Using CI to Test Code
+This chapter looks at how CI can help you to test your code. Testing is the heart of
+our application. We've built it to test other remote applications; we also want to test
+it itself, as we develop it. CI makes this a lot easier.
+
+However, 'testing' can mean a lot of things, so we start off by looking at the
+difference between the two main types, and at some other reasons for which you
+might want to run tests.
+
+Then we look at CI classes to help with testing:
+
+ Unit tests
+ 鈥
+ Benchmarking
+ 鈥
+ The 'profiler'
+ 鈥
+ Ways in which CI helps you to involve your database in tests without
+ 鈥
+ scrambling live data
+
+
+
+Why Test, and What For?
+A lot has been written about testing. It has become an industry. Complex programs
+employ an army of testers or test software. And the concept of 'test-driven
+development' is that you design your tests first, before even a single line of code:
+then write your code to pass them.
+
+At the other extreme, many programmers don't do any systematic testing, because it
+seems too difficult, boring, or time-consuming. Maybe we try the program out a few
+times, and then hope for the best.
+
+CI offers several ways to make testing easier. Even鈥攈onestly!鈥攎ore fun.
+
+
+Using CI to Test Code
+
+There are two main types of tests:
+
+ Unit tests: These take a 'bottom-up' approach. They look at one chunk of your
+ 鈥
+ code, say a single function, throw in some variables, and see if it gives back
+ the right answer.
+ End-to-end tests: These are 'top-down'. They focus on something the site is
+ 鈥
+ supposed to do, and see if it does it: for instance, they try to log in to your
+ site (using a valid username and password) and see if it allows them. (And
+ then they try to log in using an invalid password鈥)
+
+As you can see, it's a different philosophy. One tests chunks of code, and doesn't
+know or care what the end result is; while the other tests the end result, and doesn't
+know or care which chunks of code got you there.
+
+The important thing is to think through why you are testing. What worries you
+most? What is most likely to go wrong and embarrass you? What sort of information
+do you need back from your tests鈥攋ust a simple OK/ not OK, or something more
+complex? For each application, how much time can you afford to put in to writing
+and maintaining tests?
+
+While we're developing our test site, we need to test our code while we write it. Of
+course, we try to anticipate everything the user might do, and every situation that
+might arise. This is one big area where unit tests are useful: just the fact of designing
+tests helps you to improve the design of the code.
+
+Once our code is up on a production server, its day-to-day integrity is largely beyond
+our control. At its worst, this leads to clients finding error messages or blank screens,
+and expecting you to do something about it, often at times when you'd rather be
+doing something else. That's why we're building this site, to test other remote sites.
+
+CI can help us to check developing sites to see:
+
+ Firstly, that we've anticipated a range of things that might go wrong. For
+ 鈥
+ instance, I might do a database query to delete a record with a given ID
+ number in a specific table. Yes, it works: I've tested it by doing it. But what
+ happens if鈥攕omehow鈥攖he code calls a table that doesn't exist? Or gives an
+ invalid ID number? Or doesn't give one at all? This is where unit tests
+ are helpful.
+ Secondly, when I write more code somewhere else, does my first block of
+ 鈥
+ code still work the way I want, or have I inadvertently altered something the
+ first block depends on? Again, a job for unit tests. They can also help us to
+ check production sites regularly to see that the site is there (and all the parts
+ of it鈥攅.g., if the database is on a separate server, an ordinary 'ping' test
+ won't do!)
+
+ [ 112 ]
+
+
+ Chapter 8
+
+CI gives you a lot of help, whichever position you take. It doesn't have a class to run
+end-to-end tests, but you can do this using other PHP code that is outside the scope
+of this book. But let's look first of all at how CI displays errors to you as you
+develop code.
+
+
+
+CI's Error Handling Class
+CI has a system of its own for detecting and reporting errors. In one way, these are
+the simplest and most common tests of all: they are those helpful (or infuriating)
+messages you see when you are developing your own code and it doesn't work.
+
+By default, CI displays all errors on the screen. The alternative is to fail silently;
+giving you no idea of what went wrong, so this is essential for development. Overall
+behavior is controlled from the main index.php file, which begins:
+ /*
+ |---------------------------------------------------------------
+ | PHP ERROR REPORTING LE锟紼L
+ |---------------------------------------------------------------
+ |
+ | By default CI runs with error reporting set to ALL. For security
+ | reasons you are encouraged to change this when your site goes live.
+ | For more info visit: http://www.php.net/error_reporting
+ |
+ */
+ error_reporting(E_ALL);
+
+This is a PHP command. To turn off error reporting, replace the last line with:
+ error_reporting(0);
+
+This would be appropriate for a production site, where you don't want the details of
+errors displayed to users.
+
+CI has three functions, show_error(), show_404(), and log_message(), which
+control how errors are displayed on your system. (Unusually, these functions are
+globally available: you don't have to load anything before you can use them, just
+go right ahead and type them in!). In fact, show_error() and show_404() usually
+happen by default; the first displays your errors in a neat little HTML-formatted box
+at the top of the screen; and the second shows a '404' page' if you try to access a page
+that isn't there.
+
+
+
+
+ [ 113 ]
+
+
+Using CI to Test Code
+
+The third function, log_message(), is more interesting. You may want to develop
+your own error log, maybe because you can't access the log on your ISP's Apache
+server. First, you need to set permissions to make sure that your /system/logs
+folder is writable. Then you set the level of logging in the config file:
+ /*
+ |--------------------------------------------------------------------
+ ------
+ | Error Logging Threshold
+ |--------------------------------------------------------------------
+ ----
+ |
+ | If you have enabled error logging, you can set an error threshold to
+ | determine what gets logged. Threshold options are:
+ |
+ | 0 = Disables logging
+ | 0 = Error logging TURNED OFF
+ | 1 = Error Messages (including PHP errors)
+ | 2 = Debug Messages
+ | 3 = Informational Messages
+ | 4 = All Messages
+ |
+ | For a live site you'll usually only enable Errors (1) to be logged
+ | otherwise your log files will fill up very fast.
+ |
+ */
+ $config['log_threshold'] = 4;
+
+ /*
+
+This starts logging, automatically.
+
+If you alter index.php to turn off the screen display of messages, this doesn't stop
+logging. So you can see what your system is doing, and your users can't.
+
+
+
+
+ [ 114 ]
+
+
+ Chapter 8
+
+CI generates a new log file each day, and writes to it as you instruct it. But beware,
+these log files can rapidly become very large. Here's a sample of one:
+
+
+
+
+鈥攁nd so on for another 3000 lines, on a day when the program had only
+limited usage.
+
+In practice, you may want to develop your own error handling procedures to display
+a default message to users when something goes wrong.
+
+
+
+CI's Unit Test Class
+Now let's get on to proper testing: proactively looking at bits of your code to make
+sure they work under different circumstances.
+
+CI makes unit testing simple with its own class. You load it with this:
+ $this->load->library('unit_test');
+
+and then, for each test, you decide three variables:
+ $test鈥攖he actual test, as a PHP expression
+ 鈥
+ $expected_result鈥攖he result you expect
+ 鈥
+ $test_name鈥攖he test name as you want it displayed
+ 鈥
+
+ [ 115 ]
+
+
+Using CI to Test Code
+
+Here are two tests of the PHP function floor() (which rounds down a 'float'
+number to the nearest integer below it). Notice that the first expected result is correct;
+the second is wrong. (A deliberate mistake, honestly.)
+ $test = floor(1.56);
+ $expected_result = 1;
+ $test_name = 'tests php floor function';
+ $this->unit->run($test, $expected_result, $test_name);
+ $test = floor(2.56);
+ $expected_result = 1;
+ $test_name = 'tests php floor function';
+ $this->unit->run($test, $expected_result, $test_name);
+
+adding:
+ echo $this->unit->report();
+
+displays the result as formatted HTML, like this:
+
+
+
+
+If you want your system to analyze or store it, using:
+ echo $this->unit->result();
+
+returns the information as a two-dimensional array, which you can use:
+ Array (
+ [0] => Array
+ ( [Test Name] => tests php floor function
+ [Test Datatype ] => Float
+
+ [ 116 ]
+
+
+ Chapter 8
+
+ [Expected Datatype] => Integer
+ [Result] => Passed
+ [File Name] => E:\myfile.php [Line Number] => 69 )
+ [1] => Array
+ ( [Test Name] => tests php floor function
+ [Test Datatype ] => Float
+ [Expected Datatype] => Integer
+ [Result] => Failed
+ [File Name] => E:\myfile.php
+ [Line Number] => 73 )
+ )
+
+So now we have an easy way of getting the results.
+
+Along with simply comparing values (does floor(1.56) equal 1?) the class can also
+test for data types (is_string, is_bool, is_true, etc.鈥攁 full list is in the online User
+Guide.) You replace your:
+ $expected_result = 1;
+
+line with something like:
+ $expected_result = 'is_float';
+
+and the test proceeds as before.
+
+If you've scattered these things throughout your code, it may run slowly, and will
+display all sorts of diagnostics on your screen. But you can stop this. Simply add the
+following line in your constructor:
+ $this->unit->active(FALSE);
+
+And (surprise, surprise) if you change FALSE to TRUE, back it comes again. You can
+even do this dynamically.
+
+
+When to Use Unit Tests
+There is little point in testing whether a standard PHP function works. True. But
+there is value in testing your own functions to see if they consistently return the
+expected result. The main worry is always that:
+
+ They will behave perfectly when you test them
+ 鈥
+ But a user will immediately think of a combination of circumstances you had
+ 鈥
+ never imagined, which will cause the function to fail
+ Or you will write some more code, or alter existing code, and your own
+ 鈥
+ functions will no longer work properly
+
+ [ 117 ]
+
+
+Using CI to Test Code
+
+Sometimes, the failure is due to a programming issue, which can be caught by a unit
+test. You can have fun thinking up different parameters to test for.
+
+Let's go back to our example of a function that performs a database query to delete a
+record with a given ID from a given table. What does it do if:
+
+ The ID is NULL, or '', or not set? (Particularly important this one, as you
+ 鈥
+ might accidentally delete every entry in the table.)
+ The ID is not an integer? ("x", for example?)
+ 鈥
+ The ID is an integer, but is out of range (you have 1000 entries in your table,
+ 鈥
+ but this ID is 1001?)
+ The ID is a negative number?
+ 鈥
+
+and so on. It's quite amusing thinking of different conditions to test for.
+
+Throw them all at the function with a unit test, and see. However, think carefully
+about the results you expect. The first case and second case are clearly programming
+errors. You should rewrite your program to prevent this happening. So if it does, you
+want the test to return failure.
+
+We define the result we want from each test, so that we test if the program acted
+correctly, not if the parameter is correct. If we submit an 'x', that's an incorrect
+parameter; but if the program throws an exception, that is correct behavior. It helps
+to write all the tests to show 'passed' if the program is doing what we want, so we
+only have to take notice of exceptions.
+
+The third case above, where the ID is an integer but out of range, is not necessarily
+a programming error. The database should be able to sort this out safely. However,
+what you need to do depends on your program and its objectives. Maybe, before you
+submit the integer value, you need to check that it is within range? Or maybe you are
+happy to let it run, in which case, because the program may return a database error
+message to your screen, you need to intercept this and replace it with a bland 'sorry,
+unable to do this at this time' error message? Or maybe test the delete operation for
+success and branch accordingly?
+
+
+Example of a Unit Test
+Let's build some code to test the 'delete' function. I've set up a 'delete' function
+(which is in a model) so that it detects that I am testing it, and on failure returns a
+value ($dbvalue).
+ if($test == 'yes')
+ {
+ $place = __FILE__.__LINE__;
+
+ [ 118 ]
+
+
+ Chapter 8
+
+ $dbvalue = "exception at $place: sent state value $state to
+ trydelete function ";
+ return $dbvalue;
+ }
+
+If the test is successful, a similar loop returns a $dbvalue of 'OK'. The test code is
+simple. First, we build an array of ID values and the results we expect from each. In
+other words, if we try to delete using an ID of '' or 'abc' the system should throw an
+exception, prefixing the diagnostic line it returns with the word 'exception'. If it is 1,
+or 9999, the system should accept it as a valid ID, and prefix the line it returns
+with 'OK'.
+
+So the array key is the condition you are testing for and the array value is the result
+you expect the function to return.
+ $numbers = array(
+ '' => 'exception',
+ 'NULL' => 'exception',
+ 'x' => 'exception',
+ '9999' => 'OK',
+ '-1' => 'exception',
+ '1' => 'OK'
+ );
+
+Now use the following code to loop through the $numbers array and use the CI unit
+test to do each test.
+
+The test is to run the $this->delete() function, specifying the table you are
+deleting from ('fred') and the ID value ($testkey).
+ foreach($numbers AS $testkey => $testvalue)
+ {$dbvalue = $this->delete('fred', $testkey);
+ $result .= $this->unit->run(preg_match("/$testvalue/",
+ $dbvalue), 1, $dbvalue);
+ }
+
+Remember, the CI unit test allows you to provide three parameters:
+
+ $test: for each array key, we try the database by calling the delete function
+ 鈥
+ with $testkey, the array key鈥攚hich is the ID (or lack of an ID) we are
+ submitting to the function. The function returns a value (here called
+ $dbvalue). Our $test is to compare that value, using a regex, to what we
+ expect it to be鈥攚hich is $testvalue, the value of the array. (Does it include
+ 'OK' or 'exception'?)
+ $expectedresult is '1', because if our code is correct we expect the regex to
+ 鈥
+ find a match. We expect 'NULL' to throw an exception and 1 to be OK.
+
+ [ 119 ]
+
+
+Using CI to Test Code
+
+ $testname: this parameter is optional: it is the string returned by the
+ 鈥
+ test, which goes on to explain what value we tried to use and what we
+ were testing.
+
+Here's the result, as shown in CI's inbuilt HTML format: All return the Result
+'passed', so we can have confidence in our code. (The Test Datatype and Expected
+Datatype are always shown as integers, even though our inputs may not be integers,
+because the test is actually a regex comparison, which returns a 1 or a 0).
+
+
+
+
+The fun, and actually quite useful, thing is thinking of new tests to include in
+the array!
+
+For instance, what if the 'id' is a number, but not an integer? To try this with the
+above code, you'd add
+ '3.5' => 'exception',
+
+to the array of test values.
+
+ [ 120 ]
+
+
+ Chapter 8
+
+However, you'd be surprised (as I was) to find that this test fails鈥攊n other words, it
+suggests that your function would accept 3.5 as an integer. The reason is that PHP
+does a 'loose' equality test; it finds a number and takes this for an integer. What you
+need in this case is a 'strict' mode of test that compares the datatype as well as the
+values. To set this, use:
+ $this->unit->use_strict(TRUE);
+
+
+
+CI's Benchmarking Class
+This class allows you to measure the time it takes for your program to go from one
+point to another. You insert one line of code to define the first point:
+ $this->benchmark->mark('here');
+
+and another line of code to define the second:
+ $this->benchmark->mark('there');
+
+then you insert a third line to tell you the elapsed time:
+ $fred = $this->benchmark->elapsed_time('here', 'there');
+
+You can then print the result, $fred, or do whatever else you want to do with it.
+
+Benchmarks can have any name you like, as long as they are different, and you can
+have as many pairs as you want. You can use these tests to see if a particular part of
+your code is taking a suspiciously long time. If one of your pages takes too long to
+load, you can insert several benchmarks to identify the actual piece of code that is
+causing the delay.
+
+For the tests in our website monitoring application, however, we aren't so much
+interested in one-off times. By the time we get our applications up on the Internet,
+we hope that their speed is acceptable. Absolute differences between times are likely
+to be small and largely meaningless. However, if we track a benchmark on several
+successive tests, we may notice that it is changing: and this may give us a clue to
+some underlying problem. A database query may be taking longer; or our hosting
+may be less effective. So for our purposes, we'll take the contents of $fred, and store
+it in our database.
+
+
+
+
+ [ 121 ]
+
+
+Using CI to Test Code
+
+
+CI's Profiler Class
+The profiler class is simply brilliant. You put one line of code somewhere inside a
+function in your class. (It works from inside the constructor, so it makes sense to put
+it there.) That line is:
+ $this->output->enable_profiler(TRUE);
+
+If you change your mind, either delete it or change it to:
+ $this->output->enable_profiler(FALSE);
+
+In exchange for this one line of code, you get a full report on your screen, giving you
+details of the time CI took to load itself and your controller, of what was in the POST
+array, and of any database queries you have run. At the code development stage, this
+is a real help.
+
+If you add in your own benchmarks, it will display those as well. You have to
+use special names for your benchmarks鈥攖hey must end in _start and _end,
+respectively, and each pair must otherwise have the same name:
+ $this->benchmark->mark('fred_start');
+
+and, somewhere else,
+ $this->benchmark->mark('fred_end');
+
+
+
+
+ [ 122 ]
+
+
+ Chapter 8
+
+As you can see, the time elapsed between these two benchmarks is then displayed
+as 'fred'.
+
+
+
+Testing with Mock Databases
+Dynamic sites are all about databases. If you are testing them properly, you ought to
+test whether your code can actually modify a database. End-to-end tests do this: for
+instance, if your test is whether you can log in with the correct username锟絧assword
+combination, you are probably reading a database to do so.
+
+But testing whether you can update, create, and delete entries on a production
+database is a dangerous pastime, because it corrupts your real data!
+
+Remember that CI allows you to declare more than one database, and to swap
+between them easily?鈥攕ee Chapter 4. Using this, it's easy to set up a mock database,
+and then add, change, and delete data in it.
+
+You can also use CI to set up and knock down tables, or possibly even whole
+databases depending on your host and your permission level. CI's:
+ $this->db->query('YOUR QUERY HERE');
+
+function allows you to run any SQL query, including something like this:
+ $this->db->query('CREATE TABLE fred(id INT, name 锟紸RCHAR(12),
+ INDEX(id))');
+
+which will create a new table, or something like this:
+ $this->db->query("INSERT INTO fred 锟紸LUES (1, 'smith')");
+
+which will populate that table with one row of data.
+
+So, with a few lines of code, CI lets you set up a whole new set of mock data to test,
+play around with it, test the results, and then delete it ready for next time. You might
+then run a set of unit tests on the delete() function to see if the function does what
+we expect when it has different 'id' parameters, as described earlier in this chapter.
+
+Now you're going beyond the simple unit test. If we run a test that should have
+deleted the value in our table, then we need to check whether it actually has been
+deleted. This is easily done with the following code, again using CI's unit testing
+class, and its active record class:
+ $test = $this->db->count_all('fred');
+ $expected_result = 0;
+ $test_name = 'tests number of entries left in table after unique
+ entry removed';
+ $this->unit->run($test, $expected_result, $test_name);
+
+ [ 123 ]
+
+
+Using CI to Test Code
+
+$this->db->count_all counts all the results in the table, and we know that there
+should now be no results there at all. You can just as easily use this sort of code to
+check an 'insert' operation, to see if there is one more record in the table afterwards.
+
+Because this is dummy data, which we created specially for the test, we know exactly
+what to expect, and it doesn't matter what we do to it. Just remember to destroy and
+rebuild the table between tests, otherwise you may get odd results.
+
+
+
+Control and Timing
+Testing is the heart of our application, so here's just a word about how it can
+be controlled.
+
+You'll see from the database specification at the end of Chapter 4 that our application
+has a table called tests, and a table called events. Each time the site is asked to do
+a test run, it goes through the tests table, looking for two fields: frequency and
+last_done. If the frequency is, say, hourly, it checks the last_done field to see if
+the test has been done within an hour of the current time. If not, it does the test now,
+and updates its own last_done field to the current time.
+
+When the test is done, however, the program creates an entry in the events table.
+This gives the ID of the site, and various other bits of information, most notably the
+result of the test. This table then provides the statistics we can use to build reports for
+ourselves or for clients, sorting out individual tests, or all tests on a given site, etc.
+
+As a reminder of the benchmarking class that we discussed earlier in this chapter:
+when you run a test from a function like the one we just discussed, it's a good idea
+to put in benchmarks so you know how long the test is taking. Save the time to the
+events table. There's a field in this table for timetaken: it's a float data type because
+we are dealing in fractions of seconds. While the time taken to run any single test
+may be of little interest, changes of pattern when the test is run many times over at
+intervals may be interesting: they can show you if the test is speeding up or slowing
+down. If, for instance, it takes appreciably longer to log in to your pages, this might
+be because your ISP is overloading the server; it might be because you are putting
+too much code on your pages; or it might be that your site is becoming too popular
+and you need more bandwidth.
+
+Either way, testing regularly and presenting the aggregated results in a usable
+format can give you useful early warnings of problems.
+
+
+
+
+ [ 124 ]
+
+
+ Chapter 8
+
+
+Summary
+We've spent a lot of time on testing. It's not the most exciting of subjects, but if you're
+writing websites that matter, it's a great way of making sure you sleep peacefully
+at night.
+
+We've seen how CI normally handles errors, displaying them to you as you write
+code, but allowing you to turn them off (or divert them to a log file) when your site
+goes in to production mode.
+
+We looked at unit tests and CI's tools for handling these. We also looked at
+benchmarking, a class which makes it simple you to monitor the execution time of
+different part of your program.
+
+The profiler tool is great for showing you a lot of information about your code as you
+develop it. CI offers a good suite of tools for developing and testing your own code.
+
+We also looked at ways of testing with dummy database tables, to see whether
+database operations have actually taken place as we expected.
+
+We then integrated some external code with CI, to allow us to build web robots and
+therefore to run our own 'end-to-end' tests on a remote site.
+
+
+
+
+ [ 125 ]
+
+
+
+
+ Using CI to Communicate
+The main strength of the Internet is its ability to communicate. This chapter looks at
+three ways in which CI makes communication easier.
+
+First, we'll add to our testing toolkit by using CI's FTP class to access remote
+files directly.
+
+Then, we'll use the email class to make our site automatically email us when certain
+conditions are met.
+
+Lastly, we'll venture into Web 2.0 territory鈥攗sing XML-RPC to create a private
+'web service' that allows our remote sites to take action and return information on a
+request form our testing site.
+
+
+
+Using the FTP Class to Test Remote Files
+File Transfer Protocol (FTP) is a method of transferring files over the Internet. It's
+normally used to move files backwards and forwards to your website, using a special
+FTP program. It's something most of us only use occasionally, when we are putting
+up a new site.
+
+You can, however, automate the whole process painlessly with CI. One use is to
+test the integrity of your remote site: are the files still there? As a website owner,
+you always face the possibility that someone will tamper with the files on your site.
+It may just be your ISP or your server admin, mistakenly deleting or over-writing
+something. (I had this happen to me once, when my ISP rebuilt their server and
+forgot to reload one of my application files. The file concerned wasn't used very
+often, but mattered a lot when it was. This led to an interesting error that took some
+time to track down!)
+
+
+Using CI to Communicate
+
+As one example of the power of the FTP class, let's build a regular test program, to
+check the files on a remote site. A few lines of code are all we need:
+ function getremotefiles($hostname, $username, $password)
+ {
+ $this->load->library('ftp');
+ $config['hostname'] = $hostname;
+ $config['username'] = $username;
+ $config['password'] = $password;
+ $config['debug'] = TRUE;
+ $this->ftp->connect($config);
+ $filelist = $this->ftp->list_files('/my_directory/');
+ $this->ftp->close();
+ return $list;
+ }
+
+First, load the FTP library if you haven't already done so. Then, define the
+configuration parameters: hostname (e.g., www.mysite.com), username, and
+password for your FTP access.
+
+Once connected, CI's FTP class gives you several options. In this case, we've used
+list_files() to return a list of files in the /my_directory/ folder. The function
+returns an array, and you can easily check this against an array of the files that you
+expect to find there. As before, we're trying to list all our tests in a database. So this
+time we need to list the FTP URL (or host name), the user name and password, and
+instead of a regex, the array of files to check against. To maintain the integrity of this
+array, if you store it inside your database, you will need to serialize it before you put
+it in, and un-serialize it when you take it out again.
+
+Then it's easy to compare the $remotearray returned by the getremotefiles()
+function with the un-serialized $referencearray returned by your database:
+ function comparefiles($remotearray, $referencearray)
+ {
+ $report = " On site, not in reference array: ";
+ $report .= print_r(array_diff($remotearray, $referencearray), TRUE);
+ $report .= " In reference array, not on site: ";
+ $report .= print_r(array_diff($referencearray, $remotearray), TRUE);
+ return $report;
+ }
+
+
+
+
+ [ 128 ]
+
+
+ Chapter 9
+
+The PHP array_diff function compares the second array to the first, so it will list
+files present in the first, but not in the second. So, run the function twice, reversing
+the order of the array parameters: that way you get two lists, one of what isn't on
+your site (but should be) and one of what is on your site (but shouldn't be). The first
+should show any files that your ISP has accidentally deleted, the second any files that
+may have been added.
+
+The CI FTP class also allows you to upload, move, rename, and delete files. Suppose
+that your test above reveals that one of the files in your reference array (let's call it
+myfile.php is missing from your site. You can use the FTP class to upload it:
+
+ $this->ftp->upload('c:/myfile.php', '/public_html/myfile.php');
+
+In this example, the local path is given first, and the path on the remote server
+second. Optionally, you can specify in a third parameter how the file should be
+uploaded (as ASCII or binary.) If you don't, CI makes its own decision based on the
+file extension鈥攚hich will usually be correct. If you are running PHP5, you can add
+a fourth parameter to set the file permissions, assuming you are uploading to a
+Linux server.
+
+Be very careful about the delete option. As the user guide says, "It will recursively
+ It
+delete everything within the supplied path, including sub-folders and all files". Even
+ ".
+ .
+writing this paragraph has made me nervous.
+
+Using a combination of the FTP delete and upload functions, you could
+automatically update the files on your remote sites. List the files you need to update,
+and visit each site in turn, first deleting each old one, and then uploading each
+new one.
+
+There is also an interesting 'mirror' function, which allows you to set up a complete
+duplicate of a website on another server.
+
+If you are running PHP5, the FTP class also has a function that allows you to change
+file permissions.
+
+As you can see, there's plenty of scope for expanding your application from testing
+your remote websites to actually maintaining or updating them. You could, for
+instance, write code to distribute updates automatically.
+
+
+
+Machines Talking to Machines
+Again鈥擷ML-RPC
+The Web 2.0 revolution is largely built on machine-to-machine interfaces, which
+allow mashups and APIs and all those good things.
+
+ [ 129 ]
+
+
+Using CI to Communicate
+
+This is the basis of 'web services'. You can offer an interface to your site that allows
+other people to use it to do something for them. To give a simple example, if you set
+up a 'web service' that converts temperatures in centigrade to Fahrenheit, the client
+sends in a request with one parameter (the temperature to be converted) and the
+server returns the converted value. So, anyone can add a temperature conversion
+function that appears to be on his or her own site, but is actually calling yours.
+
+XML-RPC allows two machines to talk directly. The receiving site creates a simple
+API (application programming interface). Anyone who wants to talk to it needs to
+know that API鈥攚hat methods are available, what parameters they take, and what
+the syntax is鈥攆or addressing them. Many major sites use this system: Google, for
+instance, allows you to make direct calls to its search engine or to Google Earth via a
+published API.
+
+Setting up your own private API is relatively easy, thanks to CI. You need two
+websites to set this up and to test it, which makes it a little more complex than most
+things. One site (let's call it the 'receiving' site) is the one that offers the API, listens
+out for requests, and answers them. (In our example, this is one of the remote sites
+that we are trying to test and manage.) The other site makes the request using the
+API and gets the answer back. (In our example, this is the test site itself.)
+
+In the XML-RPC protocol, the two sites talk by means of highly structured XML.
+(Hence the name XML-RPC鈥攊t's short for XML Remote Procedure Call.) The client
+sends an XML packet to the 'receiving site' server, stating the function it wants to use
+and any arguments or parameters to be passed. The server decodes the XML and,
+if it fits the API, calls the function and returns a response, also structured as XML,
+which the client decodes and acts on.
+
+Your API consists of the functions that the receiving site offers, and instructions for
+how to use them鈥攅.g., what parameters they take, what data type these should
+be, etc.
+
+On the receiving site, we create an XML-RPC server, which makes the selected
+internal methods available to external sites. These 'internal methods' are actually
+just normal functions within one of your controllers: the server's role is to handle the
+interface between the external call and the internal function.
+
+There are two sets of problems when you set up an XML-RPC process:
+
+ Getting the two sites to talk to each other
+ 鈥
+ Making sure that the data is transmitted in a suitable format
+ 鈥
+
+Both rely heavily on multi-dimensional arrays, which machines can take in
+their stride, even if humans need to puzzle over them a bit. CI makes it a lot
+easier鈥攖hough it's still quite tricky to get right.
+
+ [ 130 ]
+
+
+ Chapter 9
+
+Getting the XML-RPC Server and Client in Touch
+with Each Other
+First, you have to set up a server on the remote site, and a client on the requesting
+site. This can be done with a few simple lines of code. Let's say we are doing the
+server in a controller called 'mycontroller' (on the receiving site) and the client in a
+controller called 'xmlrpc_client' (on the requesting site).
+
+In each case, start off by initializing the CI classes within the constructor. There are
+two; for a client you only need to load the first, for a server you need to load
+them both:
+ $this->load->library('xmlrpc');
+ $this->load->library('xmlrpcs');
+
+Now, for the server. Close your constructor function, and within the 'mycontroller'
+index() function, define the functions you are offering up to the outside world. You
+do this by building a 'functions' sub-array (within the main CI $config array) which
+maps the names of the incoming requests to the actual functions you want to use:
+ $config['functions']['call'] = array('function' => 'mycontroller.
+ myfunction');
+ $config['functions']['call2'] = array('function' => 'mycontroller.
+ myfunction2');
+
+In this case, there are two named function calls鈥'call' and 'call2'. This is what the
+request asks for. (It doesn't ask for the functions by name, but by the name of the
+call. Of course, you can use the same name if you wish.) For each call, you define
+a sub-sub-array giving the 'function' within the controller鈥攊.e. 'myfunction' and
+'myfunction2' respectively.
+
+You then finish off your server by initializing it and instantiating it:
+ $this->xmlrpcs->initialize($config);
+ $this->xmlrpcs->serve();
+
+and it is now ready to listen for requests.
+
+Now you need to go to the other website鈥攖he client鈥攁nd set up an XML-RPC client
+to make the requests. This should be a separate controller on your client site. It's
+quite short:
+ $server_url = 'http://www.mysite.com/index.php/mycontroller';
+ $this->load->library('xmlrpc');
+ $this->xmlrpc->set_debug(TRUE);
+ $this->xmlrpc->server($server_url, 80);
+ $this->xmlrpc->method('call');
+
+ [ 131 ]
+
+
+Using CI to Communicate
+
+You define the URL of the receiving site, specifying the controller that contains the
+XML-RPC server that you want. You load the XML-RPC class, define the server, and
+the method you want to use鈥攖his is the name of the call you want to make, not of
+the actual function you want to use. If the function you are calling needs parameters,
+you pass them this way:
+ $request = array('optimisation','sites');
+
+As you see, we're passing two here.
+
+Then, you check if a response has been received, and do something with it:
+ if ( ! $this->xmlrpc->send_request())
+ {
+ echo $this->xmlrpc->display_error();
+ }
+ else
+ {
+ print_r($this->xmlrpc->display_response());
+ }
+
+The simplest option is to display it; but in a real application you're more likely to
+want the machine to analyze it, e.g., by using a regex, and then to act on the results.
+For instance, if the result contains an error message, you might want to record the
+error in your database, and take action to report it to the human user.
+
+
+Formatting XML-RPC Exchanges
+Let's use a real, if simplified, example. In this section, we will create an XML-RPC
+call锟絩esponse that lets you remotely trigger a database optimization.
+
+The client we wrote above, is asking for a method known as 'call' and supplying two
+parameters: 'optimisation' and 'sites'.
+
+The server on the receiving site maps this request for 'call' onto a function
+called 'myfunction'.
+
+Let's have a look at this function. It's basically an ordinary function within the
+controller. It attempts to optimize a MySQL database table, and returns 'success' or
+'failure' depending on the result.
+ function myfunction($request)
+ {
+ $parameters = $request->output_parameters();
+ $function = $parameters['0'];
+ $table = $parameters['1'];
+
+
+ [ 132 ]
+
+
+ Chapter 9
+
+ if ($this->db->query("OPTIMIZE TABLE $table"))
+ {
+ $content = 'Success';
+ }
+ else
+ {
+ $content = 'failure';
+ }
+ $response = array(
+ array(
+ 'function' => array($function, 'string'),
+ 'table' => array($table, 'string'),
+ 'result' => array($content, 'string'),
+ ),
+ 'struct');
+ return $this->xmlrpc->send_response($response);
+ }
+
+Note the $request, set as the function parameter. This contains the $request
+array from the client鈥攔emember, it had two values, 'optimisation' and 'sites'. CI
+has transformed the array into an object, $request. So you can't get the individual
+parameters by treating it as an array, instead you have to use the $request
+->output_parameters() method of the $request object. This returns an array,
+which you interrogate in the normal way.
+
+Using this, we have told the function on the receiving site which table we want to
+optimize, the 'sites' table. We've also told it what to call the function ('optimisation').
+It adds a further parameter called 'result', gets the value, and returns all three to us.
+
+The result it sends back to the client site looks something like this:
+
+
+
+
+
+
+
+ function
+
+ optimisation
+
+
+
+ table
+
+ [ 133 ]
+
+
+Using CI to Communicate
+
+
+ sites
+
+
+
+ result
+
+ Success
+
+
+
+
+
+
+
+
+(Except it's not indented: I did that to make the structure clearer.)
+
+As you can see, our simple three word response (optimisation, exercises, success)
+has been wrapped in verbose layers of tags, in a way sadly typical of XML, to tell
+a machine exactly what is going on. There are three tag pairs.
+Each has a pair ('function', 'table', 'result' respectively). And each
+of these has a pair, which includes (as well as the data type) the
+actual information we want鈥攊.e. 'optimisation','sites','success'.
+
+Never mind that I don't like it. Computers thrive on this sort of stuff: it is precise,
+unambiguous, and easy for a machine to read. This chapter is about computers
+talking to each other, not about user-friendly interfaces.
+
+Now, your XML-RPC client function on your calling site can extract the values it
+wants and act on them. It's easy to do this with a regex, because each answer is
+clearly demarcated by XML mark-up brackets.
+
+Note how CI spares you a lot of fiddling around with angle brackets鈥攜ou didn't
+need to write any of this stuff.
+
+
+Debugging
+As soon as you start to test your client锟絪ever combination, you will probably get
+this message:
+ The XML data received was either invalid or not in the correct form
+ for XML-RPC. Turn on debugging to examine the XML data further.
+
+
+
+
+ [ 134 ]
+
+
+ Chapter 9
+
+Turn on debugging, by including the line:
+ $this->xmlrpc->set_debug(TRUE);
+
+in your client. This allows you to see exactly what your client-receiving site
+combination is sending back to you. Be warned, this is where debugging gets
+quite frustrating.
+
+There are several places where the exchange can go wrong:
+
+ The remote site is not responding properly. (You may have to temporarily
+ 鈥
+ set it to display errors in order to work out why it is not responding. This
+ is annoying if it is an active site. The additional Catch 22 is that it will then
+ display鈥攊.e. return as HTML鈥攅rror messages, which aren't part of the XML
+ response your client expects, so you will get a second set of error messages,
+ caused by the first set鈥 ) Debugging this may involve quite a lot of FTP
+ transfers back and forth, until you get it right.
+ The client code may not be working properly.
+ 鈥
+ You have got the URL wrong. (This needs to be CI's way of addressing the
+ 鈥
+ controller in which the XML_RPC server sits鈥攊.e. http://www.mysite.com/
+ index.php/mycontroller. If you put all the server code in the controller
+ constructor instead of in the index function, it will still work, but you need to
+ address the function you want to call by name鈥攅.g.
+ http://www.mysite.com/ index.php/mycontroller/myfunction).
+ The XML interchange may not be exactly right. The set_debug function
+ 鈥
+ allows you to see what is being sent back, but you can spend quite a while
+ staring at this trying to work out where it has gone wrong. (Believe me鈥)
+
+However, once you get all this right, you've done something quite clever. You've
+built a function in a remote site, and called it remotely.
+
+In other words, you've set up an application that can do maintenance or other
+operations on remote sites. If you have several remote sites to manage, you can easily
+replicate this across them, allowing you (for instance) to optimize all your database
+tables once a day by one action on just one site.
+
+
+Issues with XML-RPC?
+Security is an issue, of course. You would want to password-protect your function
+calls, so that the client had to send a password as a parameter before the receiving
+site responded. This can be done simply by sending the password as an additional
+parameter in the request, and having the called function check it before responding.
+
+
+
+ [ 135 ]
+
+
+Using CI to Communicate
+
+If you were exposing critical functions, you might want the whole thing to take place
+behind an SSL layer. Our example looks harmless鈥攜ou might not mind if a hacker
+repeatedly broke in to your site, but all he or she did was tidy up your database
+for you each time. On the other hand, it would be a good basis for a Denial of
+Service attack.
+
+It has to be said that XML-RPC is frustrating and time-consuming to set up and
+debug, even with CI's very considerable help. You are writing and debugging two
+sites at once, and the XML format for transmitting data between them can only be
+called picky. It doesn't let you get away with even the smallest mistake.
+
+Some would argue that XML-RPC is a superseded technology, with most new
+interfaces or APIs being written in more complex languages such as SOAP (which
+are even more time-consuming to set up) .
+
+However, for our purposes, XML-RPC is ideal. It allows us to make our remote
+websites perform complex internal functions without bothering us with the details.
+
+
+
+Talking to Humans for a Change: the
+Email Class
+We've put together a lot of the building blocks for our web test site. We have a
+database of tests, and we've built functions to run different types of tests. We can
+access our site and check that we are seeing the right page; we can check that all the
+files are where we expect them to be on the remote server. We can automatically run
+functions on the site and get it to optimize itself. It's fairly simple to write code that
+uses these tools to run a suite of tests whenever we want, either when we log on or
+by some automatic reminder, such as setting a 'cron' job on a Linux server to start
+our program running at suitable intervals.
+
+But it's not really enough to run tests and just store the results away in a database. If
+something is wrong, we need to know as soon as possible.
+
+Here's where CI's email class comes in. It allows us to program our site to send us
+emails whenever certain conditions are reached. You might want to send an email
+for each failed test, or you might want to run a series of tests, collect the results, and
+then send just one email report.
+
+To use the email class, first (as always) you have to load it.
+ $this->load->library('email');
+
+
+
+
+ [ 136 ]
+
+
+ Chapter 9
+
+Then we have to set some configuration variables. This is where we can run into
+problems, because the class depends on the server that is hosting our code being able
+(and willing) to send email for us. Once again, we may have to check with the ISP.
+(It's also difficult to test this on a local site, because Xampplite, for instance, may not
+be able to offer you a mail server.)
+
+However, once we've sorted out your ISP, we can easily configure the email class.
+There are a lot of options, all listed in the on-line user guide. The main ones are:
+
+ protocol: does your system use mail, sendmail or SMTP to send emails?
+ 鈥
+ mailpath: where is your system's mail program stored?
+ 鈥
+
+You set them like this:
+ $config['protocol'] = 'sendmail';
+ $config['mailpath'] = '/usr/sbin/sendmail';
+ $this->email->initialize($config);
+
+Other options, all of which have sensible defaults, include things like word-
+wrapping, character sets, whether you want to send text or HTML emails, and so on.
+Setting the options up and getting them working is the only (potentially) difficult
+part of using this class.
+
+Once you've loaded the class and initialized it, using it is ridiculously intuitive.
+ $this->email->from('david@mysite.com');
+ $this->email->to('someone@myownsite.com');
+ $this->email->bcc('fred@somewhere.com');
+ $this->email->subject('Test message');
+ $this->email->message('Hello world');
+ $this->email->send();
+
+will send me an email, copied to my client, reporting whatever message I want.
+
+If you're sending more than one email, start each new one with:
+ $this->email->clear()
+
+just to make sure that you start with a clean slate each time.
+
+You can also use the email class to send attachments. Remember that the attachment
+file must already be saved on the server that is sending the email, and you have to
+specify where, in terms of the server root file (giving the server address, not the
+web address).
+
+Get its address and name like this:
+ $path = $this->config->item('server_root');
+ $file = $path.'/my_subdirectory/myfile.htm';
+
+ [ 137 ]
+
+
+Using CI to Communicate
+
+then just add this line:
+ $this->email->attach($file);
+
+before the $this->email->send();.
+
+This simple CI function is so much easier than trying to write out the full PHP code
+to send attachments. It handles all the protocols involved, without you even having
+to be aware of them.
+
+If you include the line:
+ $result = $this->email->print_debugger();
+
+in your code, and print out the $result variable, you'll get a screenful of useful
+information, like this:
+ Your message has been successfully sent using the following protocol:
+ mail
+ User-Agent: Code Igniter
+ Date: Wed, 18 Apr 2007 13:50:41 +0100
+ From:
+ Return-Path:
+ Bcc: fred@somewhere.com
+ Reply-To: "david@mysite.com"
+ X-Sender: david@mysie.com
+ X-Mailer: Code Igniter
+ X-Priority: 3 (Normal)
+ Message-ID: <462614219c1a6@upton.cc>
+ Mime-锟絜rsion: 1.0
+ Content-Type: multipart/mixed; boundary="B_ATC_462614219d14d"
+ This is a multi-part message in MIME format.
+ Your email application may not support this format.
+ --B_ATC_462614219d14d
+ Content-Type: text/plain; charset=utf-8
+ Content-Transfer-Encoding: 8bit
+ test message
+ hello world
+
+ --B_ATC_462614219d14d
+ Content-type: text/html; name="myfile.html"
+ Content-Disposition: attachment;
+ Content-Transfer-Encoding: base64
+
+(etc. etc.)
+
+
+ [ 138 ]
+
+
+ Chapter 9
+
+If something went wrong, then the debug information will return any server error
+messages as well. For instance, if I set the delivery method to SMTP without setting
+the right host or permissions:
+ $config['protocol'] = 'smtp';
+
+it can't send the message, and it tells me:
+ You did not specify a SMTP hostname
+ Unable to send email using PHP SMTP. Your server might not be
+ configured to send mail using this method.
+
+Bear in mind, however, that 'sendmail' is potentially misleading here鈥攊t returns a
+success message if it has passed the message on within the server, but this doesn't
+necessarily mean that the message has actually been sent. (So if you set the wrong
+'mailpath' option, 'sendmail' may report that it has sent the email, when it actually
+hasn't.) CI relies on the messages it gets back from the mail sending application, so it
+can be fooled. As always with emails, they only way to be sure they have gone is to
+check that they've arrived鈥攂ut that's another story.
+
+CI's email class includes several useful options, all explained in the online User
+Guide. For instance, you can set it to send text or HTML format mail鈥攊f you chose
+HTML there's even a function to allow you to set a separate 'text' message for people
+who don't accept HTML email.
+
+You can also set it to use different character sets, and to handle word-wrapping. You
+can set batch sizes, if you intend to send a lot of emails to a long mailing list, so that
+your server doesn't get overloaded. (Or your ISP doesn't panic and shut you down,
+thinking you are a spammer.)
+
+
+
+Summary
+We've now used CI to build some very sophisticated tools for our website, which
+give it some significant functionality.
+
+Firstly, we used CI's FTP class to simplify and automate file transfer operations.
+Initially, we've just used this class to check that the files we expect to find on
+our site are actually there, and that noting unexpected has been added. This in
+itself is a valuable check, as many of the problems websites throw at you involve
+unexpected alterations of files, usually by site admins but sometimes by hackers. This
+function will check regularly. The CI FTP class also offers the possibility of remote
+maintenance and updating of sites.
+
+
+
+
+ [ 139 ]
+
+
+Using CI to Communicate
+
+Then we looked at developing our own private 'web services' using CI's XML-RPC
+classes. These allow us automatically to call functions on a remote site, pass in
+parameters if necessary, and have the results returned to us鈥攋ust as if we'd been
+logged on to the remote site instead of to our test site. We used this to have the
+remote site optimize a table in its database, and report back to us. Once again, we've
+gone beyond our original plan of simply monitoring the remote sites. Now we are
+able to instruct them to check or optimize themselves as well.
+
+Lastly, we looked at the CI email class, which allows our testing site to generate
+emails. The CI code is extremely simple to use, and means that our site can notify us
+whenever it thinks there is a problem. CI makes it simple to build and send an email,
+and even to send attachments.
+
+
+
+
+ [ 140 ]
+
+
+ How CI Helps to Provide
+ Dynamic Information
+We've put a lot of thought into building our test website now, and CI has made it
+easy to do some very complex things. We've set up databases, used FTP, built tests,
+and started to email the test results. But it's easy to get caught up in techie things
+and forget that websites are often judged largely on presentation, on how well they
+process data, and how appropriately they display it to human users.
+
+Here are a few CI classes that help with some problems that arise regularly when
+you are building a website, particularly when it comes to delivering dynamic
+information to your users:
+
+ The date helper translates different date formats and helps you cope with
+ 鈥
+ time zones.
+ The text and inflector helpers provide useful functions to manipulate and
+ 鈥
+ convert strings.
+ The language class makes it easier to write websites that display the same
+ 鈥
+ information in different languages, depending on user preference.
+ The table class鈥攕aves a lot of tedious
s.
+ 鈥
+
+ You can automatically cache high-load dynamic pages for a faster response.
+ 鈥
+
+Each of these can save you a lot of coding time, while making your site look more
+professional (and keeping it easier to update).
+
+
+How CI Helps to Provide Dynamic Information
+
+
+The Date Helper: Converting and
+Localizing Dates
+You know those websites that expect you to understand machine dates? MySQL's
+native 'timestamp' format, for example, is very useful, but it looks very careless to let
+your site users see things like:
+ 20070124161830
+
+or even:
+ 2007-01-24 16:18:30
+
+Of course, most people can work out what it means, but it gives your site an
+unprofessional and unfinished air. CI comes to the rescue with its date helper. This is
+loaded with:
+ $this->load->helper('date');
+
+and immediately gives you access to a lot of useful functions. See the online User
+Guide for a full description.
+
+Dates can be specified in many different ways. CI's standard_date() function gives
+you ten ways of displaying the same date:
+
+1: atom 2006-12-31T11:34:44Q
+
+2: cookie Sunday, 31-Dec-06 11:34:44 UTC
+
+3: iso 2006-12-31T11:34:44+0000
+
+4: RFC 822 Sun, 31 Dec 06 11:34:44 +0000
+
+5: RFC 850 Sunday, 31-Dec-06 11:12:34 UTC
+
+6: RFC 1036 Sun, 31 Dec 06 11:34:44 +0000
+
+7: RFC 1123 Sun, 31 Dec 2006 11:34:44 +0000
+
+8: RFC 2822 Sun, 31 Dec 2006 11:34:44 +0000
+
+9: RSS Sun, 31 Dec 2006 11:34:44 +0000
+
+10: W3C 2006-12-31T11:34:44Q
+
+All you need to do is specify which you want. For instance:
+ $time = now();
+ echo standard_date('DATE_RFC822', $time);
+
+
+ [ 142 ]
+
+
+ Chapter 10
+
+There are also functions to convert between different types of date锟絫ime values.
+Their names are self-explanatory, and the exact syntax is described in the online User
+Guide. They enable you to do quite clever conversions very simply.
+
+For instance, this code:
+ function converttimes()
+ {
+ $this->load->helper('date');
+ $mysql = '20070101120000';
+ $table = '';
+ $table .= "
";
+ echo $table;
+ }
+
+produces this result:
+
+Start with MySQL time 20070101120000
+now convert to unix timestamp 1167652800
+then back to 'human' time 2007-01-01 12:00 PM
+now convert unix stamp to local time in Tehran 1167661800
+and say that in human time 2007-01-01 02:30 PM
+
+There's a lot of useful code available to you behind these functions, and they make
+international time zones, in particular, much easier to use.
+
+The date helper also has timezone_menu(), a function that generates a drop-down
+menu of time zones. You can use this in conjunction with a database to allow site
+users to select a time zone, and later to present all their time references in their own
+'local' time. In exchange for writing:
+ echo timezone_menu();
+
+ [ 143 ]
+
+
+How CI Helps to Provide Dynamic Information
+
+you get the following screenshot:
+
+
+
+
+It looks at first as if CI offers an automatic way to handle time zones too, in the
+date helper's now() function. The User Guide suggests that you set the 'master time
+reference' in your config file to 'local' or 'gmt', using:
+ $config['time_reference'] = 'local';
+
+Local is the default. If you set it to 'gmt', the code appears to return the system time
+(if there is one) based on the PHP mktime() function; if this is not valid, or you set
+the config file to 'local', it returns a time based on the time() function.
+
+However, both of these are dependent on your server: it must be set to an accurate
+time and its default time zone must be set. (You can check this with phpinfo().) But
+the time zone may not be set, and your server may not be in the same time zone as
+you: this is quite common with large companies, for example.
+
+So CI itself doesn't actually know what your time zone offset is, though it may be
+able to get your server's offset. Therefore, if you used timezone_menu() to capture
+users' time zone preferences, you can't just rely on the date helper's now() function to
+translate GMT times to their local times. You will need to look up their preferences,
+and write separate code to translate times whenever you want to display them.
+
+
+
+
+ [ 144 ]
+
+
+ Chapter 10
+
+
+Working with Text: the Text Helper and
+Inflector Helper
+The text helper has a series of functions that help you to manipulate text in various
+ways. See the online User Guide for full details. I'd just like to show you a few of the
+useful things you can do.
+
+The word_limiter() function intelligently truncates strings to a length you set.
+word_wrap() wraps text to a length you specify. And word_censor() replaces
+words you don't want to see with harmless equivalents.
+
+There are also functions to convert ascii_to_entities() and back again, which
+may help prevent those times when text in formats like MS Word displays oddly if
+you copy it to a web page.
+
+Inflector helper functions will change words from singular to plural or vice versa,
+though they are caught out by irregular forms like 'sheep锟絪heep' and 'child锟
+children', and would make some mistakes, e.g., turning 'day' into 'daies'. They can
+also 'camelize', or underscore spaces between multiple words, and then turn them
+back again.
+
+You can have fun with these, for instance, this code:
+ function converttext()
+ {
+ $this->load->helper('text');
+ $this->load->helper('inflector');
+ $mytext = "Mr Bill Gates is a man I like. He is a very clever man
+ and writes superb software";
+ echo "$mytext ";
+ $disallowed = array('like', 'clever', 'superb');
+ $string = word_censor($mytext, $disallowed);
+ echo "Censored, this might read: ";
+ echo "$string ";
+ $mywtext = word_limiter($mytext, 3);
+ $mytext = underscore($mywtext);
+ echo " His name could be written like this $mytext";
+ $mytext = camelize($mywtext);
+ echo "or like this $mytext";
+ }
+
+
+
+
+ [ 145 ]
+
+
+How CI Helps to Provide Dynamic Information
+
+will give you this result:
+
+Mr Bill Gates is a man I like. He is a very clever man and writes superb software
+Censored, this might read: Mr Bill Gates is a man I ####. He is a very ######
+man and writes ###### software
+His name could be written like this mr_bill_gates鈥r like this mrBillGates鈥
+
+These functions can be extremely useful if you are taking in text from other sources
+and need to convert it in one way or another, or to censor it. They may save you a lot
+of time spent writing regexes.
+
+
+
+Going International: the Language Class
+If you are writing a website that may be viewed in more than one country, CI can
+present pages in more than one language for you. It works like this:
+
+Firstly, you identify the text presented to your users that needs to be translated. Let's
+go back to one of the first examples of displaying dynamic data that we discussed in
+this book. Your welcome page might be called by code in a model, which says:
+ function hello($name)
+ {
+ $data['mytitle']= 'Welcome to this site';
+ $data['mytext']= "Hello, $name, now we're getting
+ dynamic!";
+ $this->load->view('testview', $data);
+ }
+
+The strings assigned to the $data array are the messages displayed to the user:
+
+
+
+
+But you might know that the user was a German speaker鈥攑erhaps because of
+the location of his or her server, or possibly because he or she was logged in, and
+had stated a language preference. It would be nice if you could greet him or her in
+German. CI provides an easy method to do this.
+
+ [ 146 ]
+
+
+ Chapter 10
+
+First, you need to set up a language file. If you look in the system folder, you'll see
+there already is a language folder with an English sub-folder. This in turn contains
+a series of files鈥攅.g., unit_test_lang.php. This is a PHP file that simply defines an
+associative array of expressions to be shown to users:
+
+
+The array values are the expressions you want displayed, the array keys are
+whatever shorthand you want to use to identify them to yourself. The filename must
+end with '_lang'.
+
+We need to set up our own, in each of the languages we want to show. Let's call the
+first one welcome_lang.php and save it in the system/language/English sub-
+folder. It should look like this:
+
+
+The array keys can be anything you like: but it's a good idea to prefix them, say with
+'ut' for the unit test language array, and 'welcome' for the array we're writing now.
+They all go into the same base array, so if you inadvertently enter two values with
+identical keys, the second will overwrite the first.
+
+The original function that set up the page needs to be altered. Firstly, you need to
+load the language file. In this example, I've included it in the function, but normally,
+it makes more sense to do it inside the class constructor. Notice that although the file
+name ends in _lang (welcome_lang.php), you omit this suffix when you load it (i.e.
+you load 'welcome', not 'welcome_lang'). Secondly, you use the array keys instead of
+actual text鈥攖hat is, to say:
+ function hello($name)
+ {
+ $this->lang->load('welcome');
+ $data['mytitle']= $this->lang->line('welcome_title');
+ $data['mytext']= $this->lang->line('welcome_text1');
+ $data['mytext'].= $name;
+
+ [ 147 ]
+
+
+How CI Helps to Provide Dynamic Information
+
+ $data['mytext'].= $this->lang->line('welcome_text2');
+ $this->load->view('testview', $data);
+ }
+
+But this only gives us the same page as we had before: it's still in English. If we want
+to allow translation into German, we need another language file. Firstly, we create a
+new subfolder: alongside system/language/english we create system/language/
+german. In the new folder, we save a file with exactly the same name as the English
+version: 'welcome_lang.php'. (Not willkommen_sprach.php鈥攕orry, just my little joke.)
+
+This file is identical to the English original鈥攐n the left-hand side of the array, at
+least. The keys are the same, but the array values on the right-hand side now have to
+be in German.
+
+
+(I'm afraid that you have to do the translation yourself鈥擟I doesn't do that for you!)
+
+There's one thing left to do. When the original 'hello' function loaded the
+language file:
+ $this->lang->load('welcome_lang');
+
+it did not specify which language, so the default was English. As you might expect,
+the default language is specified in the 'config' file:
+ $config['language'] = "english";
+
+To get German, the language loading expression in the 'hello' function should
+additionally specify the name of the folder in which the German array is saved.
+(Logically enough, this is 'german'.)
+
+So the function now says:
+ function hello($name)
+ {
+ $this->lang->load('welcome', 'german');
+ $data['mytitle']= $this->lang->line('welcome_title');
+ $data['mytext']= $this->lang->line('welcome_text1);
+ $data['mytext'].= $fred;
+ $data['mytext'].= $this->lang->line('welcome_text2);
+ $this->load->view('testview', $data);
+ }
+
+ [ 148 ]
+
+
+ Chapter 10
+
+and the resulting page looks like this:
+
+
+
+
+All we need to do now is to ensure that our function loads the right
+language dynamically.
+
+Assuming we've detected the user's language preference, and stored it in the variable
+$user_language_pref, we need something to load the language files conditionally,
+like this:
+ if($user_language_pref == 'german')
+ {$this->lang->load('welcome', 'german');}
+ elseif($user_language_pref == 'french')
+ {$this->lang->load('welcome', 'french');}
+ // etc etc
+
+It takes a degree of self-discipline to write a code like this. You have to remember
+never to put actual text into your code, but instead to create an entry in a language
+file each time. But once you've done that, all you have to do is copy the language
+file and give it to someone to translate into your target language(s), and your site is
+magically available in translation. If you change the wording of the site, you only
+have to change the language files. If you have used some expressions more than
+once, you don't have to hunt through the pages looking for each example.
+
+If your site uses long stretches of complex text, it becomes less viable to translate it in
+this way. But for the 'boilerplate' text scattered around every website, CI's language
+class works well and makes your site look much more impressive.
+
+
+
+
+ [ 149 ]
+
+
+How CI Helps to Provide Dynamic Information
+
+
+Making HTML Tables the Easy Way: the
+Table Class
+I've been using CI for some months now, but I keep stumbling on functions that
+make life easier.
+
+Here's a good example, for anyone who spends a lot of time writing things like:
+ echo "
$value1
$value2
";
+
+CI's table class allows you to auto-generate HTML tables. Let's display details of
+some of the tests we've run. You start off by loading the class, as always. Then you
+can specify the table data as an array, like this:
+ $this->load->library('table');
+ $data = array(
+ array('name', 'type', 'time'),
+ array('test 1', 'ping', '1166627335'),
+ array('test 2', 'ping', '1166627335'),
+ array('test 3', 'ete', '1166702400')
+ );
+ echo $this->table->generate($data);
+
+But the function really comes into its own when you automatically generate the data
+directly from the object returned by a database query. For instance, this short piece
+of code:
+ function dotable()
+ {
+ $this->load->database();
+ $this->load->library('table');
+ $query = $this->db->query("SELECT name,type,time FROM events");
+ echo $this->table->generate($query);
+ }
+
+
+
+
+ [ 150 ]
+
+
+ Chapter 10
+
+gives you the query results in a properly formatted HTML table. I can't resist
+showing it, though the default format is dull!
+
+
+
+
+That's an amazing saving of your time鈥攋ust four lines of code returns a query and
+wraps it up for you in HTML. In fact, a small tear comes to my eye when I think of
+all the time I used to spend writing:
+
+
$variable1
$variable2
//etc.
+
+While, as you can see, CI's basic table layout isn't wonderful, you can set your own
+template, using CSS styles if you wish, and the function will faithfully follow that.
+The template is an array inside the 'table' class, so you will need to reset it each time
+you call the class.
+ $tmpl = array (
+ 'table_open' => '
',
+
+ 'heading_row_start' => '
',
+ 'heading_row_end' => '
',
+ 'heading_cell_start' => '
',
+ 'heading_cell_end' => '
',
+
+ 'row_start' => '
',
+
+ [ 151 ]
+
+
+How CI Helps to Provide Dynamic Information
+
+ 'row_end' => '
',
+ 'cell_start' => '
',
+ 'cell_end' => '
',
+
+ 'row_alt_start' => '
',
+ 'row_alt_end' => '
',
+ 'cell_alt_start' => '
',
+ 'cell_alt_end' => '
',
+
+ 'table_close' => '
'
+ );
+
+ $this->table->set_template($tmpl);
+
+There is a default template array, which looks like this, on which the function bases
+its design. Note that there are two sets of row definitions (row and row_alt), in case
+you want the colors of the rows to alternate.
+
+If you submit revisions to part or all of the template, the function reacts accordingly,
+generating different HTML markup.
+
+You'll have noticed that the template is just an array, and you submit revisions
+by revising the values for each key. For instance, if you have a CSS file defined
+somewhere with a class called mytable, you can refer to that:
+ $tmpl = array ( 'table_open' => '
' );
+
+You don't have to alter every value: those you don't alter remain at the
+default setting.
+
+Now your table magically jumps out in the format you specified.
+
+
+
+Caching Pages
+By now, we're writing some pretty complex code. The server has to sit down and
+puzzle out each dynamically generated page. While it's simple for you to write a
+function like dotable() above, the poor old server has to do more work as a result.
+
+Sometimes, this can lead to your pages taking longer to load than you would like.
+There may be no way round this. If you're writing a report that will be different each
+time you write it, then you just have to wait. However, you may be generating a
+page that will stay the same for a while. A blog, for instance, stays the same until you
+put another entry on it. If your blog gets a thousand views a day, on a day when you
+didn't add a new posting, each view will be the same, and it's a waste of time for the
+system to re-generate the same page over and over.
+
+
+ [ 152 ]
+
+
+ Chapter 10
+
+The way round this is to cache the page. You generate the page once, and the HTML
+produced is saved in a 'cache' file with a timestamp, as well as being returned to
+someone's browser for display on their screen. Then, when the next viewer requests
+that page, the system checks to see how long ago it was last generated and saved. If
+this is within a time limit you set, it serves up the cache page. If not, it generates the
+page from scratch.
+
+Sounds like some pretty complex coding is required here. Except if you're using CI.
+If you are, you need to do two things:
+
+Find the /system/cache file in your site. It should be empty, except for an
+index.html file. Make sure the folder is writeable鈥攊.e. permissions set to 666, if
+you're on a Linux system.
+
+Insert, somewhere in a controller function that generates an HTML page, the line:
+ $this->output->cache(5);
+
+where 5 is the number of minutes you want your cache to persist before the page
+is regenerated.
+
+That's it. If you now load the function, you'll see the page load as usual. If you now
+look at your /system/cache folder, however, you'll see a new file in there, with a
+meaningless title.
+
+Open this up (in a text editor) and you'll see it contains the HTML code for your
+page, plus a timestamp. If you request the same page again before the timestamp is
+five minutes old, you'll get the cached page. If you wait longer and the cache file is
+out of date, your next request will automatically delete it and replace it with a
+newer version.
+
+If you change your mind about caching the page, delete the this->output
+->cache(5) line from you controller, and your page will be served up afresh each
+time. (The last cached file will stay in your /system/cache folder until you delete it
+manually.) If you want to continue caching, but accidentally delete a cache file at any
+time, don't worry; the system will create a new one when that page is next called.
+
+CI makes this is so quick and simple that it is tempting to cache every page! Just
+remember that you don't always want to do this: it's best for high load pages that
+don't change very often, but may not help much on others.
+
+
+
+
+ [ 153 ]
+
+
+How CI Helps to Provide Dynamic Information
+
+
+Summary
+CI offers you lots of goodies to make coding easier and your websites more
+professional. This chapter looked at just five of them:
+
+ The text and inflector helpers provide useful functions to manipulate and
+ 鈥
+ convert strings.
+ The date helper allows you to convert between different date formats and
+ 鈥
+ also to cope with time zones.
+ The language class makes it easier to write multi-lingual websites, which
+ 鈥
+ respond to user preferences. Alas, you still have to do the translation!
+ The table class lets you output properly formed HTML tables, directly from a
+ 鈥
+ database query if you need to.
+ Automatically caching high-load dynamic pages provides a faster response.
+ 鈥
+
+
+
+
+ [ 154 ]
+
+
+ Using CI to Handle Files
+ and Images
+This chapter looks at several useful CI functions and helpers. Each of them is a
+good example of how a few lines of CI code give you seamless access to a range of
+applications and actions that would take lots of specialized knowledge to code from
+scratch. In many cases, CI is simply providing an interface to code classes that were
+already out there, and which you could download from PEAR or some other source.
+But CI gives you a standard interface: you just treat it as native CI code, and the
+framework does all the interfacing stuff for you.
+
+Let's look at five activities in this chapter:
+
+ The file helper makes it easier to write to, and read from files.
+ 鈥
+ The download helper makes it easy for your website to download files direct
+ 鈥
+ to the user, rather than displaying them as HTML.
+ The file upload class works the other way, allowing users to put files on your
+ 鈥
+ site, with built-in security precautions to limit what they can do.
+ The image manipulation class allows you to do several useful things with
+ 鈥
+ images, and we'll look at how to resize them and watermark them.
+ Lastly, the Zip class allows you to compress files before your users
+ 鈥
+ download them.
+
+Each of these examples hides a lot of clever coding and allows you to write practical
+applications with a minimum of fuss. In many cases, they add extra code to make the
+activity more robust.
+
+
+Using CI to Handle Files and Images
+
+Let's look at them one by one:
+
+
+
+The File Helper
+PHP's syntax for reading and writing files is not easy to grasp at first sight. CI's file
+helper contains a few useful functions, which act as a wrapper for PHP's own file
+handling operations. Start off as always by loading the helper:
+ $this->load->helper('file');
+
+Then life gets a lot simpler. For instance, to write to a file, all you need to know is:
+
+ The location of your file.
+ 鈥
+ The text you want to write to it.
+ 鈥
+ The mode in which you want to open the file. Modes are defined in the PHP
+ 鈥
+ manual (see the page on 'fopen'). They include 'r' for read, 'w' for write (write
+ to the file, overwriting data already there), and 'a' for append (write to the
+ file, adding on to existing data). In each case, adding a '+', say 'a+', opens
+ the file for both read and write operations. 'a' and 'w', but not 'r' or 'r+', also
+ create the file, if one is not already there.
+
+Then you use these three pieces of information as parameters to the
+write_file() function:
+
+ write_file('e:/filetest.txt', 'hello world', 'a+')
+
+This is simpler and more intuitive than PHP's two-step code:
+ if ( $fp = fopen('e:/filetest.txt','r+'))
+ {
+ fwrite($fp, 'hello world');
+ }
+
+Once again, the CI code adds a little extra: it automatically locks the file before
+writing and unlocks it afterwards. The helper returns 'FALSE' if the file operation
+doesn't take place, so you can use it to report success or failure. You have to specify a
+title for your file, but if you don't specify a filepath, it is placed in the web root folder
+for your site, where your main index.php file is.
+
+ Of course, any folder in which you create or write to a file, must have
+ write permissions set. Remember also that if you are running on
+ a Windows system you have to use forward slashes 鈥 /鈥 to describe
+ your filepath.
+
+
+
+ [ 156 ]
+
+
+ Chapter 11
+
+In our application, we can combine this helper with the database utility class. This
+allows us to create, back up, repair, and optimize databases and tables, though only
+on MySQL and MySQLi databases. Mix it in with the file helper, and you create a
+neat backup routine.
+ $this->load->dbutil();
+ $backup =& $this->dbutil->backup();
+ $this->load->helper('file');
+ write_file('e:/mybackup.gz', $backup);
+
+The above code writes the latest version of our database to a file on the server.
+
+Reading a file back again is equally simple:
+ $content = read_file('e:/filetest.txt');
+
+There's also a function that returns an array of all files and/or folders in a
+given directory:
+ $filenames = get_filenames('e:/')
+
+although, if you use it in a directory with many files, you may find that PHP times
+out before it can list them all. You can use this in a simple piece of code to check
+that the files or folders actually in a folder are what you expect. Start by using the CI
+function to find the files actually present, and a reference array of files you expect
+to find, then use array_diff() to compare them. Given two arrays, array_diff()
+tells you what values are in the first that are not in the other, so you have to use it
+twice, putting each array first.
+ //list files actually found
+ $files_there = get_filenames('e:/rootfolder/system/application/
+ controllers');
+ // list files we expected
+ $files_expected = array('start.php', 'index.php');
+ // any found that we didn't expect?
+ $difference = array_diff($files_there, $files_expected);
+ echo " Missing files are:";
+ print_r($difference);
+ // any expected that we didn't find?
+ $difference = array_diff($files_expected, $files_there);
+ echo " Extra files are:";
+ print_r($difference);
+
+
+
+
+ [ 157 ]
+
+
+Using CI to Handle Files and Images
+
+Lastly, but far too horrible even to think about, there is the delete_files()
+function. This deletes all the files within any directory you specify, so that:
+ delete_files('c:/mydirectory/');
+
+would delete everything in mydirectory. If you add the optional parameter TRUE,
+as in:
+ delete_files('c:/mydirectory/', TRUE)?
+
+it will also delete all subfolders in that directory Use with great care: just
+imagine what:
+ delete_files("c:/", TRUE)
+
+might do!
+
+
+
+The Download Helper
+The download helper only has one function, but it complements the file helper very
+nicely. You might create a file on a website and then want to serve it up to the reader
+as a file鈥攁 text file, for example鈥攔ather than converting it into a web page.
+
+A good example would be a database backup file, like the one we created just now
+for our application.
+
+To recreate the database if it crashes, we need a text file in MySQL's own format. It
+isn't much use to us to see this on the screen:
+
+
+
+
+ [ 158 ]
+
+
+ Chapter 11
+
+We need to find a way of downloading it as a file. In other words, if we're working
+on a Windows system, we want to see this dialogue:
+
+
+
+
+In order to code this over an internet connection, you must specify the type of
+page you want in the HTTP headers. CI's download helper does this for you in the
+background. Load the helper with:
+ $this->load->helper('download');
+
+and its single method is used like this:
+ force_download($name, $data);
+
+where $name is the name you give to the downloaded file and $data is the file
+contents. If you want to download an existing file, you have to read it into a
+string first:
+ $data = file_get_contents("e:/mybackup.txt");
+ $name = 'backup.txt';
+ force_download($name, $data);
+
+The file $data can now be used directly to recreate the MySQL database. You can
+also use this helper to download reports directly, rather than forcing users to scrape
+them off the screen.
+
+
+
+
+ [ 159 ]
+
+
+Using CI to Handle Files and Images
+
+Behind the scenes, the helper takes care of identifying the MIME type and setting
+HTTP headers. It relies on one of the 'config' files, system/application/config/
+mimes, which is also used by the Upload class that we'll look at next. This config file
+stores an array of MIME types and the appropriate HTTP extensions鈥攅.g.:
+ 'rtf' => 'text/rtf',
+ 'text' => 'text/plain',
+
+which saves you having to remember them!
+
+If you regularly use file types that aren't included on CI's list, you can easily add
+them to the 'config' file.
+
+
+The File Upload Class
+Sometimes, you want to allow users of your site to upload files. These may be text,
+or images, or more exotic file types like MP3 audio or MPEG video. This is a more
+complex process than the file downloads we just discussed, but CI's file upload class
+takes care of most of the work for you. It also looks after some of the security issues.
+However, you should always think twice before allowing anyone to upload files to
+your site, and you may want to protect the upload page to prevent unauthorized
+users from doing it.
+
+First, you need to allocate space to store the uploaded files鈥攁 folder or directory on
+your server. This must be set with the correct permissions, allowing users to write to
+it. (i.e. 777 on a Unix锟 Linux system). Let's assume you call this folder uploads, and
+put it in your web root folder.
+
+CI's file upload class is loaded with:
+ $this->load->library('upload');
+
+Then you need to do three things:
+
+ Set defaults
+ 鈥
+ Write a controller to handle the uploads
+ 鈥
+ Provide your user with an upload form and a 'success' form
+ 鈥
+
+Let's take them in this order. First, set a series of defaults. You do this by creating a
+$config array.
+
+Let's say you want to set the path to the upload directory you just created. For this,
+you need to say:
+ $config['upload_path'] = 'uploads';
+
+
+ [ 160 ]
+
+
+ Chapter 11
+
+This line can either be in the controller you're about to write, or you can create a
+config/upload folder to contain it (this would be system/application/config/
+upload.php).
+
+
+
+It's important to grasp the difference between these two ways of setting your
+defaults. If you set the defaults from the config锟絬pload file, you don't need to
+specifically initialize the file upload class. Just load it and it will find the defaults
+for itself.
+
+However, if you leave the defaults in the controller, you need to specify where they
+are when you load the class, using a second parameter to the loading function,
+like this:
+ $this->load->library('upload', $config);
+
+where $config is the name of your array of defaults. (Don't try to set some defaults
+from the config锟絬pload file and some from the controller!)
+
+OK, so what's this fuss about defaults? So CI sets sensible defaults and you don't
+need to worry about them, right? Yes, but in this case you do. There are several
+important ones:
+
+ The location of your upload file: CI doesn't make an assumption about this,
+ 鈥
+ you have to tell it.
+ The types of file you want to allow your users to upload. This is set like this:
+ 鈥
+ $config['allowed_types'] = 'gif|jpg|png';
+
+ where the acceptable file types are specified with the pipe operator ( | )
+ between them. This setting would allow your site to upload the most popular
+ image file types but would not allow it to upload audio files, for example.
+ Setting this is a basic security measure: if you only want images to be
+ uploaded, don't allow people to upload executable files or large MP3s.
+ Note that you must set a value before a file type can be uploaded: the default
+ setting (i.e., no setting) allows no files to be uploaded.
+
+
+
+
+ [ 161 ]
+
+
+Using CI to Handle Files and Images
+
+ Max_size: it's sensible to set a limit on the maximum size of file, in kilobytes,
+ 鈥
+ that can be uploaded. You don't want malicious users filling up all your
+ space. The default setting is 0, which sets no limit.
+ Overwrite: if you already have a file in your uploads folder with the same
+ 鈥
+ name as the one that a user is uploading, should the old one be overwritten
+ and lost forever? This depends on what your site is doing and why you are
+ allowing uploads. CI defaults to 'FALSE', which means it doesn't overwrite
+ the old file and the new file is saved under a new name. Explicitly set this
+ default to 'TRUE', if you want new files to overwrite the old.
+ Note that CI doesn't automatically tell the user that it has renamed his or her
+ file, which may possibly be confusing: see below for how to get at reports on
+ the process.
+ You can also set defaults for the size, width, and height of images, for
+ 鈥
+ encrypting the file, and for trimming blank spaces from its title.
+
+Now that you have decided on your defaults, you need an upload controller. This is
+simple. Its role is to initialize the upload class, receive an upload from a user form,
+and then decide if it has been successful. If yes, it displays a report; if no, it returns to
+the upload form with a user message. At its simplest, it need only include one active
+function, do_upload(), like this:
+ load->helper(array('form', 'url'));
+ $this->load->library('upload');
+ }
+ /*now the function which does all the work!*/
+ function do_upload()
+ {
+ if ( ! $this->upload->do_upload())
+ {
+ $error = array('error' =>
+ $this->upload->display_errors());
+
+ $this->load->view('upload_form', $error);
+ }
+ else
+
+
+ [ 162 ]
+
+
+ Chapter 11
+
+ {
+ $data = array('upload_data' =>
+ $this->upload->data());
+ $this->load->view('upload_success', $data);
+ }
+ }
+ }
+
+This function needs an upload_form view, and an upload_success view. To build
+the first, use the form helper to build a normal form, pointing to the do_upload
+function of our 'upload' controller: but instead of opening it with:
+ echo form_open('upload/do_upload')
+
+(as we did in the forms we built in Chapter 5), you open it with the form helper's
+multipart function:
+ echo form_open_multipart('upload/do_upload');
+
+(Remember that we are writing code to produce HTML markup, so we need to echo
+it to the screen.)
+
+Then, instead of the form helper's form_input function, use the
+form_upload function:
+
+ echo form_upload($data);
+
+These two lines of code take care of a lot of really tedious stuff for you.
+
+Add as normal a submit button, and close the form.
+ echo form_submit('mysubmit', 'Submit Post!');
+
+Pass your $view variable to a view and load it. Your view should also echo the
+$error variable, which the do_upload function passed when it loaded the view.
+
+ echo $error;
+
+You should now see something like this:
+
+
+
+
+ [ 163 ]
+
+
+Using CI to Handle Files and Images
+
+Clicking on Browse allows the user to view the files on her or his own (local)
+computer, not your server. Once (s)he has selected a file, clicking on upload will
+call the upload controller, and the file will be transferred to the upload folder on
+your server.
+
+Let's say I try to upload a text file. (Remember that our allowed file types are limited
+to 'gif | jpg | png'.) I now see:
+
+
+
+
+CI is reporting back the type of error to me: this is the $this->upload->display_
+errors() function in the controller at work, adding an extra variable to the view.
+
+You can also have CI report data on successful uploads to you. As you see, the
+controller we wrote just now calls a view called upload_success, if the upload
+is successful. The data passed to this view is the contents of the function
+$this->upload->data. This gives you a full array of information about the
+upload process: possibly more than you would want to display.
+
+Let's say I uploaded a file called waltzer.jpg: the default report looks like this:
+
+
+
+
+ [ 164 ]
+
+
+ Chapter 11
+
+If you are using your site to create a rival to Flickr, for example, then this amount of
+information might confuse users who have uploaded their photographs! However,
+you can easily filter out any information that you don't want in the upload controller.
+
+Please note, by the way, that I set the 'overwrite' default to 'FALSE' when configuring
+the file upload class to write this example. I then used the code to upload the image
+file waltzer.jpg鈥攖wice.
+
+The previous screenshot is CI's report on the second successful upload. You'll see
+that the file has been renamed waltzer1.jpg. If I look in my upload directory, I can
+see both the original waltzer.jpg and the new waltzer1.jpg file. Depending on
+your application, you might want to compare the values for raw_name and orig_
+name and inform the user that the file name has been changed.
+
+CI does not compare the two files, only their names. If you are allowing several users
+to upload files, it is quite possible that two of them will inadvertently use the same
+filename for different files, and you may not want to lose the first. On the other hand,
+if you are using the site to upload reports that always have the same name, you may
+prefer only to have the latest version stored on the site鈥攊n which case overwriting is
+a simple way of saving space.
+
+Here's that image, by the way. We're going to do a lot more with it in the
+next section.
+
+
+
+
+CI's Image Class
+If you are allowing users to upload images to your site, you also need to look at
+CI's Image Manipulation class. This works with the three most popular image
+libraries for PHP: GD锟紾D2, NetPBM, and ImageMagick. (Use phpinfo() to find
+out which of these your server supports.) Image watermarking only works with
+GD锟紾D2, though:
+ [ 165 ]
+
+
+Using CI to Handle Files and Images
+
+The image manipulation class allows you to perform four basic functions
+with images:
+
+ Resize: You may want to fit them into a standard size on your screen; or you
+ 鈥
+ may want to cut them right down to 'thumbnail' images.
+ Crop
+ 鈥
+ Rotate
+ 鈥
+ Watermark (only available with GD锟紾D2): This is often used to put a
+ 鈥
+ copyright notice on an image, so that people can't simply download it from
+ your site and pass it off as their own work.
+
+Probably the most useful of these functions is resizing, so we'll look at that in a little
+detail. Cropping and rotating are less useful because you can't do them meaningfully
+unless you can see the image on the screen. To do this, you need some sort of user
+interface that allows the user to specify what she or he wants to do and control the
+way CI implements these functions, and you'll have to build this for yourself!
+
+Let's say you've uploaded your waltzer.jpg image, using the file upload class we
+just discussed, to your /uploads folder. (Permissions for this folder have to be set to
+777 for you to upload鈥攁nd also for you to manipulate the images, because CI needs
+to write the manipulated result back to the folder.)
+
+First, load the library:
+ $this->load->library('image_lib');
+
+Then, you need to set a few configuration details. (As with the file upload class,
+you can either do this in your code, or in a separate system/application/config/
+image_lib.php file.)
+
+There are several preferences you can set and they are listed in the online User
+Guide. Perhaps the most important are:
+
+ Which image library you are using. The default is GD2, so if you're not using
+ 鈥
+ that in your PHP installation, you would need to specify the one you are
+ running, e.g., $config['image_library'] = 'ImageMagick' (You'd
+ also have to supply a path to the ImageMagick library using:
+ $config['library_path'] = '/mypath';.)
+ The image you want to manipulate. This should be the path (relative to your
+ 鈥
+ site's root folder) and filename.
+ The size you want the image to be after processing鈥攚here 'x' is a number
+ 鈥
+ of pixels, the width is set by $config['width'] = x;, and the height by
+ $config['height'] = x;.
+
+
+ [ 166 ]
+
+
+ Chapter 11
+
+These are enough to resize your image, overwriting the old image file with the
+resized image. The code looks like this:
+ function do_image($image_name)
+ {
+ $this->load->library('image_lib');
+ $config['image_library'] = 'GD2';
+ $config['source_image'] = "$image_name";
+ $config['width'] = 75;
+ $config['height'] = 50;
+ $this->image_lib->initialize($config);
+ if(!$this->image_lib->resize())
+ {echo "failed";}
+ else{echo 'success!';}
+ }
+
+The library can do some other clever things as well. If you don't want to overwrite
+the original image, specify a new name and filepath for the new version, by adding:
+ $config['new_image'] = 'newfolder/newname.png';
+
+Or, if you want to create a thumbnail of the image, simply add:
+ $config['create_thumb'] = TRUE;
+
+instead. This has the effect of re-naming the new resized file with a default suffix
+of _thumb, so that waltzer.jpg becomes waltzer_thumb.jpg. (You can alter the
+default suffix as well鈥攕ee the User Guide.) So you then have two files in the same
+folder: the original and the thumbnail.
+
+Note that the thumbnail setting doesn't do anything else鈥攜ou still have to set the
+size you want it to be.
+
+Here's the image, shrunk right down to 75 by 50 pixels:
+
+
+
+
+An additional function of the image class allows you to watermark images. So,
+if you've put your own brilliant photograph on your website, you can also add a
+copyright notice to it.
+
+
+
+
+ [ 167 ]
+
+
+Using CI to Handle Files and Images
+
+Once again there are a lot of options, fully explained in the User Guide, but the basic
+code is simple. Initialize the class, tell it which image you want to watermark and
+what you want the watermark to say, and call the watermark function.
+ function wm_image()
+ {
+ $this->load->library('image_lib');
+ $config['source_image'] = 'uploads/waltzer.jpg';
+ $config['wm_text'] = 'Copyright 2007 - David Upton';
+ $config['wm_type'] = 'text';
+ $this->image_lib->initialize($config);
+ if(!$this->image_lib->watermark())
+ {echo 'failure to watermark';}
+ else {echo 'success';}
+
+ }
+
+(The wm_type option set to text allows you to watermark with text. Otherwise, set
+this option to overlay, and supply an image, which will be superimposed on your
+original image.)
+
+This is what the image looks like now.
+
+
+
+
+My actual code was slightly more complex than the example shown above, so that
+I could control the size and positioning of my watermark to make it more easily
+visible on this page. The default code shown above would be adequate for most
+purposes, but the watermark comes out too small to be clear on a printed page. See
+CI's online User Guide for more information about working with external fonts.
+
+This class is beautifully simple to use, and it's only when you look at the underlying
+code in the image manipulation library (in the file system/libraries/Image_lib.
+php) that you realize how much complex coding CI has spared you!
+
+ [ 168 ]
+
+
+ Chapter 11
+
+
+Easy File Compression with the CI Zip
+Class
+If you're moving around large files like images, you might need to compress them.
+CI contains a handy library for doing this.
+
+Let's take the photograph we've already used: waltzer.jpg. It's on our
+/uploads folder.
+
+As always, you start by initializing the Zip class. Once you've done that, you have to
+tell CI which file you want to zip, and create an archive to put it in. Then you use the
+read_file function to read it in and zip it, and the download function to download
+it to your desktop.
+ function zip_image()
+ {
+ $this->load->library('zip');
+ $this->zip->archive('my_backup.zip');
+ $path = 'uploads/waltzer1.jpg';
+ $this->zip->read_file($path);
+ $this->zip->download('my_backup.zip');
+ }
+
+The CI Zip encoding class is more complex than this and allows you several options.
+As usual, they're all set out in the online User Guide. But this should give you an
+idea of how easy CI makes it to zip downloads from your site, minimizing the
+bandwidth they consume and saving time for your users.
+
+
+
+Summary
+This chapter collects together a few CI helpers and classes with similar themes. They
+help you to:
+
+ Write and read files on your system, with minimal coding, while CI locks and
+ 鈥
+ unlocks the files in the background.
+ Download files rather than show them as HTML on the screen, with CI
+ 鈥
+ providing the HTTP headers and worrying about MIME types for you.
+ Upload files to your site, allowing you to specify security constraints like the
+ 鈥
+ size and type of files you allow.
+ Easily manipulate images, to resize or watermark them.
+ 鈥
+ Compress your files before you download them to your users.
+ 鈥
+
+
+ [ 169 ]
+
+
+Using CI to Handle Files and Images
+
+This to me is what frameworks are all about. Instead of a lot of tedious coding, they
+let you get on with building an application that works. They give you a standard and
+easy interface, and they worry about the details for you.
+
+
+
+
+ [ 170 ]
+
+
+ Production Versions,
+ Updates, and Big Decisions
+The great day has come. Your development site is running well enough on your
+local development for you to transfer it to a production site hosted on a remote web
+server. It should be easy to do this. Copy over all the files, including the whole of the
+system folder, update the config settings, copy over and link to the database, and
+away you go. Sometimes, it really is that easy.
+
+But when it isn't, it's always the night before you are giving an all-important
+presentation to a venture capitalist or a major prospect. So, in case this happens to
+you, this chapter covers:
+
+ What to look for in your config files
+ 鈥
+ Some diagnostic tools to use if you get stuck
+ 鈥
+ Some potential differences between servers that may trip you up
+ 鈥
+ Some notes on security, now that you're out there in the big world
+ 鈥
+
+Next, this chapter covers upgrading, and looks at some of the ways in which CI has
+changed in the year it's been available. How stable is it? What decisions should you
+make if you are committing a critical site to it? And what should you do, once your
+site is up and running, if Rick Ellis brings out a newer version of CI?
+
+Lastly, we briefly discuss making your own alterations to the core of CI. It's all there;
+it's open-source it's possible. Whether it's sensible or not is another matter.
+
+
+Production Versions, Updates, and Big Decisions
+
+
+Connections: Check the Config Files
+Systems usually fail at the interfaces. That's what your config files are there for: to
+give you a place to put all those interfaces. If you haven't done so, you've missed one
+of CI's major strengths.
+
+The major interface problems are likely to be:
+
+
+URLs
+CI works by finding files. Your user connects to index.php, and then a whole
+process of loading starts. At least, it should do. Make sure you have set the web
+address and server addresses correctly in your config files. The web address is your
+web root folder; you may have to ask your ISP for the server address, though usually
+it is clear from their own 'file manager' programme.
+
+I've had particular issues trying to run sub-domains. Many hosts allow them, but
+map the domains to folders in ways you don't expect.
+
+
+Databases
+Locating and connecting to your database is often a major issue. Look at your config
+file and your config/database file. You need to make sure that you have the correct
+site and server addresses, and the correct database name, address, user name, and
+password. Be careful with prefixes鈥攕ometimes these are added automatically. (Your
+site is called 'fred'. Your database, you think, is called 'mydata' and your user name is
+'mylogin'. But the server thinks they are called 'fred_mydata', 'fred_mylogin', etc.)
+
+Sometimes, it helps to create a new user on your database, even if you have one
+already, and set the log-in for this user's name and password. I have no idea why this
+works, but it does.
+
+In the config file, you can set CI to accept different types of URI protocols, which
+determine how the server handles the URI string. The default is:
+ $config['uri_protocol'] = "auto";
+
+but there are four other options that you can try if this doesn't work. If the correct
+option isn't set, you may find your site partly works, but (for example) forms don't
+call the target page.
+
+
+
+
+ [ 172 ]
+
+
+ Chapter 12
+
+Other config Files
+The config/routes file sets the default path the application follows, if the user
+doesn't specify a controller锟絤ethod through the URL (i.e. if they just log on to
+www.mysite.com). This should normally be set at the default:
+
+ $route['default_controller'] = 'index';
+
+If you renamed the system folder, then remember that you also need to alter the
+index.php file in the site's root directory. The default setting is:
+
+ $system_folder = "system";
+
+
+
+Look Out for PHP 4/5 and Operating
+System Differences
+CI should be able to live with any version of PHP including and later than 4.3.
+However, this doesn't mean that any PHP code you write will also be
+compatible鈥攕o if you wrote it on Xampplite using PHP 5, and are moving to an ISP
+with a PHP 4 server, look out for problems due to language differences.
+
+PHP (of whatever version) can be set up in different ways. It's worth running
+phpinfo() on your local and remote sites, and checking for differences.
+
+Case sensitivity is different between Microsoft and Linux servers. So if you've
+developed your site on a PC, and then uploaded it to a Linux-based server, expect
+the server to report that it can't find some of the models or libraries you want to
+load. If you've checked and they have been uploaded, then make sure that the
+capitalization is correct. Because class definitions and constructors in CI have
+to begin with a capital, it's easy to begin the file name with a capital too. If
+you load a model, say, inside a controller, and give it a capitalized name
+($this->load->Mymodel), then Windows and Linux may have different views
+about calls to $this->mymodel.
+
+As an extreme example of server differences, I once began to write a controller,
+decided to make it a model instead, and so saved it in the model folder without
+realizing that I had left the first few lines as:
+ class Myclass extends Controller {
+
+ function Myclass()
+ {
+ parent::Controller();
+
+
+
+ [ 173 ]
+
+
+Production Versions, Updates, and Big Decisions
+
+instead of changing them to:
+ class Myclass extends Model {
+ function Myclass()
+ {
+ parent::Model();
+
+Running locally on Xampplite, this did not throw an exception. Transferred to a
+remote Linux server, it immediately failed (and you can imagine how long it took to
+track down鈥).
+
+Some PHP functions also appear to behave differently on different operating
+systems: for instance, include_once() is case insensitive on Windows but not on
+other systems. That's not specifically a CI issue, though.
+
+Also, your database may be a different version鈥攎any ISPs are very traditional,
+running MySQL 3.23 for instance! This seems to cause some incompatibilities, which
+means that uploading a database by a SQL query is less easy than it should be. (For
+instance, it may not accept comments on db tables.)
+
+Linux has a different system of file permissions to Windows. Make sure that you
+have the correct permissions on your files and folders. Certain CI file permissions
+must be set correctly before the system can work.
+
+
+Diagnostic Tools
+The first line of the index.php file is:
+ error_reporting(E_ALL);
+
+which displays any PHP errors on your screen, like this:
+
+
+
+
+ [ 174 ]
+
+
+ Chapter 12
+
+Obviously, error reports like this look bad, and may give hackers too much
+information, so for the production version you alter this to:
+ error_reporting(0);
+
+But then, any problems may simply result in a blank screen, with no helpful
+diagnostic information. You may have to turn error reporting back on, until you have
+got the site running. One compromise is to set it to an intermediate level, such as:
+ error_reporting(E_ERROR);
+
+This will prevent 'warnings' but will still give you information about serious
+problems. 'Warnings' are usually conditions that don't stop the programme from
+executing, but may point to other underlying issues that you hadn't considered.
+
+The CI Profiler class鈥攕ee Chapter 8鈥攊s also very useful: it shows you what queries
+you are doing, and what is in the POST array.
+
+Various other tools I have found useful when things don't work:
+
+ 1. Set CI to print a log file. (Done from the config file鈥攕ee Chapter 8鈥攜ou need
+ to set:
+ $config['log_threshold'] = 4;
+ 4 shows all messages, including just notices and warnings; sometimes these
+ are pointers to an underlying problem. Then look at the log (printed in
+ /system/logs, filed by date.) This will tell you which parts of the CI system
+ have been called, so you can at least see where the process stopped. (Set the
+ value back to 0 to prevent further logging. Remember to do this, and to remove
+ the log files when you've finished: it's amazing how much space they take up.)
+
+ 2. If you can access them, print out the PHP server and session variables:
+ print_r($_SER锟紼R)
+
+ and:
+ print_r($_SESSION)
+
+and use them to check that the document_root and script_filename values are
+what you expect. If not, you may need to adjust your config file values for base_url
+and server You can also see if there is an [HTTP_COOKIE]value set, which will show
+you if your session class is enabled and working.
+ .
+
+ 3. Check what CI has loaded into its superobject by using PHP's methods:
+ get_declared_classes();
+
+ and:
+ get_class_methods();
+
+ [ 175 ]
+
+
+Production Versions, Updates, and Big Decisions
+
+ 4. CI's own show_error() function is only a means of formatting error reports
+ that you generate. So including the following line in your code, say at some
+ branch that should not be reached:
+ show_error('test of error function');
+
+would result in your screen showing:
+
+
+
+
+I don't find that very useful. What I want is a system that will give me full, helpful
+error reports, when and where I want them, but won't show them when I don't want
+them to appear. I wrote my own function, which reads:
+ 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);
+
+ [ 176 ]
+
+
+ Chapter 12
+
+ fclose($fp);
+ }
+ }
+
+This lives in a library called errors. I have to remember to load the library, and then,
+whenever I have a section of code that I'm not confident about, I include the function:
+ $this->errors->reportme(__FILE__,__LINE__,'if the code has reached
+ here it is because......');
+
+I can then set the reportme() function to give me a report on the screen, or in my
+log file.
+
+There are several advantages to a simple method like this. Firstly, I can alter the
+reportme() function easily, to make it write errors to a file, or to do nothing at all: so
+I can make all my reports disappear from the screen at once, or come back again, by
+changing one line of code.
+
+Secondly, let's say I am expecting a variable to have a particular value. (An ID
+number to be an integer, say.) I make the message as complete and helpful as
+possible. I try to say what I expected to find (an integer), as well as including the
+value I actually got. The function call also uses PHP's __FILE__ and __LINE__ 'magic
+constants' to tell me exactly where it happened.
+
+So, if this particular piece of code becomes a problem when I transfer it to another
+server, possibly some time after I wrote it, I can immediately find the code, and the
+text helps me to remember why it is a problem. Six months after you write code, you
+can't just pick it up straight away, especially if it is late at night and a client is on the
+phone asking for an explanation! The more helpful the error text, the easier it is to
+respond sensibly.
+
+Thirdly, if the integrity of the site was really critical, I could set the function to
+email me with error reports. That might result in a very full mailbox during the
+development phase, but once the site is stable and in use, it might be very useful
+to have an immediate warning if the site is experiencing problems. You will know
+about them before your users tell you.
+
+
+Coping with Change in New CI Versions
+Between 28 February 2006 and 30 October 2006, CI went from its first beta to version
+1.5. That's a pretty impressive rate of development.
+
+
+
+
+ [ 177 ]
+
+
+Production Versions, Updates, and Big Decisions
+
+During that time Rick Ellis made several fairly radical changes, particularly to the
+structure of the site. For the most part, he has been careful to make them backwardly
+compatible鈥攂ut not all of them are. If you are new to CI and have downloaded the
+latest version, you can skip this section. But if you wrote programmes using earlier
+versions, you may need to check these changes. You may also need to check if you
+are using CI libraries or plug-ins written by other people.
+
+Rick has grappled with two main problems:
+
+
+How to Load Models, and What to Call Them
+At first, there were no models, just folders for scripts and libraries. There was no
+provision to initialize them automatically as part of the CI 'super-object'. As a result,
+you had an MVC system without 'model' files, which seems confusing.
+
+As well as this, there are two libraries folders: /system/application/libraries
+holds any files you write for yourself, while /system/libraries holds the system's
+own operating files. This may have confused a few people: the two are quite
+different! You ought to be adding to or altering the former; you probably don't need
+to alter the latter. (And if you do, you run serious risks of incompatibility if you
+upgrade to a later CI version: see below.)
+
+With version 1.3 came a new 'model' class. The User Guide defines models as, "PHP
+classes that are designed to work with information in your database". When first
+introduced, CI models connected automatically to the database. However, since
+Version 1.3.3, you must specifically load the database from inside the model or the
+controller that calls it.
+
+Or, when you call the model from the controller, you can do so in this format:
+ $this->load->model('Mymodel', '', TRUE);
+
+and then the 'TRUE' loads the model with the default database connection made, as
+defined in your config file. (The second parameter, left blank here, is an optional alias
+for the model.)
+
+CI will probably still work if you put the functionality of a 'model' (in the MVC
+sense) into a 'library' or a (deprecated) 'script', as you had to in the early days
+when there were no 'models' folders: but you'll have to access the CI resources
+differently鈥攕ee the next section!
+
+
+
+
+ [ 178 ]
+
+
+ Chapter 12
+
+How to Initialize Your Own 'library' Classes
+Originally, you couldn't make your own classes part of the CI 'super-object'. This
+was a problem, because it meant that your library code couldn't, for instance, access
+the database through Active Record, or use other CI libraries, and that became
+pretty limiting.
+
+Version 1.2 added the get_instance() function that allows you to access the
+'super-object'. (See Chapter 7.) You could include it in your 'library' or 'script' and
+then use the CI resources. (Unless your new file was a functional script rather than
+an OO class, of course. However, script files are probably best used for simple
+low-level functions.)
+
+Version 1.4 introduced a new system. You had to create two files for each 'library'
+class. The first was the class itself, say Newclass.php, stored in the application/
+libraries folder, and the second, stored in an application/init folder, had
+to be called init_newclass.php and contained a few standard lines of code that
+initialized it as part of the 'super-object'. However, you still had to use the
+get_instance() function to access CI resources.
+
+In version 1.5, the init folder has been deprecated, and initialization happens
+automatically. You now only need the one file for each 'library' class.
+
+The old scripts folder has also been deprecated. 'Deprecate' in this context, usually
+means that the thing concerned is till recognized and should still work, but that the
+developer offers no guarantee that it will do so in all future versions. In other words,
+don't panic if you still have scripts in a system/application/scripts folder鈥攂ut
+don't write any more.
+
+If you are planning to use libraries or plug-ins written by the CI community, please
+check first that they are up to date with the latest CI version. There are quite a few
+around still that were written for 1.4.1 and have separate 'init' files. Updating them
+isn't difficult, but it does take some care to get it right.
+
+
+
+So Should I Update If a New CI Version
+Comes Out?
+New versions of CI come out from time to time. They come with comprehensive
+instructions for updating. Usually, this involves copying a new set of files to your
+system folder. Sometimes, you need to change config files, or your index.php file,
+as well, but none of these are major changes and none of them are rocket science.
+Because the folder structure keeps your application files in their own place, it's
+usually easy to update the system without touching the applications.
+
+ [ 179 ]
+
+
+Production Versions, Updates, and Big Decisions
+
+But, say you've written your killer app in version 1.5. It's uploaded to your
+production system and working fine. Then, Rick Ellis brings out CI version 1.6 (or 2.8
+or whatever鈥). It has interesting new features, and some bug fixes. Do you
+upgrade to it?
+
+I would say, 'Yes', if it's a minor upgrade, say between 1.5.2 and 1.5.3. But if it's a
+major version change, and your existing system is working, leave well alone. You
+can tell the difference partly from the numbering, but also from the 'change log'
+published with each upgrade when it comes out. The sort of changes that have been
+made in CI over the last year fall into three categories:
+
+Bug fixes: There are surprisingly few of these鈥擟I is excellent code, and most of the
+base classes have been well tested by hundreds if not thousands of users.
+
+New features:. These appear regularly, but if you managed to build your application
+without them, will they really be helpful now?
+
+Subtle changes: As I've described, CI has gone through a process of internal
+evolution, and it may well continue to do so. As you can see from the following
+table, some of these might be backwardly compatible, or they might require fairly
+major re-writes of your code.
+
+Some changes between versions of CI:
+
+ Version Change Log
+ 1.2 Added a global function named get_instance() allowing the main
+ CodeIgniter object to be accessible throughout your own classes.
+ 1.3 Added support for Models.
+ 1.3 Added the ability to pass your own initialization parameters to your
+ custom core libraries when using $this->load->library().
+ 1.3 Added better class and function name-spacing to avoid collisions
+ with user developed classes. All CodeIgniter classes are now prefixed
+ with CI_ and all controller methods are prefixed with _ci to avoid
+ controller collisions.
+ 1.3.3 Models do not connect automatically to the database as of this version.
+ 1.4 Added the ability to replace core system classes with your own classes.
+ 1.4 Updated the Models loader function to allow multiple loads of the
+ same model.
+ 1.4.1 Updated plugins, helpers, and language classes to allow your
+ application folder to contain its own plugins, helpers, and language
+ folders. Previously, they were always treated as global for your
+ entire installation. If your application folder contains any of these
+ resources they will be used instead the global ones.
+
+
+ [ 180 ]
+
+
+ Chapter 12
+
+ Version Change Log
+ 1.4.1 Deprecated the application/scripts folder. It will continue to
+ work for legacy users, but it is recommended that you create your own
+ libraries or models instead. It was originally added before CI had user
+ libraries or models, but it's not needed anymore.
+ 1.5 Added the ability to extend libraries and extend core classes, in addition
+ to being able to replace them.
+ 1.5 Deprecated init folder. Initialization happens automatically now.
+
+Don't misunderstand me. All of these are sensible changes and all of them are
+improvements. If you are starting a new project, start with the latest CI version. But
+if you wrote code using version 1.3, say, you will find that your scripts folder is
+deprecated and your models don't automatically connect to the database any more.
+Personally, I would leave that code running on version 1.3 of CI, rather than try to
+upgrade it. Life's too short.
+
+
+
+How to Add On to CI's Basic Classes
+Normal users are unlikely to need to alter the base CI classes. It's a pretty good
+framework, it does a lot of things, and after all, the point of a framework is to make
+thing easy, right? However, if you must 鈥.
+
+CI is open source, and you can see all the code as soon as you download it. This
+includes the basic libraries that make CI work (stored in system/libraries) as well
+as the ones you wrote in system/application/libraries.) So it has always been
+possible to change CI any way you like.
+
+Changing system library files has two problems, however:
+
+ There's no guarantee that your new code will be compatible with the rest of
+ 鈥
+ CI, or with updated versions. This may lead to subtle or strange errors that
+ won't be easy to track down.
+ If you later update your CI version, the system folder is likely to be changed.
+ 鈥
+ The library file you altered may well be over-written and updated, so you'd
+ have to go through your changes and transfer them to the updated version.
+
+However, since version 1.5, there are now two sensible 'work-arounds' for tinkering
+with the CI library classes (except for the underlying 'database' and 'controller'
+classes, which you touch at your peril.)
+
+
+
+
+ [ 181 ]
+
+
+Production Versions, Updates, and Big Decisions
+
+ Firstly, you can create a file with the same name as any of the system base
+ 鈥
+ classes in your /system/application folder. The system then uses this one,
+ in preference to the standard one in the /system folder. This requires exact
+ naming conventions鈥攕ee the online User Guide. It also requires you to copy
+ all the functionality in the existing class as well as your own additions
+ or changes.
+ Secondly, and more conveniently, you can create a new class that extends the
+ 鈥
+ system class. (So it's perhaps best referred to as a 'sub-class'.) Again, there are
+ naming conventions鈥攕ee the online User Guide. Extending the underlying
+ system class means that your new sub-class inherits all the resources of the
+ underlying CI class, but adds a few extra methods of your own. This should
+ mean that, if you update your CI version, the underlying CI class will be
+ replaced, but your new sub-class (which you should put in the system/
+ application folder) will be left untouched.
+However, neither of these methods will guarantee that your code is (or remains)
+compatible with the rest of CI.
+
+Looking through the CI online forums, there are various suggestions for extending
+the Validation, Unit Testing, and Session classes. Unit Testing, for example, only has
+two functions and a limited number of comparisons. Perhaps you want a function to
+show up errors in red, so they stand out when the test results are returned?
+
+If you wanted to make extensive use of some other testing function, it would be
+simpler to add it in via a sub-class, extending Unit Testing, than to write it out in the
+controller each time you called Unit Testing.
+
+If you wanted to do this, you'd start your new sub-class this way:
+ class MY_Unit_test extends CI_Unit-test {
+ function My_Unit_test()
+ {
+ parent::CI_Unit_test();
+ }
+
+ function newfunction()
+ {
+ //new code here!
+ }
+ }
+
+
+
+
+ [ 182 ]
+
+
+ Chapter 12
+
+Notice three things here:
+
+ The name of the underlying unit testing class is CI_Unit_test, even though
+ 鈥
+ the filename of the class code is system/libraries.unit_test.
+ If you need to use a constructor in your sub-class, make sure you extend the
+ 鈥
+ parent constructor first, as here.
+
+ Your new sub-class name should be prefixed with MY_, and saved as
+ 鈥
+ application/libraries/MY_unit_test.php. (Unlike the main classes,
+ where the CI_ prefix is part of the class name but not of the filename, here
+ the MY_ prefix is part of both.)
+
+Once you've created your sub-class, you load it like this:
+ $this->load->library('unit_test');
+
+In other words, exactly the same as before you wrote the sub-class; and you call a
+function in the same way too, except that this time you can call not only the existing
+unit test functions, but also any new ones you've written yourself:
+ $this->unit_test->newfunction();
+
+When you next update your CI installation, the unit test library in the system folder
+will be overwritten, but the one in the application folder won't, so your code will
+still be there. Of course, you'll need to check that the updated system library is still
+compatible with your own code.
+
+
+
+Summary
+In this chapter, we've seen some of things that can go wrong when you try to transfer
+your system from a local server to a remote one. This may involve:
+
+ A different version of PHP or MySQL
+ 鈥
+ A different operating system
+ 鈥
+
+In particular, we've looked at case sensitivity, PHP differences, and MySQL issues.
+We've also looked at diagnostic tools.
+
+Then we looked at the CI's updates. These have all been major improvements, but
+my advice is, if you have a system working on the current CI version and a new one
+comes out, think carefully before you upgrade.
+
+Lastly, we looked at the pros and cons of adding to CI's basic classes. Most users
+won't need to do this, but if you want to, I strongly suggest that the best way to do it
+is to sub-class an existing library class.
+
+ [ 183 ]
+
+
+
+
+ Instant CRUD鈥攐r Putting it
+ All Together
+The most essential鈥攁nd the most boring鈥攑art of writing any dynamic site is the
+CRUD. You have one or more database tables; you need to be able to Create, Read,
+Update, and Delete entries on each of these. Later on, you'll do clever things with the
+data, but until there is some user-friendly way to put it there and maintain it, your
+site isn't viable.
+
+But this involves writing CRUD functions and these, though conceptually quite easy,
+are fairly complex and time-consuming. So for our site, I've written a generalized
+CRUD model, using CI's classes and helpers to make it easier. In this chapter, you'll
+see how this model works and how to integrate it into our application.
+
+The CRUD model does more than just CRUD. It validates user-entered data, and also
+checks it in several ways鈥攅.g., to see that you don't do a 'delete' operation without
+limiting it to a specific table row, or that you don't accidentally repeat 'create'
+operations by going back to the entry form and reloading it in your browser.
+
+Lastly, the CRUD model contains its own self-testing framework, so that you can
+perform development tests as you build or adapt your code.
+
+There are other CRUD frameworks out there, which may be more comprehensive
+and possibly better code (see Chapter 15). However, this one does work. And it's a
+good way of summing up and using many of the lessons that we've learned in the
+previous chapters.
+
+This chapter sets out the code for the model, with some comments:
+
+ First of all, we look at the design philosophy.
+ 鈥
+ Then we look at a standard controller to use with the model.
+ 鈥
+ Then we look at the way database tables must be structured.
+ 鈥
+
+
+Instant CRUD鈥攐r Putting it All Together
+
+ Then we'll look at the model itself: firstly, the array that holds information
+ 鈥
+ about the database, and then at the separate functions.
+ Lastly, we'll look at the self-test functions.
+ 鈥
+
+
+
+The CRUD Model: Design Philosophy
+The idea behind this CRUD model is that it can be called by any controller for any
+table. The data about that table, and how you want its update form displayed is held
+once, in an array. Everything else is standard: the controller just identifies itself (and
+thereby the table it acts on) and if necessary, gives an ID number for a record. So all
+you have to do is write a few simple controllers, and all the work of setting out forms
+and making database connections is done for you.
+
+Remember that the user can't talk to a model directly, so (s)he has to go through a
+controller every time. You could put all the code in the controller, but then you'd
+have to copy it all for each new controller. This way, there is only one set of CRUD
+code in the model, so only one set to update and maintain. The price is that you
+have to keep passing information backwards and forwards between controllers and
+model, which makes the code slightly more difficult to follow.
+
+For the sake of simplicity, I've used two external functions that aren't defined in
+this code:
+
+ failure(), which reports errors; however, I want this done.
+ 鈥
+ A model called display鈥攖his creates menus and sets up base URLs and
+ 鈥
+ so on. So all the CRUD functions build up a pile of data, put it in the $data
+ variable, and simply call:
+ $this->display->mainpage($data);
+
+I also want the CRUD model to be able to test itself, so it includes a self-test suite.
+Calling this during the design process allows me to check that the model will behave
+as I want it to, under any conditions I can think of. (It's surprising how writing the
+test suite makes you realize new things that can go wrong鈥攂ut better now than
+when your clients use the site.)
+
+Please bear in mind that every model like this is a compromise. The more you ask of
+it, the more it asks of you. For instance, this model won't work unless your database
+tables are laid out in specific ways. It will lay out forms in quite sophisticated ways,
+but it is not infinitely flexible. It doesn't include JavaScript for better control of the
+user's experience. It can't handle exceptions to its own rules. On the other hand,
+provided you only want to do a set of standard things (that are pretty common), it
+makes life a lot easier.
+
+
+ [ 186 ]
+
+
+ Chapter 13
+
+
+The Standard Controller Format
+Firstly, for each database table, you need a standard controller. This is how the user
+will interact with your table鈥攅.g., to add a new site, change the details of an existing
+one, etc. To add a new person, the user will interact with the people table, so we
+need a different controller: but it is almost the same as the sites controller.
+
+This is the controller for our sites table:
+ load->model('crud');
+ }
+ /*function to update an entry (if an ID is sent) or to insert a new
+ one. Also includes validation, courtesy of CI */
+ function insert($id)
+ {
+ $this->crud->insert($this->controller, $id);
+ }
+ /*interim function to pass post data from an update or insert through
+ to Crud model, which can't receive it directly*/
+ function interim()
+ {
+ $this->crud->insert2($this->controller, $_POST);
+ }
+ /*function to delete an entry, needs table name and id. If called
+ directly, needs parameters passed to function; if not, from Post
+ array*/
+ function delete($idno=0, $state='no')
+ {
+ if(isset($_POST['id'])&& $_POST['id'] > 0)
+ {$idno = $_POST['id'];}
+ if(isset($_POST['submit']))
+ {$state = $_POST['submit'];}
+
+ [ 187 ]
+
+
+Instant CRUD鈥攐r Putting it All Together
+
+ $this->crud->delete($this->controller, $idno, $state);
+ }
+ /*function to show all entries for a table*/
+ function showall()
+ {
+ $this->crud->showall($this->controller, $message);
+ }
+
+ /*function to show all data in a table, but doesn't allow any
+ alterations*/
+ function read()
+ {
+ $this->crud->read($this->controller);
+ }
+ /*function to set off the test suite on the 'crud' model. This
+ function need only appear in one controller, as these tests are made
+ on a temporary test table so that your real data is not affected*/
+ function test()
+ {
+ $this->crud->test();
+ }
+ }
+ ?>
+
+As you see, this is pretty lean and completely generalized. If you wanted to make it
+the people controller instead of the sites controller鈥攊n other words, to allow you
+to create, read, update or delete entries in the people table, all you need to do is:
+
+ Change the class name from Sites to People (upper-case initial letter!).
+ 鈥
+ Change the $controller variable from sites to people (lower case).
+ 鈥
+ Change the constructor function name from Sites to People (upper case
+ 鈥
+ initial letter).
+ Save the new controller as:
+ 鈥
+ system/application/controllers/people.php.
+
+The name of the controller must be exactly the same as the name of the database
+table to which it relates鈥攕o for the people table, it must be people. The name must
+have an uppercase first letter in the class definition line and the constructor function,
+but nowhere else.
+
+
+
+
+ [ 188 ]
+
+
+ Chapter 13
+
+
+The Database Tables
+There are three simple rules for your database tables:
+
+ 1. The main ID field in each table must always be called 'id' and must be
+ an auto-incrementing field. (This is a standard MySQL field type. It
+ automatically creates a new, unique number each time you make a
+ new entry.)
+ 2. There must also be a field called 'name' in every table, if you want to use it as
+ the basis for a dynamic drop-down box on any form.
+ 3. You also need a 'submit' field for holding states, and things like that.
+
+Otherwise you can have whatever fields you like, and name them anything your
+database system is happy with. Everything else is handled by the CRUD model, for
+any controller锟絫able pair designed along these lines.
+
+
+
+The Heart of the Model: the Array
+Preliminaries over. Let's start on the CRUD model.
+
+First, you need to define the CRUD model and a constructor function. Standard stuff
+by now:
+ load->helper('form');
+ $this->load->helper('url');
+ $this->load->library('errors');
+ $this->load->library('validation');
+ $this->load->database();
+ $this->load->model('display');
+
+Save it as system/application/models/crud.php.
+
+
+ [ 189 ]
+
+
+Instant CRUD鈥攐r Putting it All Together
+
+Then comes the boring bit, but you only have to do it once. You need to write a
+multi-dimensional array. (I started to learn PHP from a book鈥攊t had better be
+nameless鈥攚hich said 'multi-dimensional arrays aren't encountered very often,
+so we won't go into them in further detail here'. I seem to have been using them
+ever since.)
+
+The first dimension of our array is the list of tables. (sites, people, etc.)
+
+The second dimension is the list of fields within each table. For the sites tables,
+these are id, name, url, etc.)
+
+The third dimension describes each field and provides a set of parameters that
+control how it will be handled by the insert锟絬pdate form. These are:
+
+ The text you want the user to see on the insert form: how this field is
+ 鈥
+ described to a human, rather than its actual name. (So the first one is
+ ID number of this site rather than just id.) This is to help you to make your
+ forms user-friendly.
+ The type of form object you want to use to display this field on your insert/
+ 鈥
+ update form: this might be an input box, or a text area, or a drop-down box.
+ (This CRUD model covers some but not all of the options.)
+ Any CI validation rules you want to impose when the user fills out this form.
+ 鈥
+ This can be left blank.
+ If you want to display this field as a dynamic drop-down box, the name
+ 鈥
+ of the table it will draw on. See below for explanation. This can also be
+ left blank.
+
+We've already declared the array as a class variable $form, so ever afterwards we
+have to refer to it as $this->form). It is defined inside the constructor, that is, it
+follows on immediately from the previous code.
+ $this->form =
+ array
+ ('sites' => array
+ (
+ 'id' => array('ID number of this site',
+ 'readonly', 'numeric'),
+ 'name' => array('Name of site', 'textarea',
+ 'alpha_numeric'),
+ 'url' => array('Qualified URL,
+ eg http://www.example.com', 'input', ''),
+ 'un' => array('username to log in to site',
+ 'input', 'numeric|xss_clean'),
+ 'pw' => array('password for site', 'input',
+ 'xss_clean'),
+
+ [ 190 ]
+
+
+ Chapter 13
+
+ 'client1' => array('Main client',
+ 'dropdown', '', 'people' ),
+ 'client2' => array('Second client', 'dropdown',
+ '', 'people'),
+ 'admin1' => array('First admin', 'dropdown',
+ '', 'people'),
+ 'admin2' => array( 'Second Admin', 'dropdown',
+ '', 'people'),
+ 'domainid' => array('Domain name', 'dropdown',
+ 'numeric', 'domains'),
+ 'hostid' => array( 'Host', 'dropdown',
+ 'numeric', 'hosts'),
+ 'submit' => array( 'Enter details', 'submit', 'mumeric')
+ ),
+ 'domains' => array
+ (
+ 'id' => array('ID number of this domain',
+ 'hidden', 'numeric'),
+ //etc etc etc!!
+
+You can see that, within the $form array, there are sub-arrays for each table (here,
+sites and domains, though I only just started the latter, for space reasons) which
+each contain their own sub-sub-arrays, one for each field ('id', 'name', etc). Each of
+these sub-sub-arrays is in turn an array that contains the three or four values we
+described above.
+
+It can be fiddly to get the array syntax right, but conceptually it is simple.
+
+For the complete set of tables in our application, this array takes about 120 lines to
+specify. But you only have to do it once! This is the heart of your model. End the
+constructor function with a closing bracket '}', and go on to the other functions in the
+CRUD model.
+
+If you ever need to change your database tables (add a new field, for instance) or you
+want to change your validation rules, then you need only change the values in this
+array. Everything else will change automatically: for instance, next time you try to
+add a new entry, you should see the entry form reflecting the change.
+
+
+
+
+ [ 191 ]
+
+
+Instant CRUD鈥攐r Putting it All Together
+
+
+Function by Function: the CRUD Model
+The various functions that make up the CRUD model are as follows:
+
+
+Showall
+This is the function that the user will go to most often. It acts as an entry point for
+all the other operations鈥攎ake a new entry, update, or delete an entry. It shows you
+what is already in the table. With some test data in the sites table, it looks like this:
+
+
+
+
+As you can see, from this screen you can update the entry for a site, or delete it. You
+can also make a new entry, or read all the data in the table.
+
+By the way, please bear in mind that this model doesn't include any security
+provisions. In a real site, you might want to fine-tune users' options鈥攅.g., allow them
+to update but not delete. And you would want to make sure that hackers couldn't
+access the functions of the CRUD model by typing in URLs like www.example.com/
+index.php/sites/delete/18. CI's URL-based structure makes it relatively easy to
+deduce how the system accesses these commands, so you might want to ensure that
+the user has to be logged in to the site before the CRUD model will activate at all.
+
+ [ 192 ]
+
+
+ Chapter 13
+
+Right, back to the CRUD mechanisms. Remember that humans can't call the model
+directly. In each case the option (to delete, update, etc.) is exercised via a call to
+the controller. Just to jump back, the sites controller that called up the showall
+function did so with this line of code:
+ $this->crud->showall($this->controller);
+
+in other words, it substituted sites for $this->controller, and passed that value
+as a parameter to the CRUD function, to tell it which controller it was acting for.
+
+Let's now look at the showall function. It has been passed its first parameter, sites.
+We'll leave $message until later. Concentrate on the highlighted lines.
+ /*this function lists all the entries in a database table on one
+ page. Note that every db table must have an 'id' field and a 'name'
+ field to display!
+ This page is a jumping-off point for the other functions - ie to
+ create, read, update or delete an entry.
+ When you've done any of these, you are returned to this page. It has a
+ 'message' parameter, so you can return with a message - either success
+ or failure.*/
+
+ function showall($controller='', $message = '', $test ='no')
+ {
+ $result = '';
+ $mysess = $this->session->userdata('session_id');
+ $mystat = $this->session->userdata('status');
+ if(!$this->db->table_exists($controller))
+ {
+ $place = __FILE__.__LINE__;
+ $outcome = "exception:$place:looking for table
+ $controller: it doesn't exist'";
+ /*test block: what if there is no controller by that name?*/
+ if($test =='yes')
+ {
+ return $outcome;
+ }
+ else{
+ $this->failure($outcome, 'sites');
+ }
+ }
+ /*end test block*/
+ $this->db->select('id, name');
+ $query = $this->db->get($controller);
+ if ($query->num_rows() > 0)
+ {
+ $result .= "
";
+
+
+ [ 193 ]
+
+
+Instant CRUD鈥攐r Putting it All Together
+
+ $result .= "
";
+ $data['text'] = $result;
+ $this->display->mainpage($data, $this->status);
+ }
+ else
+ {$place = __FILE__.__LINE__;
+ $outcome = "exception: $place:
+ no results from table $controller";
+ /*test block: were there results from this table/ controller?*/
+ if($test == 'yes')
+ {$place = __FILE__.__LINE__;
+ return $outcome;
+ }
+ /*end test block*/
+ else{
+ $message = "No data in the $controller table";
+ /*note: this specific exception must return to another controller
+ which you know does contain data鈥︹ otherwise, it causes an infinite
+ loop! */
+ $this->failure($message, 'sites');
+ }
+ }
+ }
+
+
+ [ 194 ]
+
+
+ Chapter 13
+
+It sets up a table, displaying some data (id and name) about each entry. Each entry
+line also gives you the option to update or delete the entry: this is achieved using
+CI's anchor function to create hyperlinks to the appropriate functions in the
+appropriate controller.
+
+There's also a single line that offers you the opportunity to create a new site, again
+by offering a hyperlink to the controller's insert function. (Note: I've called the
+insert function both for making new entries and updating old ones. This is because
+the model assumes that if insert is called with an ID number, it is to update the
+corresponding entry. If it's called without an ID number, it creates a new entry.)
+
+A lot of the code is taken up with exception handling: what if the table doesn't
+exist, what if the query returns no information? Exceptions are passed to the failure
+function. There are also two test blocks to allow me to run self-tests.
+
+In addition, there's a line that allows you to read (but not alter) all the data in the
+tables. Let's look at the read function first, as it's the simplest.
+
+
+Reading the Data
+I've used CI's HTML Table (see Chapter 10) and Active Record classes (see
+Chapter 4) to show just how simple this piece of functionality is. I want a simple
+formatted page that shows all the data in the database in an HTML table. It doesn't
+allow any changes: it is literally the 'read' page.
+
+First there has to be a function in the controller to call the model and tell the model
+which controller锟絫able is to be displayed. That's the read() function in the standard
+controller.
+
+It calls the following function in the CRUD model:
+ /*queries the table to show all data, and formats it as an HTML
+ table.*/
+ function read($controller)
+ {
+ $this->load->library('table');
+ $tmpl = array (
+ 'table_open' => '
',
+ 'row_alt_start' => '
',
+ );
+ $this->table->set_template($tmpl);
+ $this->load->database();
+ $this->load->library('table');
+ $query = $this->db->get($controller);
+
+ [ 195 ]
+
+
+Instant CRUD鈥攐r Putting it All Together
+
+ $result = $this->table->generate($query);
+ $data['text'] = $result;
+ $this->display->mainpage($data);
+ }
+
+The two highlighted lines do all the work of querying the database and formatting
+the results.
+
+I've used the mainpage function in the display class to provide formatting for the
+page: the read function here just builds the data and hands it on as part of an array.
+
+The result is a page full of data from the test file:
+
+
+
+
+Let's just remind ourselves how control passes between the controller, the CRUD
+model, and other parts of the program.
+
+
+ user clicks
+ Controller
+ 鈥榬ead鈥
+ 鈥榬ead鈥
+ hyperlink
+ CRUD model
+ function
+ read function
+ calls model
+ generates table
+
+
+
+
+ View Display
+ generates Model
+ HTML generates
+ page for view
+ user
+
+
+
+Delete and Trydelete
+Deleting is the most permanent operation! For this reason our delete function checks
+to make sure of two things:
+
+
+
+ [ 196 ]
+
+
+ Chapter 13
+
+ 1. That a state variable of 'yes' has been set in the 'submit' field: if not, it passes
+ the request to a trydelete function. That asks the user if she or he really
+ does want to do a delete. If she or he confirms, the trydelete function sets a
+ state variable of 'yes' and sends the request back to the delete function, which
+ now accepts the delete instruction.
+ 2. Before doing the delete query, it checks that an ID number has been set
+ (otherwise all the entries might be deleted). Then, it uses CI's Active Record
+ to do the delete and to check that one line has indeed been removed from
+ the table. If one line was removed, then it returns to the showall function.
+ You'll notice that it passes back two parameters鈥攖he controller name, and a
+ message reporting that the deletion has been successfully done. (This is the
+ second parameter to showall. If it is set, it appears in a red box at the top of
+ the table, letting the user know what is going on.)
+
+First, here's the delete function. You'll notice this code is also complicated by a lot of
+'test block' lines. Ignore these for now: just follow the highlighted code..
+ /*DELETE FUNCTION: given table name and id number, deletes an entry*/
+ function delete($controller, $idno, $state='no', $test='no')
+ {
+ /*first check that the 'yes' flag is set. If not, go through the
+ trydelete function to give them a chance to change their minds*/
+ if(!isset($state) || $state != 'yes')
+ {
+ /*test block: are 'yes' flags recognised?*/
+ if($test == 'yes')
+ {
+ $place = __FILE__.__LINE__;
+ $outcome = "exception at $place: sent state
+ value $state to trydelete function ";
+ return $outcome;
+ }
+ else
+ /*end test block*/
+ {$this->trydelete($controller, $idno, 'no');}
+ }
+ else{
+ /*'yes' flag is set, so now make sure there is an id number*/
+ if(isset($idno) && $idno > 0 && is_int($idno))
+ /*test block: with this id no, am I going to do a delete?*/
+ {
+ if($test == 'yes')
+ {
+ $place = __FILE__.__LINE__;
+ $outcome = "OK at $place:
+ doing delete on id of $idno ";
+
+ [ 197 ]
+
+
+Instant CRUD鈥攐r Putting it All Together
+
+ return $outcome;
+ }
+ else{
+ /*end test block*/
+ /*if there is an id number, do the delete*/
+ $this->db->where('id', $idno);
+ $this->db->delete($controller);
+ $changes = $this->db->affected_rows();
+ }
+ if($changes != 1)
+ {
+ /*test block: did I actually do a delete? */
+ $place = __FILE__.__LINE__;
+ $outcome = "exception at $place: cdnt do delete
+ op on $controller with id no of $idno";
+ if($test == 'yes')
+ {return $outcome;}
+ else
+ /*end test block*/
+ /*if there was no update, report it*/
+ {$this->failure($outcome);}
+ }
+ else{
+ /*test block: I did do a delete*/
+ if($test == 'yes')
+ {return 'OK';}
+ else{
+ /*end test block: report the delete*/
+ $this->showall($controller,
+ "Entry no. $idno deleted.");}
+ }
+ }
+ else
+ /*test block: report id number wasn't acceptable'*/
+ {
+ $place = __FILE__.__LINE__;
+ $outcome = "exception at: $place : id no of $idno set
+ for delete op in $controller, expecting integer";
+ if($test == 'yes')
+ {return $outcome;}
+ else
+ /*endtest block: if I failed, report me*/
+ {$this->failure($outcome);}
+ }
+ }
+ }
+
+ [ 198 ]
+
+
+ Chapter 13
+
+I promised to explain the $message parameter we used when we call showall. You
+can see it here: if this function is successful, it returns to the showall page, by calling
+it with an appropriate message:
+ $this->showall($controller, "Entry no. $idno deleted.");}
+
+It's important not only that the action is done, but that the user knows it has been.
+
+Now, back to preventing accidental deletions. If the delete function wasn't
+called with the state=yes parameter, it reroutes the request to the trydelete
+function鈥攖he 'second chance'. Actually, only the trydelete function will ever set
+this parameter to yes, so the delete form will always present an are you sure option
+to the user.
+
+Let's look at the trydelete function. It creates a simple form, which looks like this:
+
+
+
+
+Clicking on yes re-calls the delete function. (Notice again that the form can't return
+directly to crud/delete, because a form can't point to a model. It has to point to the
+sites/delete function in the controller, which simply passes everything straight on
+to the crud/delete function in the model again.)
+
+The subtle change is that, if the user confirms the delete, the trydelete form adds
+(as a hidden field) the submit=yes parameter, which goes into the post array, and
+is returned to the controller's delete function. The controller's delete function reads
+the submit=yes parameter from the post array, and puts together a call to the
+crud/ delete function, which this time includes state=yes as a parameter, so the
+delete function moves on to the next step.
+
+If the user doesn't want to do the delete, she or he clicks on the hyperlink created by
+the CI anchor function, and is passed back to the showall function, which is most
+probably where she or he came from.
+
+Here's the code that does all this:
+ /*TRYDELETE FUNCION: interrupts deletes with an 'are you sure?
+ screen'*/
+ function trydelete($controller, $idno, $submit = 'no')
+ {
+ if($state == 'yes')
+ {$this->delete($controller, $idno, 'yes');}
+
+ [ 199 ]
+
+
+Instant CRUD鈥攐r Putting it All Together
+
+ else{
+ $result .= "
";
+ $result .= anchor("$controller/showall",
+ "No, don't delete");
+ $data['text'] = $result;
+ $this->display->mainpage($data);
+ }
+ }
+
+Just for clarity, here's a diagram of how control passes during a delete operation.
+
+
+ user clicks Controller
+ 鈥榙elete鈥 鈥榙elete鈥
+ CRUD model
+ hyperlink function
+ delete function
+ calls model
+ checks for
+ permission to
+ delete (i.e. submit
+ field is 鈥榶es鈥)
+ user receives View generates
+ trydelete view. appropriate HTML
+ If user confirms page for user
+ 鈥榙elete鈥 action,
+ submit field is No permission:
+ set to 鈥榶es鈥 calls trydelete
+ function
+
+ user receives
+ Display Model
+ showall view: Permission:
+ generates either
+ confirmation deletes entry,
+ 鈥榯rydelete鈥 or
+ of deletion calls showall
+ 鈥榮howall鈥 view
+ function
+
+
+
+As you can see, this is quite complex, more so than our previous example. The
+model is doing all the work, but the user can only talk to the controller, so if you
+need to go back and re-present the question to the user, you need to involve the
+controller again.
+
+ [ 200 ]
+
+
+ Chapter 13
+
+However, once you've sorted it out, it works well and is highly logical. CI imposes
+this framework on you, but in the long run that's an advantage. Your code is
+consistent, and modular. Note how the same display model and the same view is
+invoked each time: what they show the user depends on the CRUD model function
+that called them.
+
+
+Insert
+This is the most complex function, because it generates a form for users to fill out.
+(Interface with humans is always the most difficult thing鈥)
+
+Rather than write two separate functions, one to insert and one to update, and have
+to build the form twice, I've written one function that does duty for both. If you
+supply a valid ID number, it updates the corresponding record; if not, it inserts a
+new entry.
+
+To make this easier to follow, I haven't included the test blocks that we saw in the
+delete function.
+
+This is where we use the array we defined at the beginning of this chapter. The
+function sets up a form, using CI's form helper, and based on the type of form
+element we specified in the array (dropdown, textarea, etc.). At the heart of the
+function is a switch statement, which accomplishes this.
+
+The code uses CI's validation class to help us check the incoming data: remember we
+set the validation rules in our initial array.
+ /*the most complex function. This creates an HTML form, based on the
+ description of the fields in the form array. This is sent to our
+ display model, which sets up a view and shows it to the user.
+ The view then sends a POST array back to the controller. The form
+ can't call this model directly, so it has to call the controller,
+ which refers it back to the model.
+ Note the function parameters:
+ 1. The controller parameter is whichever controller/ table has called
+ the model - eg the 'sites' controller, or the 'domains' controller.
+ The controller has the same name as the table it manipulates.
+ 2. The optional id parameter is the id of an individual entry in that
+ table.
+ 3. The optional 'test' parameter is so you can set the form up to make
+ usable responses to self-test functions.
+ */
+ function insert($controller='', $id=0, $test='no')
+ {
+ $myform = '';
+ $myid = 0;
+
+ [ 201 ]
+
+
+Instant CRUD鈥攐r Putting it All Together
+
+ $currentvalue = array();
+ /*test if the table exists*/
+ if(!$this->db->table_exists($controller))
+ {
+ $place = __FILE__.__LINE__;
+ $outcome = "exception: $place:looking for table
+ $controller: it doesn't exist'";
+ if($test =='yes')
+ {
+ return $outcome;
+ }
+ else{
+ $this->failure($outcome, $controller);
+ }
+ }
+ else
+ {
+ if($test =='yes')
+ {
+ return 'OK';
+ }
+ }
+ /*end test block*/
+ /*next check if there is an id number. If there is, we need to get the
+ values to populate the table fields*/
+ if(isset($id) && $id > 0)
+ {$myid = $id;
+ $this->db->where('id', $id);
+ $query = $this->db->get($controller);
+ if ($query->num_rows() > 0)
+ {
+ $row = $query->row();
+ //--------------work out the values we want!
+ foreach($row as $key =>$value)
+ /*
+ first of all work out what value you want to show as the existing
+ value in each line of the form. In priority order these are:
+ 1. the last value the user entered, from the post array
+ 2. the value from the database
+ 3. nothing, if neither of these is set.
+ if we got here, the id does exist and is returning values, so get the
+ existing values into a value array. Or, if there is something in the
+ validation array, use that instead*/
+ {
+ $_POST[$key] = $this->validation->$key;
+
+
+ [ 202 ]
+
+
+ Chapter 13
+
+ if(isset($_POST[$key]))
+ {$currentvalue[$key] = $_POST[$key];}
+ else
+ {$currentvalue[$key] = $value;}
+ }
+/*test block: there was an id number, so has the program gone for an
+update? if this is not a test, of course, just do the update*/
+ if($test == 'yes')
+ {
+ $place = __FILE__.__LINE__;
+ $outcome = "exception: $place: id of $id
+returned results from $controller table so have gone for update";
+ return $outcome;
+ }
+
+/*end test block*/
+ $myform .= "
Update
+existing entry number $id
";
+ }
+/*now catch situation where this query isn't returning results. We
+could only have got here with an integer set as our ID number, so
+this probably means we are trying to delete an entry that doesn't
+exist.*/
+ else{
+ $place = __FILE__.__LINE__;
+ $outcome = "exception: $place: despite
+id of $id cant get any results from $controller table";
+ if($test == 'yes')
+/*test block: there was and ID but there were no results*/
+ {
+ return $outcome;
+ }
+/*end test block*/
+ else
+ {$this->failure($outcome, $controller);}
+ }
+ }
+/*there was no ID number, so this is a new entry*/
+ else{
+/*If the user has filled in values, and has returned here because some
+of them didn't validate, we still need to repopulate the form with
+what he entered, so he only has to alter the one that didn't validate.
+Get these from the post array*/
+ if(isset($_POST))
+ {
+ foreach($_POST as $key => $value)
+
+
+ [ 203 ]
+
+
+Instant CRUD鈥攐r Putting it All Together
+
+ {
+ if(isset($_POST[$key]))
+ {$currentvalue[$key] = $_POST[$key];}
+ }
+
+ }
+ $myform .= "
New entry
";
+
+ /*test block: there was no ID, so this is a new entry*/
+ if($test == 'yes')
+ {
+ $place = __FILE__.__LINE__;
+ $outcome = "exception: $place: id of $id
+ treated as no id, so going for new entry";
+ return $outcome;
+ }
+ /*end test block*/
+ }
+
+ /*the table exists, whether this is an update or new entry, so start
+ to build the form*/
+ $myform .= "
';
+ $myform .= $this->validation->error_string;
+
+
+ /*the rest of this function is common to inserts or update.
+ Look up in the form array which form field type you want to display,
+ and then build up the html for each different type, as well as
+ inserting the values you want it to echo.*/
+
+
+ foreach($this->form[$controller] as $key => $value)
+ {
+ /*This switch statement develops several types of HTML form field
+ based on information in the form array.
+ It doesn't yet cover checkboxes or radio or password fields. It adds
+ a 'readonly' type, which is a field that only displays a value and
+ doesn't let the user modify it*/
+ $fieldtype = $value[1];
+ $val_string = $this->validation->$key;
+ switch($value[1])
+ {
+ /*a simple input line*/
+ case 'input':
+ $data = array(
+ 'name' => $key,
+
+ [ 204 ]
+
+
+ Chapter 13
+
+ 'id' => $key,
+ 'value' => $currentvalue[$key],
+ 'maxlength' => '100',
+ 'size' => '50',
+ 'style' => 'width:50%',
+ );
+ $myform .= "
";
+ break;
+ case 'dropdown':
+/*a drop-down box. Values are dynamically generated from whichever
+table was specified in the forms array. This table must have an id
+field (which is now entered in the form) and a name field (which is
+displayed in the drop-down box).*/
+ $dropbox = array();
+ if(isset($value[3]))
+ {
+ $temptable = $value[3];
+ $this->db->select('id, name');
+ $query = $this->db->get($temptable);
+ if ($query->num_rows() > 0)
+ {
+ foreach ($query->result() as $row)
+ {
+ $dropbox[$row->id] = $row->name;
+ }
+ }
+ }
+
+
+ [ 205 ]
+
+
+Instant CRUD鈥攐r Putting it All Together
+
+ $myform .= "
";
+ break;
+ case 'hidden':
+ /*generates a hidden field*/
+ $myform .= form_hidden($key,
+ $currentvalue[$key]);
+ break;
+ case 'readonly':
+ /*generates a field the user can see, but not alter.*/
+
+ $myform .= "
";
+ /*Finally we've built our form and populated it! Now, stuff the form
+ in an array variable and send it to the model which builds up the rest
+ of the view.*/
+ $data['text'] = $myform;
+ $this->display->mainpage($data);
+ }
+
+A couple of things to explain here. All the form field types are standard, except for
+readonly鈥攚hich is a hidden form field that allows you to see, but not to alter, what
+it says. This is not secure, of course: a smart user can easily hack the value. It's just
+designed to simplify the choices the user faces.
+
+You'll notice the form points to a function called interim, on whichever controller
+called it. Again, that's because you can't address a model directly via its URL. So, if
+it was set up by the 'sites' controller, the form points to 'sites锟絠nterim' and the values
+entered by the user, or from existing data, are packed in the $_POST array and sent
+there. As you'll recall from the beginning of this chapter, that function just calls the
+crud function insert2, passing on the $_POST array to it as a parameter.
+
+
+
+
+ [ 207 ]
+
+
+Instant CRUD鈥攐r Putting it All Together
+
+Insert2
+Insert2 receives the $_POST array as a parameter and checks to see if it has an 'id'
+field set. If yes, it updates that entry. If not, it creates a new entry.
+
+In order that CI's validation class, which requires a $_POST array, can work, our
+function renames the array it received as a parameter as $_POST.
+ function insert2($controller, $newpost, $test = 'no')
+ {
+ $myform = '';
+ /*test the incoming parameters*/
+ if(!$this->db->table_exists($controller))
+ {
+ //test here!
+ }
+
+ $this->load->library('validation');
+ /*handle the validation. Note that the validation class works from
+ the post array, whereas this function only has a $newpost array: same
+ data, but different name. So we re-create the $_POST array.
+ */
+ $_POST = $newpost;
+ /*now build up the validation rules from the entries in our master
+ array*/
+ $errorform = '';
+ $newtemparray = $this->form[$controller];
+ foreach($newtemparray as $key => $value)
+ {$rules[$key]= $value[2];}
+ $this->validation->set_rules($rules);
+ /*and the name fields*/
+ foreach($newtemparray as $key => $value)
+ {$fields[$key]= $value[0];}
+ $this->validation->set_fields($fields);
+
+
+ $this->validation->set_fields($fields);
+ /*now do the validation run*/
+ if ($this->validation->run() == FALSE)
+ {
+ /*if the validation run fails, re-present the entry form by calling
+ the 'insert' function*/
+ $id = $_POST['id'];
+ $this->insert($controller, $id, 'no', $_POST);
+
+
+ [ 208 ]
+
+
+ Chapter 13
+
+ }
+ else
+ {
+ /*The validation check was OK so we carry on. Check if there is an id
+ number*/
+ if(isset($_POST['id']) && $_POST['id'] > 0)
+ {
+ /*if yes: this is an update, so you don't want the id number in the
+ post array because it will confuse the autoincrement id field in the
+ database. Remove it, but save it in $tempid to use in the 'where'
+ condition of the update query, then do the update*/
+ $tempid = $_POST['id'];
+ unset($_POST['id']);
+ $this->db->where('id', $tempid);
+ $this->db->update($controller, $_POST);
+ if($this->db->affected_rows()== 1)
+ {$this->showall($controller, "Entry number
+ $tempid updated.");}
+ else{$this->failure("Failed to update $controller for
+ id no $tempid", __FILE__,__LINE__);}
+
+ /*if no id number, we assume this is a new entry: no need to unset the
+ post array id as it isn't there! the database will create its own id
+ number. Do the new entry*/
+ $this->db->insert($controller, $_POST);
+ if($this->db->affected_rows()== 1)
+ {$this->showall($controller,
+ "New entry added.");}
+ else{$this->failure("Failed to make new entry in
+ $controller ", __FILE__,__LINE__);}
+ }
+ }
+ }
+
+And that's it. A few hundred lines of code, which allow you to do CRUD on
+any table.
+
+
+The Test Suite
+Remember those 'test blocks' in the delete function? Their purpose is simply to detect
+if the function is being run 'for real' or for a test, and, if the latter, to make sure that it
+returns a value we can easily test.
+
+This is all because, at the end of the CRUD model, we have a 'self-test' suite. This
+is called by the test function in any controller (it doesn't matter which one) and
+performs generalized CRUD tests using a dummy table.
+ [ 209 ]
+
+
+Instant CRUD鈥攐r Putting it All Together
+
+First in the CRUD class there is a master 'test' function, which only exists to call
+the others.
+ /*now a suite of self-test functions.*/
+ /*first function just calls all the others and supplies any formatting
+ you want. Also it builds/ destroys temporary data table before/ after
+ tests on the database.*/
+ function test()
+ {
+ $return = "
Test results
";
+ $this->extendarray();
+ $return .= $this->testarray();
+ $this->reducearray();
+ $return .= $this->testarray();
+ $this->testbuild();
+ $return .= $this->testdelete();
+ $this->testdestroy();
+ $return .= $this->testinsert();
+ $return .= $this->testinsert2();
+ $return .= $this->testshowall();
+ $data['text'] = $return;
+ $this->display->mainpage($data);
+ }
+
+This just assembles any tests you want, and runs them.
+
+However, rather than go through all these functions, let's just show one: the test
+function called testdelete().
+
+First, though, we need two functions: one to build, and one to destroy, our special
+dummy testing table, 'fred'. The first function destroys any existing 'fred' table,
+builds another, and puts a line of test data in it:
+ /*this function builds a new temporary table. 'fred', in your database
+ so you can test the CRUD functions on it without losing real data*/
+ function testbuild()
+ {
+ $this->db->query("DROP TABLE IF EXISTS fred");
+ $this->db->query("CREATE TABLE IF NOT EXISTS fred (id INT(11)
+ default NULL, name varchar(12) default NULL)");
+ $this->db->query("INSERT INTO fred 锟紸LUES (1, 'bloggs')");
+ }
+
+Depending on the test you want to run, you can make this more elaborate鈥攅.g.,
+populate more fields, or have more rows of data.
+
+
+ [ 210 ]
+
+
+ Chapter 13
+
+The second destroys the table so we can start afresh. There ought not to be any value
+left in it after we've done the delete test, but in case that failed, or in case we write
+other tests, let's make sure:
+ /*this function destroys the temporary table, to avoid any confusion
+ later on*/
+ function testdestroy()
+ {
+ $this->db->query("DROP TABLE IF EXISTS fred");
+ }
+
+Now we can start to test the delete function:
+ function testdelete()
+ {
+ $result = '
Deletion test
';
+
+The first test we might do is to make sure that the 'delete' function intercepts any
+delete call without a $state parameter of yes, and sends it to the trydelete
+function to ask 'are you sure?'
+
+Remember that we want the test to return 'OK' if the program handles each
+possibility correctly鈥攏ot if the possibility itself is 'right' or 'wrong'. So if the 'state'
+parameter says 'haggis', which is clearly 'wrong', the test should say 'OK' as long as
+the program treats it as 'not yes'! Ideally, we only want a short list highlighting the
+failures: if the tests are successful, we don't need to know the details.
+
+First we set up an array, in which each key is an expression we might use in a test,
+and the corresponding value is the result we expect:
+ $states = array(
+ 'no' => 'exception',
+ '1' => 'exception',
+ 'haggis'=> 'exception',
+ 'yyyes' => 'exception',
+ 'yes' => 'OK'
+ );
+ foreach($states AS $testkey => $testvalue)
+ {$test = $this->delete('fred', 1, $testkey, 'yes');
+ /*if you got the value you want, preg_match returns 1*/
+ $result .= $this->unit->run(preg_match("/$testvalue/",
+ $test), 1, $test);
+ }
+
+
+
+
+ [ 211 ]
+
+
+Instant CRUD鈥攐r Putting it All Together
+
+Assuming our code is working properly, that will return:
+
+
+
+
+Our next test is to see how, given a correct state, the delete function reacts to a series
+of ID values鈥攊ncluding non-integers, negative values, etc. Be careful about the
+granularity of the tests. For instance 9999 is a valid ID number in that it is an integer
+and greater than 0, but it won't lead to a delete operation as we only have one record,
+with an ID of 1! You need to be clear which stage of the process you are testing.
+ /*given $state set to 'yes', test another array of values for the id
+ number. Start by building a test table*/
+ $this->testbuild();
+ /*then another array of values to test, and the results you expect..*/
+ $numbers = array(
+ '9999' => 'OK',
+ '-1' => 'exception',
+ 'NULL' => 'exception',
+ '0' => 'exception',
+ '3.5' => 'exception',
+ '' => 'exception',
+ '1' => 'OK'
+ );
+
+
+ [ 212 ]
+
+
+ Chapter 13
+
+ /*now do the tests*/
+ foreach($numbers AS $testkey => $testvalue)
+ {$test = $this->delete('fred', $testkey, 'yes', 'yes');
+ $result .= $this->unit->run(preg_match("/
+ $testvalue/", $test), 1, $test);
+ }
+ /*destroy the test table, just in case*/
+ $this->testdestroy();
+ /*return the results of this test*/
+ return $result;
+ }
+
+All being well, that will return something like this:
+
+
+
+
+You can add as many tests as you wish.
+
+Testing helps the development process. As you think about different values to put
+in your test arrays, you have to consider whether your code will in fact handle
+them gracefully.
+
+It may also help later on, if you change your code and accidentally introduce errors;
+and it will reassure you, once the code goes to a production site, to run a test now
+and then.
+
+
+ [ 213 ]
+
+
+Instant CRUD鈥攐r Putting it All Together
+
+
+Summary
+This has been a long chapter but it has drawn a lot together. We've seen:
+
+ How to generalize CRUD operations so that you can do them with two
+ 鈥
+ classes: one for the controller, and one for the CRUD model. The former
+ needs to be repeated for each table, the latter stays the same.
+ As a result we could built in various checks and safeguards, as well as tests,
+ 鈥
+ so we can be confident that our CRUD operations are done.
+
+Using CI has allowed us to write all of this in a few hundred lines of (relatively)
+simple code, which we can reuse on almost any site we build, provided we obey a
+few simple naming and layout rules. To me, that's what frameworks are all about.
+
+
+
+
+ [ 214 ]
+
+
+ The Verdict on CI
+This book started out with some specific examples of how CodeIgniter can save
+you time and effort when you are writing websites using PHP. We've gone through
+some of the many things CI can do, using as a basis some parts of a website that will
+conduct regular tests of other websites. I hope that these examples have shown how
+CI makes coding much easier at the macro level.
+
+In this chapter, I'd like to step back a little and look at the overall impact of using the
+CodeIgniter framework. Does it make writing a complete application easier? Can it
+produce professional results?
+
+When you write a book like this, it's important to divide it up into sections and focus
+on one new trick at a time. That means that it's sometimes difficult to see how all the
+bits fit together. I hope the 'Instant CRUD' code in the last chapter went some way to
+putting different bits of code together, mixing up Active Record and unit testing and
+forms. In this chapter, I'd like to show how all the pieces can fit together to deliver a
+finished project. In other words, does our website-testing project work?
+
+Taking this top-down look at some specimen code from the site should help us to
+draw up a balance sheet:
+
+ Where CI helped
+ 鈥
+ 1. Organizing the site
+ 2. Making the code simpler
+ 3. Doing things for you (data validation etc.)
+
+ Where CI wasn't of much help.
+ 鈥
+
+
+The Verdict on CI
+
+
+Some Code: the 'do_test' Model
+What follows is a detailed look at part of one model on our site. This model works
+from the 'tests' table and the 'events' table. Its purpose is to control the most central
+function of our site i.e. the tests that it does on remote sites. This model:
+
+ Links the database tables listing the sites and the tests
+ 鈥
+ Updates another table, which lists events (i.e., each time a test is carried out)
+ 鈥
+ Helps interface with the user, allowing him or her to select a test pattern or to
+ 鈥
+ get information in various formats
+
+Because it's a model, it needs to be called by a controller, and it returns its results to
+a view. If the view contains hyperlinks, these in turn call a controller function, which
+acts as a 'front end' for another model function.
+
+Here's one of the do_test model's main functions, to generate a table of information
+directly from a database query. The idea is to list the sites available and select one to
+be tested. If you haven't selected one already, the function generates a list of sites and
+asks you to choose. The code looks like this:
+ /*this function prepares a report on existing tests and allows you to
+ choose which to do.
+ First, it selects a site and reports on that*/
+ function report($site=0,$message='')
+ {
+ /*have you chosen a site yet?*/
+ $siteid = $this->uri->segment(3, 0);
+ if(!$siteid > 0)
+ {
+ $text = "
";
+ /*note the next line uses the CI anchor function to generate
+ hyperlinks*/
+ $text .= anchor("tests/report/$row->id","Select");
+ $text .= "
";
+
+ [ 216 ]
+
+
+ Chapter 14
+
+ }
+ }
+ $response['mytext'] = $text;
+ $response['message']= $message;
+ $this->display->mainpage($response);
+ }
+
+The result, when it's called via a controller, looks like this:
+
+
+
+
+Notice there's a hyperlink for each option, so you can select that site. I'd have liked
+to use the HTML 'Table' class to do this automatically, but I can't see an easy way to
+include hyperlinks in the result rows if I do. But:
+
+ CI's active record class makes the database queries much easier to write
+ 鈥
+ CI's anchor function (part of the URL helper) makes it easier to write
+ 鈥
+ the hyperlinks
+
+And, of course, when we come to move our site from my development server to
+the web, we know that these two CI functions will ensure that the database
+connection information, and the site configuration details such as the URL, are
+automatically changed.
+
+CI helped me to organize the code, too. The $siteid variable is passed in as part of a
+URI segment when the model is called from the menu.
+
+Notice how the actual web page is created via the mainpage function of the display
+model. All the do_test model has to do is take the specific information we need
+out of the database, and send it off. The display model ensures that it is formatted
+according to our .css file, and laid out on a page with a menu bar, etc.
+
+I'm not being quite proper here, of course, since the table formatting is in the dotest
+model. There are far too many angle brackets in there: ideally, they should be in a
+view file. However, this would have required us to design a 'view' file specially for
+this piece of data. It seemed simpler to do it this way. At least the design information
+is kept out of the way in the .css file.
+
+
+ [ 217 ]
+
+
+The Verdict on CI
+
+Now we move on to the next part of this function. If you have selected a site, the
+view now lets us see the tests that have been done for that particular site.
+
+The code looks like this鈥攊t's the else loop following the:
+ if(!$siteid > 0)
+
+In other words, this is what happens if a valid $siteid was passed.
+ else
+ {
+ /*ok, you've chosen a site. let's go to town*/
+ $this->db->select('sites.name AS sitename, sites.url AS siteurl,
+ tests.name AS testname, tests.lastdone AS lastdone, tests.id AS
+ testid, frequency, sites.id AS siteid, tests.type AS type');
+ $this->db->join('sites', 'sites.id = tests.siteid');
+ $this->db->orderby('frequency desc, sitename asc');
+ $this->db->where('sites.id', $siteid);
+ $query = $this->db->get('tests');
+ if ($query->num_rows() > 0)
+ {
+ $xrow = $query->row();
+ $report = "
";
+ }
+ else
+ $report = "no tests for this site yet.";}
+ $report .="
Other
+ options
";
+ $report .= "
";
+ $report .= anchor("tests/getwrittenreport/$row->siteid/604800",
+ 'Get a written report for last week');
+ $report .= anchor("tests/getwrittenreport/$row->siteid/2592000",
+ 'Get a written report for last month');
+ $report .= "
";
+ $report .= anchor("tests/getbaseremotefiles/$row->siteid", 'Update
+ remote file list for this site');
+ $report .= "
";
+ $response['mytext'] = $report;
+ $this->display->mainpage($response);
+ }
+ }
+
+The result of this code looks like this:
+
+
+
+
+You can see that it has consulted the database to find out which tests should be
+applied to our selected site. There's only one test, a simple 'ping' which is supposed
+to be done every 15 minutes.
+
+ [ 219 ]
+
+
+The Verdict on CI
+
+It hasn't been done in the last 15 minutes, so it's shown as overdue and a hyperlink
+allows me to do it now if I want to. If I click on the hyperlink, I call the tests
+controller's runtest function, supplying it with the ID number of the test I want.
+
+The result looks like this:
+
+
+
+
+The system tells me when the test has been done, that there was no problem with it,
+and how long it took.
+
+If I now go back to the report function, you'll recall that there were options to
+generate historical reports on tests conducted on my selected site over the last week
+or the last month. I've only set this site up as an example, so it has no real test history:
+but this is how it looks if I do a series of identical tests in quick succession. I've also
+added another test, and the report includes that as well.
+
+
+
+
+ [ 220 ]
+
+
+ Chapter 14
+
+As you can see, we're building up a report that we could show to a client, to
+demonstrate that the site was alive and responding when it was tested in different
+ways at various times. The time taken for each test to respond is not of much interest
+at any one time, but may help us to see a pattern over a longer period.
+
+The function that actually runs the tests is built around a switch statement. It takes
+two parameters:
+
+ The ID number of the test, from which we look up basic information that we
+ 鈥
+ need to run it: the URL to use, any name锟絭alue pairs, and any text we expect
+ to find if the test is successful.
+ The type of user. (If the user is 'human', the programme returns more
+ 鈥
+ information in a more user-friendly format鈥攊n other words, if you want to
+ display the result on a screen, call it with the user parameter set to human.
+ If you want to handle the results programmatically, set this parameter to
+ something else.)
+
+In this excerpt from the code, we've defined several types of test. Two examples are:
+
+ 'ping' tests which simply call the URL. If they get a result, they examine it
+ 鈥
+ either for an expected phrase (called 'regex' in the database) or for a generic
+ HTML term if there is no 'regex' phrase set.
+ 'ete' tests which use some code we developed to make an 'end-to-end' test of
+ 鈥
+ a protected page, logging in and looking for an expected phrase. This code is
+ not explained in this book as its not included in CI's functionality.
+
+Each test returns a $result and a $timetaken variable; these are entered into
+the 'events' table of the database as a record of the test, together with the other
+information from the database. Here's the code, which draws heavily on the CI
+Active Record model to read from and write to the database, and on the benchmark
+class to get the times taken by each test.
+ /*function to run an individual test*/
+ function runtest($testid, $user='human')
+ {
+ /*first, look up the test details */
+ $this->db->where('id', $testid);
+ $query = $this->db->get('tests');
+ if ($query->num_rows() > 0)
+ {
+ foreach ($query->result() as $row)
+ {
+ $type = $row->type;
+ /*then work out which type it is and forward it accordingly*/
+
+
+ [ 221 ]
+
+
+The Verdict on CI
+
+ switch ($type){
+ case 'ping':
+ $this->benchmark->mark('code_start');
+ $result =$this->pingtest($testid);
+ $this->benchmark->mark('code_end');
+ $timetaken = $this->benchmark->elapsed_time('code_
+ start', 'code_end');
+ break;
+
+ case 'ete' :
+ $this->benchmark->mark('code_start');
+ $result = $this->httppost($testid);
+ $this->benchmark->mark('code_end');
+ $timetaken = $this->benchmark->elapsed_time('code_
+ start', 'code_end');
+ break;
+
+ default:
+ $result = 'noid';
+ }
+ /*work out which site the test belongs to*/
+ $this->db->select('tests.siteid AS id');
+ $this->db->where('id', $testid);
+ $query = $this->db->get('tests');
+ if ($query->num_rows() > 0)
+ {$srow = $query->row();
+ $mysiteid = $srow->id;
+ }
+ else{$mysiteid = 0;}
+ /*build the rest of the result set and enter it into the database*/
+ $time = now();
+ if($result == 'OK')
+ {$isalert = 'n';}
+ else{$isalert = 'y';}
+ $this->db->set('name', $type);
+ $this->db->set('type', 'test');
+ $this->db->set('timetaken', $timetaken);
+ if($result != '')
+ {$this->db->set('result', $result);}
+ $this->db->set('testid', $testid);
+ $this->db->set('userid', 0);
+ $this->db->set('siteid', $mysiteid);
+ $this->db->set('time', $time);
+
+ [ 222 ]
+
+
+ Chapter 14
+
+ $this->db->set('isalert', $isalert);
+ $this->db->insert('events');
+ $mydata = array(
+ 'lastdone' => $time,
+ 'notes' => $result,
+ 'isalert' => $isalert
+ );
+ $this->db->where('id', $testid);
+ $this->db->update('tests', $mydata);
+ /*only return this info to screen if user is human. Otherwise, no need
+ to do anything more; you've updated the database.*/
+ if($user == 'human')
+ {$mytext = "
";
+ $mytext .= "
Test took $timetaken.
";
+ $mytext .= "
Result was ".$result.
+ '
';
+ $mytext .= $this->testhistory($testid);
+ $response['mytext'] = $mytext;
+ $this->display->mainpage($response);}
+ else{return $response;}
+ }
+ }
+ }
+
+
+How we actually print out the test report is an interesting example of CI in action. As
+ever, there is a range of choices. You can print out your report the hard way, using
+HTML in your code as we did earlier, like this:
+ /*do database query here!*/
+ /*now format the results the hard way鈥︹*/
+
+ $report .= "
";
+
+On the other hand, you could use the HTML Table library to make life easier:
+ /*do db query here */
+ /*format the results using the CI HTML table library*/
+ $report .= "Test history:";
+ /*redefine our CI table layout if we want to, using our css file*/
+ $tmpl = array ('table_open' => '
',);
+ $this->table->set_template($tmpl);
+ if ($query->num_rows() > 0)
+ {
+ $this->table->set_heading('Time of test', 'Name ,'Time
+ taken','Result');
+ $report .= $this->table->generate($query3);
+ }
+ $this->table->clear();
+
+Clearly, shorter and simpler. The results appear exactly the same, depending only on
+the HTML formatting. However, let's look at two issues that this code raises.
+
+When you look more closely, there is a compromise that comes with convenience. In
+the first, longer, version, it is possible to format the date:
+ $report .= gmDate("j:n:Y H:i", $row->time);
+
+This gives me a 'human' date rather than the Unix date I've actually stored in my
+database. In other words, the table says '24 Apr 2007 09 04' rather than '1177405479'.
+However, it isn't easy to do this with the CI HTML Table functions. (You might
+be able to do this within the database query itself in some database systems; but
+MySQL's date functions only operate on dates and times stored in MySQL's unique
+date format, and we chose to use the Unix format instead.) We had the same trouble
+earlier on when we wanted to generate hyperlinks. You can't do it this way.
+
+
+
+
+ [ 224 ]
+
+
+ Chapter 14
+
+There's another big question about this. Where should the formatting go? All the
+code written previously should appear either in a controller or a model. (As I've
+written it, it's in a model, which is called by controllers when the human user clicks
+on a hyperlink to generate reports. The reason for putting it in a model is so I can call
+the code from several controllers if I need to.)
+
+MVC purists would say that you should never put formatting in a model. It should
+be in a view. And, indeed, they've got a point. I might want to rewrite the code to
+produce a text report, and use CI's download helper to force the site to download
+it in text format rather than display it on the screen. (See Chapter 11 for more about
+the download helper.) As it is, I've got to re-write all the code to produce something
+formatted with text breaks rather than
pairs鈥攐r, even worse, to produce
+format in .rtf, or some similar rich-text format.
+
+The other option is to use the alternative PHP syntax or the Template Parser class
+and put variables, or placeholders, actually in the views themselves. (See Chapter 5
+for a brief coverage of these options.) Then, all you pass to the view is the actual data,
+unformatted. While this may satisfy the MVC purists, I find it adds an extra layer of
+complexity to the code. But this is a personal view, and many would disagree.
+
+The main point is that CI offers a range of options: it's up to you to choose the one
+you are happiest with. There is no 'right' or 'wrong' way: there are just some ways
+that work better than others, and some ways that suit your personal style better
+than others.
+
+
+
+A Balance Sheet
+Let's look back over what we've covered in this book. Was CI helpful?
+
+
+Where CI Helped: Structure
+Even from looking at a part of just one model on our site, it's obvious that any
+worthwhile application quickly becomes complex. CI, by suggesting and to some
+extent enforcing an MVC structure, helps bring some order to that complexity. It's
+still possible to forget where you put a piece of code, or even write a similar function
+twice in different controllers or models: but it's more likely that your code will be in
+logical chunks.
+
+CI's URL mechanism helps you quickly link from one code file to another.
+
+
+
+
+ [ 225 ]
+
+
+The Verdict on CI
+
+CI's 'super-object' structure makes sure that the pieces of code can call each other and
+pass information around, without namespace conflicts. We are less likely to have
+confusions between variables with the same name, because each one is confined to
+its own area. At the same time, you can easily access all the CI resources on your site,
+from wherever you are in your code.
+
+CI's 'config' files encourage you to build up a centralized collection of information
+about your site.
+
+All these benefits mean that your site will be easier to develop, easier to maintain,
+and easier for other programmers to understand.
+
+
+Where CI Helped: Simplicity
+There are lots of places where CI helps simplify your code. Perhaps the best
+examples come from the Active Record class, but there are many others in this book.
+CI is excellent at taking complex chunks of code, hiding them away in a function of
+some library class, and allowing you to use them with a simple function call.
+
+
+Where CI Helped: Extra Functionality
+Many CI functions bring additional benefits when you use them. Functions like the
+URL class and Active Record automatically refer to your 'config' settings, so you
+don't have to keep repeating information, and so that any changes you make in one
+place are automatically applied across the site.
+
+There are lots of small examples鈥攖he easy way you can hide your email addresses
+from robots, for example (see Chapter 3), or that the Active Record class also
+prepares your data.
+
+In fact, one of the main learning curves (for me at least) with CI is finding out what
+shortcuts are available, and remembering to use them instead of writing it all out the
+laborious long-hand PHP way that I'm used to. If this book draws your attention to
+some of these shortcuts, it will have served a useful purpose for that alone.
+
+
+
+Problems with CI
+CI isn't perfect. This really means that it is a balancing act: lightness and ease of use
+against complexity and completeness. As somebody once said, 'lightweight' means,
+"includes everything I want and nothing that I don't want".
+
+
+
+
+ [ 226 ]
+
+
+ Chapter 14
+
+Completeness
+CI contains functions for almost every regular application you can think of. There are
+some exceptions to the subjects covered by the main CI classes, and many of them
+are covered by additional libraries contributed by CI users (see the next chapter) or
+available through the PEAR repository. The most obvious omissions include:
+
+ An AJAX class
+ 鈥
+ A class to write web robots
+ 鈥
+ A class to build a secure site, handling logging in, protected pages, and such
+ 鈥
+ like, as well as basic session maintenance
+ An improved 'scaffolding' class, producing something that could be used on
+ 鈥
+ a public site rather than purely for development
+
+CI might also take a leaf from the Rails book, and build a code generator class鈥攚hich
+would allow developers to build self-customizing classes.
+
+
+Ease of Use
+CI demands some effort to learn, as you would expect. But, assuming that you
+already know some PHP, it's quite easy. In fact, the main 'learning curve' I find is
+that, when you know the standard PHP way of doing something, you tend to do it
+that way. Only later on, when you are looking in the CI User Guide for something
+else, do you realize that you could have done the first thing much more quickly and
+simply with a CI class or helper.
+
+Speaking personally, I found two CI classes quite difficult to understand and follow.
+These are the XMLRPC class, and the Validation class. This is largely because they
+both require interface between different pages, or different sites, in order to
+work, and it is sometimes difficult to get this set up properly. (See Chapters 9 and
+8, respectively)
+
+I also found it difficult at first to follow the way CI uses its 'super-object'鈥攕ee
+Chapter 7. Getting that right may be the steepest CI learning curve, and mistakes can
+lead to some odd and frustrating results until you understand it.
+
+The rest: easy. If in any doubt, you can always explore the source code.
+
+
+
+
+ [ 227 ]
+
+
+The Verdict on CI
+
+
+Summary
+In this chapter, we've looked at some coding examples, bringing together a lot of the
+functions , which we've discussed bit by bit in the preceding chapters.
+
+We've also looked at the way CI helps:
+
+ To organize your site
+ 鈥
+ To make coding simpler
+ 鈥
+ To add functionality
+ 鈥
+
+I hope this book has persuaded you that, if you want to code dynamic websites in
+PHP, CI is a very smart way to do it.
+
+It's also a tribute to the open-source movement, and to the generous people who
+support it, that such a rich collection of code is available, freely and universally.
+Thanks, Rick!
+
+Taking this theme of generosity further, the last chapter in this book looks at some
+other ways you can develop your CI coding鈥攖he community of CI users who can
+(and usually will) provide help, support, and additional code resources.
+
+
+
+
+ [ 228 ]
+
+
+ Resources and Extensions
+Well, we've looked at CI pretty thoroughly, and I hope you're impressed. We've also
+developed some of our own code in the process. I'm sure when you looked at some
+of my code, you started thinking "I could write that better鈥". Everyone has their
+own style, and CI allows you a lot of freedom.
+
+The CI Community is full of people who write good code, and luckily many of them
+are prepared to make it available free of charge to the rest of us. So there's a lot of
+code out there that may save you a lot of work. If, to take one example, you want to
+create dynamic graphs of data drawn from your database, you could sit down and
+write the code yourself, but in fact, at least three people have already tackled this
+problem, and all of them have made their code available to you.
+
+This last chapter looks at some of the resources you can draw on, to make your
+coding quicker and easier. CI has a thriving and active community of users and
+the available resources are changing all the time, so I haven't tried to produce an
+exhaustive list, just give you an idea of what's there and where to look.
+
+There's a cautionary note too. There is so much code out there that it can easily be
+confusing. People write their pet projects, some brilliant, some just quite good. Many
+of us are better at writing code than writing explanations or comments. As a result, it
+can be quite difficult to work out just what each library or plug-in does, and whether
+it's the best one for you.
+
+So, let's spend the last chapter of this book looking at the available help.
+
+ Firstly, let's look at the sources of code.
+ 鈥
+ Then, let's look at a few subjects and compare the code that's available.
+ 鈥
+ Lastly, let's look at more general sources of help: on PHP, MySQL,
+ 鈥
+ and Apache.
+
+
+Resources and Extensions
+
+
+CI's User Forums
+CI has two main resources:
+
+ The user forums, at http://www.codeigniter.com/forums/ offer a lively
+ 鈥
+ and pretty well continuous discussion of most CI issues. Comments and
+ suggestions made are not always helpful (or accurate), but there are a
+ number of 'senior members' who usually make a lot of sense. It's quite a kind
+ forum, too; people ask very obviously 'newb' questions, but get patient and
+ helpful replies. Occasionally, Rick Ellis himself chips in, but he quite rightly
+ doesn't try to field every issue himself.
+
+
+
+
+ [ 230 ]
+
+
+ Chapter 15
+
+ The wiki, at http://www.codeigniter.com/wiki/ .This is intended as a
+ 鈥
+ "repository for tips, tricks, hacks, plugins, and enhancements." It contains a
+ lot of useful code, although coverage is not systematic.
+
+
+
+
+Using the forum or the wiki is easy: you just create a membership for yourself (free)
+and then log on and do your thing.
+
+If you are seriously using CI, it's worth setting your RSS reader to subscribe to the
+'recent changes' feed on the Wiki.
+
+Remember, though, that:
+
+ Not all plug-in writers are as technically competent as Rick Ellis. Their
+ 鈥
+ products may have bugs or issues.
+ Some of the older plugins written before CI version 1.5 came out may need
+ 鈥
+ altering, because the way that libraries were initialized was changed (see
+ Chapter 12). This should not be too difficult to do, but it does mean these
+ library files won't work straight out of the box.
+
+
+
+
+ [ 231 ]
+
+
+Resources and Extensions
+
+
+Video Tutorials
+If you want to be literally talked through your first CI application, there are three
+excellent video tutorials on the CI site.
+
+ An introduction to CI.
+ 鈥
+ Create a blog in 20 minutes. Derek Jones builds basic blog pages, showing
+ 鈥
+ you how to set out the site, make database queries, and present the results
+ in views.
+ A link to an external video by Derek Allard
+ 鈥
+ (see http://video.derekallard.com/), which describes,
+ among other things, how to use the Scriptaculous library to integrate
+ AJAX and JavaScript effects. Using the view below, this shows you how to
+ build an auto-complete text entry dropdown, using Ajax to update it.
+
+
+
+
+Available Plug-ins and Libraries
+Rick Ellis's intention and hope was that CI users would contribute 'plug-ins' or
+libraries to help other CI users. The framework has only been available for about a
+year, but already there is a lot of interesting code available.
+
+
+ [ 232 ]
+
+
+ Chapter 15
+
+The number of plug-ins and libraries is growing steadily, and those already there are
+being changed. So the next section is not a systematic account of what's there: just a
+few notes on some of the things you might find useful. I'm sorry that I've had to miss
+out a lot of good stuff: please do look at the wiki yourself.
+
+
+AJAX/JavaScript
+The wiki contains two AJAX packages: one using XAJAX, and the other the
+prototype.js锟絪criptaculous.js libraries.
+
+ Name Ajax for CI 1.5.1
+ URL http://www.codeigniter.com/wiki/AJAX_for_CodeIgniter/
+ Uses the prototype.js and scriptaculous.js libraries
+ Download includes .js files as well as .php and a full User Guide. (This is not easy to
+ understand if you don't already have a good grasp of AJAX and the DOM, and it could
+ usefully have had some longer examples.) Simple to install: place the .php file in your
+ application/libraries folder and the .js files in your root directory.
+ Newly released, so very few comments on the CI Forums.
+ Author siric
+
+
+ Name XAJAX
+ URL http://www.codeigniter.com/wiki/XAJAX/
+ A CI 'front end' for the XAJAX library. Includes its own JavaScript 'include' file, xajax.js
+ Author Greg McLellan鈥攂ased on the xajax php library (see http://www.
+ xajaxproject.org/ )
+
+
+Authentication
+Wiki users have also wrestled with security: these three packages look at
+authenticating your users and avoiding the possible pitfalls of storing session data
+in cookies.
+
+
+
+
+ [ 233 ]
+
+
+Resources and Extensions
+
+
+ Name FreakAuth_light
+ URL http://www.4webby.com/freakauth/
+ This includes a library to perform
+ user login锟絣ogout
+ 鈥
+ user registration
+ 鈥
+ remember password
+ 鈥
+ change password
+ 鈥
+ website reserved areas locking
+ 鈥
+ a backend administration application to:
+ manage users
+ 鈥
+ manage administrators
+ 鈥
+ It allows you to set four levels of access (from superadmin down to guest) and then to set
+ a 'check' method in controllers. This can be set either in the controller constructor or in
+ individual functions. If a user invokes the controller (or the individual function) the code
+ checks that he锟絪he is logged in, consulting
+ There's an extensive discussion of this code going on in the CI forums at the time of
+ writing. Some errors have been identified, but the code is now on its third release and it
+ looks as if the problems are being resolved.
+ Author danfreak
+
+
+ Name Auth
+ URL http://www.codeigniter.com/wiki/auth
+ This package offers login锟絣ogout functionality, registration, with activation, and even a
+ forgotten password reset. It's quite complex to set up: you have to set up a database table,
+ include some new core libraries and helpers, and also do some configs.
+ Works with CI 1.5.
+ Author Anonymous
+
+
+ Name DB Session
+ URL http://www.codeigniter.com/wiki/DB_Session/
+ http://dready.jexiste.fr/dotclear/index.
+ php?2006/09/13/19-reworked-session-handler-for-code-
+ igniter
+ Alters the CI session class (see Chapter 6) which stores session data in cookies. (Which can
+ be encrypted, of course.) This class only stores a session identifiers: you add an extra table
+ to your database, and it looks up all the rest of the session information there.
+ Works with CI 1.5.
+ Author dready
+
+
+ [ 234 ]
+
+
+ Chapter 15
+
+External Sites
+There are some 'power users' of CI who contribute code of their own. One good
+example is Glossopteris, a site run by a US web design company. This makes
+available some of their own libraries, for instance (at http://www.glossopteris.
+com/journal/post/table-relationships-in-ci) another CRUD library, which
+they claim "will allow for complex table inter-relationships to be assigned and simple
+CRUD actions to be completed." This follows the Rails precedent quite closely: you
+can define relationships between tables such as 'has one' and 'has many' links. The
+code is available, but could do with more comments or a user guide.
+
+Another development is CI_Forge (http://www.ciforge.com/), which is intended
+as, "A place for projects designed to enhance or extend the lightweight PHP
+framework CodeIgniter." It provides Subversion and Trac hosting, a wiki, a
+bug锟絠ssue tracker, and change log support. This is a new application, but (as at July
+2007) already hosts 20 projects.
+
+
+Comparisons: Which Charting Library to Use?
+That's quite a range of options. Sometimes, there can be almost too much choice.
+
+To demonstrate this, let's look at three options for doing the same thing, and see how
+they differ. Making dynamic charts of data is not an easy thing to code on your own.
+But it does make your site look good.
+
+Let's look at three add-ons available for CI that do just this, and try to compare their
+strengths and weaknesses, as well as look at the results they produce.
+
+ Name 3d-pie-chart
+ URL http://codeigniter.com/wiki/3d-pie-chart/
+ Generates a pie chart from two arrays of data (labels and values) and saves it on your site.
+ Looks great, but this is all it does.
+
+
+
+
+ Simple to set up: put the piechart.zip file in your application/libraries folder,
+ and write a controller based on the example. Requires a font, and you need to modify a
+ view to display the results. Works with CI version 1.5
+ Author Craig
+
+ [ 235 ]
+
+
+Resources and Extensions
+
+
+ Name Panaci
+ URL http://bleakview.orgfree.com/ or
+ http://codeigniter.com/wiki/Charting/
+ Dynamically generates charts and graphs, including bar, line, area, step, and impulse charts
+ (but not pie charts). The wiki entry states: "Please note, this is NOT a commercial grade
+ library such as jpgraph or chartdirector, but it is quite adequate for basic plots". The code
+ example, and specimen plot, below, show what it looks like and how to use it.
+
+
+
+
+ Works with CI version 1.5. As with 3d-pie-chart, you copy the file into your
+ application/ libraries folder, and call it from your controller, supplying basic
+ parameters and an array of data.
+ Short discussion in CI Forums, no major bugs found at time of writing.
+ Author Oscar Bajner
+
+ [ 236 ]
+
+
+ Chapter 15
+
+
+Name JP Graph
+URL http://codeigniter.com/wiki/JP_Graph/
+This is not strictly a plug-in: it's code that allows you to interface between CI and the
+external JP Graph library. You need to download the JP Graph library, create a series of
+plug-ins for each graph type you want to use, and then call the plug-ins from a controller as
+you need them.
+
+As these examples form its website, http://www.aditus.nu/jpgraph/features.
+php show, JP Graph offers a much wider range of charts, and they look great.
+
+
+
+
+There are two disadvantages with JP Graph. As the wiki entry says: "Keep in mind that
+JpGraph has a very large codebase, so be sure to include only the specific libraries you need
+for each chart." Secondly, JP Graph is free for personal use, but not for commercial use.
+Author Aditus Consulting
+
+
+
+ [ 237 ]
+
+
+Resources and Extensions
+
+Three options: the first two relatively simple, the second more complex. It depends
+on what you need (and if you are prepared to pay).
+
+
+CRUD: the Final Frontier
+You need to write CRUD pages in almost every application. It seems simple, and
+logical, to automate the process of creating those pages! They are tantalizingly
+standard鈥攁nd yet they have deceptively large numbers of possible variations. It's
+impossible to write one without starting to impose your own rules and assumptions
+on the user. Also, there is always a trade-off between covering more and more
+possible options on the one hand, and simplicity of use on the other. The more
+exceptions and possibilities you try to cover, the more complex your code becomes
+and the larger the download is.
+
+So, quite a few people have had a go at simplifying the basic CRUD operation.
+
+We tried our hand at developing our own CRUD application in Chapter 13. This was
+a fairly simple model that cut a lot of corners and only allowed you to use a subset
+of the available HTML form objects; but it does manage to incorporate CI's
+validation functions.
+
+We've already mentioned, in this chapter, the Glossopteris library.
+
+Another interesting approach is 'CodeCrafter', which is listed on the CI wiki
+and available from Datacraft Software Consulting in South Africa, at:
+http://www.datacraft.co.za/index.php?contents=codecrafter/codecraft.
+This claims that, "CodeCrafter will help you generate your entire CodeIgniter
+application in just seconds." It comes with a 26 page online manual, which shows
+you how to use its interface to generate CI code. This is a different method to most
+of the other offerings reviewed here: it builds the CI code for you, using a graphical
+interface, rather than providing libraries or code for you to patch in.
+
+SuperModel (see http://codeigniter.com/wiki/SuperModel/) claims: "The
+SuperModel Library is an extension to models to automate most of the mundane
+form-generation and validation tasks. Think of it as scaffolding on steroids."
+
+The author's comments explain the frustrations of writing this sort of code鈥攁nd
+also the risk for users. He says: "Please note this library is a work-in-progress. I am
+currently making many changes, including API changes that will break applications.
+As I write this (May 30锟2006) I am working on implementing one<>many and
+many<>many joins鈥︹t's impossible to write something like this, but stay as
+flexible like CodeIgniter is. Unfortunately, this library forces you into doing some
+things a certain way. I've tried to be as flexible as possible, but at the same time, there
+has to be a line drawn between being flexible, and being completely bloated. That's
+
+ [ 238 ]
+
+
+ Chapter 15
+
+why this is an external 3rd party library鈥攜ou're free to implement models the way
+you want, or use some other similar 3rd party library that does something similar."
+
+
+
+Resources for Other Programmes, e.g.
+Xampplite, MySQL, PHP
+There are a lot of useful resources for PHP. Let's just touch on some of them briefly.
+
+ PHP itself can be downloaded free from www.php.net, which also includes a
+ 鈥
+ full manual.
+ A low-cost PHP editor can be bought from MP Software at
+ 鈥
+ http://www.mpsoftware.dk/.
+
+There are many good books on PHP, including PHP Programming with PEAR, by
+Carsten Lucke, Aaron Wormus, Stoyan Stefanov and Stephan Schmidt, published
+by Packt.
+
+To run a local web server on your own machine, try looking at
+http://www.apachefriends.org/en/index.html鈥攁 site that offers free
+downloads of the XAMPP package. This installs an Apache web server with MySQL,
+PHP, and Perl. If the XAMPP package is too comprehensive for you, try Minixampp
+from the same site, on which the code used in this book was written.
+
+MySQL too has its own web page鈥攈ttp://www.mysql.com/鈥攖hough if you want
+to download the latest versions for free, go to http://dev.mysql.com/. (Bear in
+mind though that many ISPs don't use the latest versions. Although MySQL is up to
+version 5, most ISP's are still using Version 4. This prevents you using some of the
+more interesting new features, like stored procedures.) See, Creating your MySQL
+Database: Practical Design Tips and Techniques, by Marc Delisle, published by Packt.
+
+Although MySQL comes with its own tools, the most popular (and most common)
+tool is PHPMyAdmin. (See Mastering phpMyAdmin 2.8 for Effective MySQL
+Management, also by Marc Delisle, published by Packt.)
+
+
+
+
+ [ 239 ]
+
+
+Resources and Extensions
+
+
+Summary
+In this Chapter, we've looked at some of the resources available to you when you
+start to code with CI. There's a lot of ready-made code available. You have to look
+before you use: don't just take the first plug-in or library that seems to do what you
+want and start using it. You need to study each offering to see what it really does,
+and it also helps to go through the code and make sure you understand it. However,
+if you are prepared to do this, you can find libraries at different levels of scope and
+complexity that will take on many of the tasks that would otherwise have involved a
+lot of hand coding.
+
+In particular, we looked at libraries for
+
+ AJAX and JavaScript
+ 鈥
+ Authentication
+ 鈥
+ Charting
+ 鈥
+ CRUD
+ 鈥
+
+Lastly, we looked at some of the resources available for PHP and MySQL and for
+running a local web server.
+
+
+
+
+ [ 240 ]
+
+
+ Index
+ CodeIgniter. See also CodeIgniter site
+A
+ about 12
+Active Record class advantages 7-11
+ about 51-53 config files, using 24
+ advantages 54, 55 configuration file 24
+ automatic functionality 54 configuration settings 51
+ queries, creating 59, 60 disadvantages 16, 17, 226
+ queries, deleting 61 downloading 22
+ queries, reading 56, 57 file structure 23
+ queries, updating 59, 60 file types 23
+ query results, displaying 58 for communication 127
+ time, saving 54 framework 12
+ with default query styles 61 installing 22
+array interface problems 171
+ about 189 license 18
+ parameters 190 open source business model 15, 16
+ prerequisites 21
+B requirements 22
+ setting up 21
+benchmarking class 121 syntax rules 33
+ testing 25
+C URL helper 46
+ uses 7
+charting libraries
+ version changes 177
+ about 235
+ CodeIgniter site. See also CodeIgniter
+ comparing 235-237
+ architectural issues 73
+classes, CodeIgniter
+ class types 34
+ add ons 181-183
+ designing 85, 86
+ date helper 141
+ file types 34-36
+ inflector helper 141
+ navigating 86-90
+ language class 141
+ table structure 37
+ text helper 141
+ configuration settings, CodeIgniter
+code, testing
+ configuration file 51
+ end-to-end tests 112
+ database 52
+ need for 111, 112
+ dbdriver 52
+ unit testing 112
+ hostname 52
+ uses of CodeIgniter 112
+
+
+ password 52 test types 221
+ username 52 download helper
+ about 158
+controllers
+ default controller 33 database, downloading as a text file
+ designing 39-41 158-160
+ parameters, getting to function 40, 41
+ E
+ syntax rules 34
+ welcome controller 31
+ email class
+copy by reference 103
+ working with 136-139
+CRUD
+ error handling class 113
+ about 185, 238
+ array 189
+ F
+ concept 186
+ data, reading 195
+ file helper
+ defining 189
+ about 156
+ functions 192
+ arrays, comparing 157
+ insert 201
+ file, writing 156
+ insert2 208
+ loading 156
+ parameters 190
+ file upload class
+ showall 192
+ about 160
+ test suite 209
+ loading 160
+ working of 160-165
+D form helper
+ about 74
+database
+ advantages 74-77
+ designing 53
+ forums
+ setting up, for website 63-66
+ CodeIgniter forum 230
+database tables
+ CodeIgniter wiki 231
+ about 189
+ framework
+ rules 189
+ about 12
+date helper
+ PHP frameworks 13
+ about 142
+ Rails 13
+ date formats 142
+ Ruby on Rails 13
+ date formats, converting 143
+ FTP class
+ time zones, generating 143, 144
+ remote files, testing 127-129
+diagnostic tools 174
+display model 78, 79
+ I
+dotest model
+ about 216
+ image class
+ database, reading from 221-223
+ about 165
+ database, writing to 22-223
+ crop, fucntions 166
+ parameters, switch statement 221
+ fucntions 166
+ report 220
+ resize, fucntions 166
+ table information, generating from database
+ resizing 166, 167
+ query 216
+ rotate, fucntions 166
+ test loop 218, 219
+ watermark, fucntions 166
+ test report, printing 223, 224
+
+
+ [ 242 ]
+
+
+ authentication packages 233, 234
+inflector helper 145
+ charting libraries 235-237
+insert 201
+ CRUD 238
+insert2 208
+ external sites 235
+interface problems
+ about 172 JavaScript package 233
+ CodeIgniter version changes 177 profiler class 122
+ config files 173
+ Q
+ databases 172
+ diagnostic tools 174
+ queries, Active Record
+ OS difference 173
+ creating 59, 60
+ PHP version difference 173
+ deleting 61
+ URLs 172
+ reading 56, 57
+L results, displaying 58
+ updating 59, 60
+language class
+ R
+ about 146
+ webpage, translating 146-149
+ remote files
+library class
+ testing, FTP class used 127-129
+ for security 92
+ resources
+M CodeIgniter forum 230
+ CodeIgniter wiki 231
+ MySQL 239
+menu
+ creating 48 PHP 239
+ video tutorials 232
+N XAMPPLite 239
+ Ruby on Rails 13
+nested views
+ S
+ about 70
+ header view, creating 70-72
+ parameters 72 security/sessions
+ library class 91, 92
+O sessions, turning into security 94-96
+ session class 91
+objects showall 192
+ about 99 standard controller 187
+ super-object 100 super-object
+OOP code, adding 105, 106
+ about 99 drawbacks 106-108
+ objects 99 log, tracing 101
+ working with 100-103
+P
+ T
+PHP syntax
+ long syntax 69 table class
+ short syntax 69 about 150
+plugins and libraries HTML tables, writing 150-152
+ AJAX package 233
+ [ 243 ]
+
+
+ pages, caching 152, 153 model, calling 178
+ model, loading 178
+text helper
+ about 145 updating 179-181
+ inflector helper 145 views
+ strings, truncating 145 designing 37, 39
+ nested views 70-73
+U syntax rules 34
+ working with 32
+unit test class 115 writing 67-69
+unit testing
+ X
+ about 112
+ controlling 124
+ criteria 117, 118 XMLRPC
+ example 118-120 about 129
+ mock database, testing with 123 debugging 134, 135
+ timing 124 exchanges, formatting 132-134
+ issues 135, 136
+V server-client communication 131, 132
+
+ Z
+validation class
+ about 79
+ controller, setting up 81 zip class
+ forms, setting up 81, 82 about 169
+ validation, setting up 80 file, compressing 169
+version changes, CodeIgniter
+ library classes, initializing 179
+
+
+
+
+ [ 244 ]
+
+
diff --git a/_docs/index.txt b/_docs/index.txt
new file mode 100644
index 0000000..6c46e46
--- /dev/null
+++ b/_docs/index.txt
@@ -0,0 +1,294 @@
+Index
+
+A
+Active Record 类
+ 关于 4.3
+ 优点 4.3.1
+ 自动机制 4.3.1.2
+ 查询,创建 4.3.4
+ 查询,删除 4.3.5
+ 查询,读取 4.3.2
+ 查询,更新 4.3.4
+ 查询结果,显示 4.3.3
+ 时间,节约 4.3.1.1
+ 默认查询风格 4.3.6
+数组
+ 关于 13.4
+ 参数 13.4 (下面)
+
+B
+基准测试类 8.4
+
+C
+图表类库
+ 关于 235
+ 比较 235-237
+类,CodeIgniter
+ 修改 181-183
+ 日期辅助函数 141
+ Inflector 辅助函数 141
+ 语言类 141
+ 文本辅助函数 141
+代码,测试
+ 端到端测试 112
+ 需要 111, 112
+ 单元测试 112
+ 用途 112
+CodeIgniter
+ 关于 12
+ 优点 7-11
+ config 文件,使用 24
+ 配置文件 24
+ 配置设置 51
+ 缺点 16, 17, 226
+ 下载 22
+ 文件结构 23
+ 文件类型 23
+ 通讯 127
+ 框架 12
+ 安装 22
+ 接口问题 171
+ 许可协议 18
+ 开源商业模式 15, 16
+ 准备知识 21
+ 要求 22
+ 设置 21
+ 语法规则 33
+ 测试 25
+ URL 辅助函数 46
+ 使用 7
+ 版本更改 177
+CodeIgniter 站点
+ 架构问题 73
+ 类的类型 34
+ 设计 85, 86
+ 文件类型 34-36
+ 浏览 86-90
+ 表结构 37
+配置设置,CodeIgniter
+ 配置文件 51
+ database 52
+ dbdriver 52
+ hostname 52
+ password 52
+ username 52
+控制器
+ 默认控制器 33
+ 设计 39-41
+ 参数,传给函数 40, 41
+ 语法规则 34
+ Welcome 控制器 31
+引用复制 103
+CRUD
+ 关于 185, 238
+ 数组 189
+ 概念 186
+ 数据,读取 195
+ 定义 189
+ 函数 192
+ insert 201
+ insert2 208
+ 参数 190
+ showall 192
+ 测试套件 209
+
+D
+数据库
+ 设计 53
+ 设置,为站点 63-66
+数据库表
+ 关于 189
+ 规则 189
+日期辅助函数
+ 关于 142
+ 日期格式 142
+ 日期格式,转换 143
+ 时区,生成 143, 144
+诊断工具 174
+Display 模型 78, 79
+dotest 模型
+ 关于 216
+ 数据库,读取从 221-223
+ 数据库,写入到 22-223
+ 参数,switch 语句 221
+ 报告 220
+ 表信息,从数据库查询生成 216
+ 测试循环 218, 219
+ 测试报告,打印 223, 224
+ 测试类型 221
+下载辅助函数
+ 关于 158
+ 数据库,下载为文本文件 158-160
+
+E
+Email 类
+ 使用 136-139
+错误处理类 113
+
+F
+文件辅助函数
+ 关于 156
+ 数组,比较 157
+ 文件,写入 156
+ 装载 156
+文件上传类
+ 关于 160
+ 装载 160
+ 使用 160-165
+表单辅助函数
+ 关于 74
+ 优点 74-77
+论坛
+ CodeIgniter 论坛 230
+ CodeIgniter Wiki 231
+框架
+ 关于 12
+ PHP 框架 13
+ Rails 13
+ Ruby on Rails 13
+FTP 类
+ 远程文件,测试 127-129
+
+I
+图像类
+ 关于 165
+ 裁剪,功能 166
+ 功能 166
+ 缩放,功能 166
+ 缩放 166, 167
+ 旋转,功能 166
+ 水印,功能 166
+Inflector 辅助函数 145
+insert 201
+insert2 208
+接口问题
+ 关于 172
+ CodeIgniter 版本更改 177
+ config 文件 173
+ 数据库 172
+ 诊断工具 174
+ OS 差异 173
+ PHP 版本差异 173
+ URL 172
+
+L
+语言类
+ 关于 146
+ 网页,翻译 146-149
+类库
+ 安全 92
+
+M
+菜单
+ 创建 48
+
+N
+嵌套视图
+ 关于 70
+ header 视图,创建 70-72
+ 参数 72
+
+O
+对象
+ 关于 99
+ 超级对象 100
+OOP
+ 关于 99
+ 对象 99
+
+P
+PHP 语法
+ 长语法 69
+ 短语法 69
+插件和类库
+ AJAX 包 233
+ 身份验证包 233, 234
+ 图表类库 235-237
+ CRUD 238
+ 外部站点 235
+ JavaScript 包 233
+分析器类 122
+
+Q
+查询,Active Record
+ 创建 59, 60
+ 删除 61
+ 读取 56, 57
+ 结果,显示 58
+ 更新 59, 60
+
+R
+远程文件
+ 测试,使用 FTP 类 127-129
+资源
+ CodeIgniter 论坛 230
+ CodeIgniter Wiki 231
+ MySQL 239
+ PHP 239
+ 视频教程 232
+ XAMPPLite 239
+Ruby on Rails 13
+
+S
+安全/Session
+ 类库 91, 92
+ Session,安全 94-96
+Session 类 91
+showall 192
+标准控制器 187
+超级对象
+ 代码,添加 105, 106
+ 缺点 106-108
+ 日志,跟踪 101
+ 使用 100-103
+
+T
+表格类
+ 关于 150
+ HTML 表格,编写 150-152
+ 页面,缓存 152, 153
+文本辅助函数
+ 关于 145
+ Inflector 辅助函数 145
+ 字符串,截断 145
+
+U
+单元测试类 115
+单元测试
+ 关于 112
+ 控制 124
+ 准则 117, 118
+ 范例 118-120
+ 模拟数据库,测试 123
+ 时间安排 124
+
+V
+验证类
+ 关于 79
+ 控制器,设置 81
+ 表单,设置 81, 82
+ 验证,设置 80
+版本更改,CodeIgniter
+ 类库,初始化 179
+ 模型,调用 178
+ 模型,装载 178
+ 更新 179-181
+视图
+ 设计 37, 39
+ 嵌套视图 70-73
+ 语法规则 34
+ 使用 32
+ 编写 67-69
+
+X
+XMLRPC
+ 关于 129
+ 调试 134, 135
+ 数据交换,格式化 132-134
+ 问题 135, 136
+ 服务器-客户端通讯 131, 132
+
+Z
+Zip 类
+ 关于 169
+ 文件,压缩 169
diff --git a/_docs/preface.txt b/_docs/preface.txt
new file mode 100644
index 0000000..3f47a30
--- /dev/null
+++ b/_docs/preface.txt
@@ -0,0 +1,226 @@
+Preface
+前言
+
+This book sets out to explain some of the main features of CI. It doesn't cover them
+all, or cover any of them in full detail. CI comes with an excellent on-line User Guide
+that explains most things. This is downloaded with the CI files.
+本书详细讲解了 CI 的一些主要特性。本书并不包含 CI 的所有内容和全部细节。CI 有一本出色的在线《用户指南》,它详细讲解了大多数的内容。它可以与 CI 一起下载。
+
+This book doesn't try to duplicate the User Guide. Instead it tries to make it easier for
+you to pick up how the CI framework works, so you can decide whether it is right
+for you, and start using it quickly.
+本书并不想重复《用户指南》中的内容。相反,本书试图让你轻松了解 CI 框架是如何工作的,那么,你可以先决定它是否对你有价值,然后再阅读本书。
+
+In some places, this book goes beyond the User Guide, though, when it tries to
+explain how CI works. (The User Guide is more practically oriented.) This means
+that there are some fairly theoretical chapters in between the "here's how" pages. I've
+found that it helps to understand what CI is doing under the hood; otherwise you
+sometimes get puzzling error messages that aren't easy to resolve.
+在试图解释 CI 是如何工作时,本书的某些内容已经超出了《用户指南》的范围。(《用户指南》更注重实际应用。)这意味着在“实战训练”中有一些非常理论化的章节。我发现这有助于理解 CI 内部的运行机制;否则,当你遇到令人费解的错误消息时就不容易解决。
+
+I've tried to use a 'real-world' example when showing sections of CI code. I want
+to show that CI can be used to develop a serious website with a serious purpose.
+I'm currently running several websites for clients, and I want a program that will
+monitor them, test them in ways I specify, keep a database of what it has done, and
+let me have reports when I want them.
+我尝试在显示 CI 代码段时使用一个“真实世界”的例子。我想展示的是,CI 可以用于开发一个正式的网站。目前,我手头上有几个正在运行的客户网站,我希望依照我指定的方式去对其进行检测控制以及测试,同时记录下程序操作行为,在我需要时我可以得到一份相关的报告。
+
+The examples in this book don't show it in full detail, of course: but they do, I hope,
+demonstrate that you can use CI to make pretty well any common coding simpler,
+and some uncommon stuff as well.
+本书中的范例无法将 CI 的功能一丝不漏的完全展示,但我想这些范例应当还是在一定程度上展现了 CI 在简化处理常用应用(以及一些非常用应用)上的能力。
+
+This book steps you through the main features of CodeIgniter in a systematic way,
+explaining them clearly with illustrative code examples.
+本书系统地讲解了 CodeIgniter 的主要特性,并配合相应的代码范例进行了详尽的解释,使你能够由浅入深地掌握 CodeIgniter。
+
+
+What This Book Covers
+本书内容概述
+
+Chapter 1 explains what CodeIgniter can do, the 'framework', and how CodeIgniter
+fits in. It further talks about the open-source business model and gives some
+disadvantages of CodeIgniter, at the end.
+第一章讲解了 CodeIgniter 能做什么?什么是“框架”?如何安装 CodeIgniter?还对开源商业模式进行了深层次讨论,并在结尾给出了 CodeIgniter 的一些不足。
+
+Chapter 2 explains what happens when you install the site, and which files will be
+created. It gives a detailed overview of the required software, and explains the basic
+configuration of CodeIgniter.
+第二章讲解了安装 CodeIgniter 会发生什么?会自动创建哪些文件?本章给出了安装 CodeIgniter 所需软件的详细说明,并讲解了如何对 CodeIgniter 进行基本的配置。
+
+Chapter 3 explains how MVC helps to organize a dynamic website. It goes further
+to explain the process by which CodeIgniter analyzes an incoming Internet request
+and decodes which part of your code will handle it. Then CodeIgniter syntax rules
+and the different types of files or classes you can find—or write for yourself—on a
+CodeIgniter site are explained. At the end of the chapter, some practical hints on site
+design are given.
+第三章讲解了 MVC 如何帮助组织一个动态网站。本章节更详细的阐述了当 CodeIgniter 收到 Internet 请求后,通过分析该请求并解析相关程序来执行的过程。然后介绍 CodeIgniter 的语法规则,并对 CodeIgniter 网站上你能找到的(或自己编写的)各种不同类型的文件或类进行了详细的解释。在本章的结尾部分,会告诉大家一些在网站设计中极具实用性的技巧和提示。
+
+Chapter 4 looks at how you set up a database to work with CodeIgniter, and then
+how you use the Active Record class to manipulate the database.
+第四章看看如何设置一个数据库来配合 CodeIgniter 的使用,还有如何使用 Active Record 类来操作数据库。
+
+Chapter 5 covers various ways of building views, how to create HTML forms quickly,
+and how to validate your forms using CodeIgniter's validation class.
+第五章包含了创建视图的各种方法,如何快速创建 HTML 表单,如何使用 CodeIgniter 的验证类验证你的表单。
+
+Chapter 6 looks at one of the basic questions affecting any website i.e. session
+management and security; we also explore CodeIgniter's session class.
+第六章看看一个对任何网站都有影响的基础问题,即 Session 管理和安全;同时我们也来探讨一下 CodeIgniter 的 Session 类。
+
+Chapter 7 covers the way in which CodeIgniter uses objects, and the different ways in
+which you can write and use your own objects.
+第七章涵盖了 CodeIgniter 使用对象的方式,以及你能够以各种不同的方式编写和使用你自己的对象。
+
+Chapter 8 covers CodeIgniter classes to help with testing: Unit tests, Benchmarking,
+the 'profiler' and ways in which CodeIgniter helps you to involve your database in
+tests without scrambling live data.
+第八章涵盖了 CodeIgniter 中用于测试的类:单元测试、基准测试和“分析器”,通过这些方法,CodeIgniter 可以帮你在不影响当前数据的情况下测试数据库。
+
+Chapter 9 looks at using CodeIgniter's FTP class and email class to make
+communication easier, and then we venture into Web 2.0 territory using XML-RPC.
+第九章讲述了通过使用 CodeIgniter 的 FTP 类和 Email 类来简化通讯,并使用 XML-RPC 从 Web 2.0 的世界中获取信息。
+
+Chapter 10 talks about CodeIgniter classes that help in overcoming problems arising
+regularly when you are building a website, for example, the date helper, the text and
+inflector helpers, the language class, and the table class.
+第十章讲解了一些能帮你解决建站常见问题的 CodeIgniter 类和类库,例如:日期辅助函数、文本和 Inflector 辅助函数、语言类和表格类。
+
+Chapter 11 looks at several useful CodeIgniter functions and helpers: file helper,
+download helper, file upload class, image manipulation class, and the ZIP class.
+第十一章讲解了一些有用的 CodeIgniter 功能和辅助函数:文件辅助函数、下载辅助函数、文件上传类、图像处理类和 ZIP 类。
+
+Chapter 12 covers exploring your config files, using diagnostic tools, and potential
+differences between servers, along with some notes on security.
+第十二章涵盖了探索你的 config 文件、使用诊断工具、服务器之间的潜在区别和一些与安全相关的提示。
+
+Chapter 13 shows you how to generalize CRUD operations so that you can do them
+with two classes: one for the controller, and one for the CRUD model.
+第十三章告诉你如何使 CRUD 操作一般化,你可以用两个类来实现:一个用于控制器,另一个用于 CRUD 模型。
+
+Chapter 14 looks at some coding examples, bringing together a lot of the functions
+that have been discussed bit by bit in the preceding chapters.
+第十四章讨论了一些代码范例,这些范例综合使用了大量之前章节中逐步讨论的功能。
+
+Chapter 15 looks at some of the resources available to you when you start to code
+with CodeIgniter, such as the libraries for AJAX and JavaScript, authentication,
+charting, and CRUD.
+第十五章介绍了一些在你编写 CodeIgniter 程序时会用到的资源,例如:AJAX 和 JavaScript 类库、身份验证、图表和 CRUD。
+
+
+What You Need for This Book
+阅读本书你需要什么?
+
+Throughout this book, we will assume that you have the following packages
+installed and available:
+本书中,我们假定你已经安装了下列软件包
+
+1. PHP 4.3.2 or above
+1. PHP 4.3.2 或以上版本
+2. A working web server
+2. 一个运行中的 Web 服务器
+3. One of MySQL, MySQLi, MS SQL, Postgre, Oracle, SQLite, ODBC
+3. MySQL、MySQLi、MS SQL、Postgre、Oracle、SQLite、ODBC 其中之一
+
+
+Conventions
+约定
+
+In this book, you will find a number of styles of text that distinguish between
+different kinds of information. Here are some examples of these styles, and an
+explanation of their meaning.
+在本书中,你会发现许多不同的文本样式以区别不同的信息类型。下面是一些文本样式的范例,以及他们的含义。
+
+There are three styles for code. Code words in text are shown as follows: "We can
+include other contexts through the use of the include directive."
+我们有三种文本样式。
+
+A block of code will be set as follows:
+一个代码块将显示为下列样式:
+ $active_group = "default";
+ $db['default']['hostname'] = "";
+ $db['default']['username'] = "";
+ $db['default']['password'] = "";
+
+When we wish to draw your attention to a particular part of a code block, the
+relevant lines or items will be made bold:
+当我们想请你注意某特定部分的代码块时,相关的行或内容将被加粗:
+
+
+
+
+
+
+
+New terms and important words are introduced in a bold-type font. Words that you
+see on the screen, in menus or dialog boxes for example, appear in our text like this:
+"clicking the Next button moves you to the next screen".
+新名词和重要文字采用粗体字。例如在屏幕上、菜单或对话框中,以我们的文本样式则显示为:“单击下一步按钮以转到下一个屏幕”。
+
+Warnings or important notes appear in a box like this.
+警告或重要提示将显示在这样的方框中。
+
+
+Reader Feedback
+读者反馈
+
+Feedback from our readers is always welcome. Let us know what you think about
+this book, what you liked or may have disliked. Reader feedback is important for us
+to develop titles that you really get the most out of.
+我们非常欢迎读者能够给我们反馈。让我们了解你对本书有什么想法,喜欢(或可能不喜欢)什么。读者反馈对我们今后的改进工作十分重要,你也会从中受益良多。
+
+To send us general feedback, simply drop an email to feedback@packtpub.com,
+making sure to mention the book title in the subject of your message.
+要给我们发送反馈,只需简单的写一封 Email 到 feedback@packtpub.com,不过要在邮件标题中写清书名。
+
+If there is a book that you need and would like to see us publish, please send
+us a note in the SUGGEST A TITLE form on www.packtpub.com or
+email suggest@packtpub.com.
+如果你需要我们出版一本书,请在 www.packtpub.com 网站中的 SUGGEST A TITLE 中给我们发信息,或发 Email 到 suggest@packtpub.com。
+
+If there is a topic that you have expertise in and you are interested in either writing
+or contributing to a book, see our author guide on www.packtpub.com/authors.
+如果有你精通的主题,且你对写作或对本书作出贡献感兴趣的话,请到 www.packtpub.com/authors 查看我们的作者指南。
+
+
+Customer Support
+客户支持
+
+Now that you are the proud owner of a Packt book, we have a number of things to
+help you to get the most from your purchase.
+现在,你是一本 Packt 书的“自豪”持有人,我们有很多事情可以帮助你,以便你从你的购买中得到最大的收获。
+
+
+Downloading the Example Code for the Book
+下载本书的范例代码
+
+Visit http://www.packtpub.com/support, and select this book from the list of titles
+to download any example code or extra resources for this book. The files available
+for download will then be displayed.
+访问 http://www.packtpub.com/support,并从书名列表中选择本书,以便下载本书的范例代码或附加资源。然后将显示可供下载的文件。
+
+The downloadable files contain instructions on how to use them.
+下载的文件中包含如何使用它们的说明。
+
+
+Errata
+勘误表
+
+Although we have taken every care to ensure the accuracy of our contents, mistakes
+do happen. If you find a mistake in one of our books—maybe a mistake in text or
+code—we would be grateful if you would report this to us. By doing this you can
+save other readers from frustration, and help to improve subsequent versions of
+this book. If you find any errata, report them by visiting http://www.packtpub.
+com/support, selecting your book, clicking on the Submit Errata link, and entering
+the details of your errata. Once your errata are verified, your submission will be
+accepted and the errata added to the list of existing errata. The existing errata can be
+viewed by selecting your title from http://www.packtpub.com/support.
+虽然我们尽全力保证内容的准确性,但还是会产生错误。如果你在我们的任何一本书中发现错误—也许是文字或代码错误—如果你报告给我们,我们将不胜感激。这样做可以使其他读者免受该错误的影响,并有助于改善本书的后续版本。如果你发现任何勘误,请通过访问 http://www.packtpub.com/support 来提交他们:选择你的书,单击 Submit Errata(提交勘误)链接,然后输入勘误的详细内容。一旦你的勘误通过审核,你提交的内容将被接受,并添加到已存在的勘误表中。你可以从 http://www.packtpub.com/support 通过选择你的书名来查看已存在的勘误表。
+
+
+Questions
+问题
+
+You can contact us at questions@packtpub.com if you are having a problem with
+some aspect of the book, and we will do our best to address it.
+如果你有任何关于书籍方面的问题,你可以通过 questions@packtpub.com 联系我们,我们会尽最大的努力来解决它。
\ No newline at end of file
diff --git a/_images_source/13.5.1.1_1.png b/_images_source/13.5.1.1_1.png
new file mode 100644
index 0000000..64f5053
Binary files /dev/null and b/_images_source/13.5.1.1_1.png differ
diff --git a/_images_source/13.5.1.2_1.png b/_images_source/13.5.1.2_1.png
new file mode 100644
index 0000000..eed1634
Binary files /dev/null and b/_images_source/13.5.1.2_1.png differ
diff --git a/_images_source/3.2-1.png b/_images_source/3.2-1.png
new file mode 100644
index 0000000..4413c19
Binary files /dev/null and b/_images_source/3.2-1.png differ
diff --git a/_images_source/3.8.4-1.png b/_images_source/3.8.4-1.png
new file mode 100644
index 0000000..4cf9991
Binary files /dev/null and b/_images_source/3.8.4-1.png differ
diff --git a/_images_source/CodeIgniter-01.jpg b/_images_source/CodeIgniter-01.jpg
new file mode 100644
index 0000000..483ed58
Binary files /dev/null and b/_images_source/CodeIgniter-01.jpg differ
diff --git a/_images_source/CodeIgniter-02.jpg b/_images_source/CodeIgniter-02.jpg
new file mode 100644
index 0000000..9893103
Binary files /dev/null and b/_images_source/CodeIgniter-02.jpg differ
diff --git a/_images_source/CodeIgniter-03.png b/_images_source/CodeIgniter-03.png
new file mode 100644
index 0000000..5535b84
Binary files /dev/null and b/_images_source/CodeIgniter-03.png differ
diff --git a/_images_source/CodeIgniter-04.png b/_images_source/CodeIgniter-04.png
new file mode 100644
index 0000000..31b15d4
Binary files /dev/null and b/_images_source/CodeIgniter-04.png differ
diff --git a/_images_source/CodeIgniter-05.png b/_images_source/CodeIgniter-05.png
new file mode 100644
index 0000000..f8e84f0
Binary files /dev/null and b/_images_source/CodeIgniter-05.png differ
diff --git a/_images_source/CodeIgniter-06.png b/_images_source/CodeIgniter-06.png
new file mode 100644
index 0000000..2bf62d9
Binary files /dev/null and b/_images_source/CodeIgniter-06.png differ
diff --git a/_images_source/CodeIgniter-07.png b/_images_source/CodeIgniter-07.png
new file mode 100644
index 0000000..5d80af8
Binary files /dev/null and b/_images_source/CodeIgniter-07.png differ
diff --git a/_images_source/CodeIgniter-08.png b/_images_source/CodeIgniter-08.png
new file mode 100644
index 0000000..db9f019
Binary files /dev/null and b/_images_source/CodeIgniter-08.png differ
diff --git a/_images_source/CodeIgniter-09.png b/_images_source/CodeIgniter-09.png
new file mode 100644
index 0000000..3ca328c
Binary files /dev/null and b/_images_source/CodeIgniter-09.png differ
diff --git a/_images_source/CodeIgniter-10.png b/_images_source/CodeIgniter-10.png
new file mode 100644
index 0000000..0aa69c7
Binary files /dev/null and b/_images_source/CodeIgniter-10.png differ
diff --git a/_images_source/CodeIgniter-11.png b/_images_source/CodeIgniter-11.png
new file mode 100644
index 0000000..1fdcb27
Binary files /dev/null and b/_images_source/CodeIgniter-11.png differ
diff --git a/_images_source/CodeIgniter-12.png b/_images_source/CodeIgniter-12.png
new file mode 100644
index 0000000..4b63204
Binary files /dev/null and b/_images_source/CodeIgniter-12.png differ
diff --git a/_images_source/CodeIgniter-13.png b/_images_source/CodeIgniter-13.png
new file mode 100644
index 0000000..81cd5ac
Binary files /dev/null and b/_images_source/CodeIgniter-13.png differ
diff --git a/_images_source/CodeIgniter-14.png b/_images_source/CodeIgniter-14.png
new file mode 100644
index 0000000..c26ad43
Binary files /dev/null and b/_images_source/CodeIgniter-14.png differ
diff --git a/_images_source/CodeIgniter-15.png b/_images_source/CodeIgniter-15.png
new file mode 100644
index 0000000..7bf747c
Binary files /dev/null and b/_images_source/CodeIgniter-15.png differ
diff --git a/_images_source/CodeIgniter-16.png b/_images_source/CodeIgniter-16.png
new file mode 100644
index 0000000..e8f8b38
Binary files /dev/null and b/_images_source/CodeIgniter-16.png differ
diff --git a/_images_source/CodeIgniter-17.png b/_images_source/CodeIgniter-17.png
new file mode 100644
index 0000000..d000c5d
Binary files /dev/null and b/_images_source/CodeIgniter-17.png differ
diff --git a/_images_source/CodeIgniter-18.png b/_images_source/CodeIgniter-18.png
new file mode 100644
index 0000000..c8e3973
Binary files /dev/null and b/_images_source/CodeIgniter-18.png differ
diff --git a/_images_source/CodeIgniter-19.png b/_images_source/CodeIgniter-19.png
new file mode 100644
index 0000000..bb9ccc4
Binary files /dev/null and b/_images_source/CodeIgniter-19.png differ
diff --git a/_images_source/CodeIgniter-20.png b/_images_source/CodeIgniter-20.png
new file mode 100644
index 0000000..fa7d782
Binary files /dev/null and b/_images_source/CodeIgniter-20.png differ
diff --git a/_images_source/CodeIgniter-21.jpg b/_images_source/CodeIgniter-21.jpg
new file mode 100644
index 0000000..68dd10d
Binary files /dev/null and b/_images_source/CodeIgniter-21.jpg differ
diff --git a/_images_source/CodeIgniter-22.jpg b/_images_source/CodeIgniter-22.jpg
new file mode 100644
index 0000000..6ba2a16
Binary files /dev/null and b/_images_source/CodeIgniter-22.jpg differ
diff --git a/_images_source/CodeIgniter-23.jpg b/_images_source/CodeIgniter-23.jpg
new file mode 100644
index 0000000..7233006
Binary files /dev/null and b/_images_source/CodeIgniter-23.jpg differ
diff --git a/_images_source/CodeIgniter-24.png b/_images_source/CodeIgniter-24.png
new file mode 100644
index 0000000..f6913e4
Binary files /dev/null and b/_images_source/CodeIgniter-24.png differ
diff --git a/_images_source/CodeIgniter-25.png b/_images_source/CodeIgniter-25.png
new file mode 100644
index 0000000..e5b74f2
Binary files /dev/null and b/_images_source/CodeIgniter-25.png differ
diff --git a/_images_source/CodeIgniter-26.png b/_images_source/CodeIgniter-26.png
new file mode 100644
index 0000000..6ff3f4a
Binary files /dev/null and b/_images_source/CodeIgniter-26.png differ
diff --git a/_images_source/CodeIgniter-27.png b/_images_source/CodeIgniter-27.png
new file mode 100644
index 0000000..03e6937
Binary files /dev/null and b/_images_source/CodeIgniter-27.png differ
diff --git a/_images_source/CodeIgniter-28.png b/_images_source/CodeIgniter-28.png
new file mode 100644
index 0000000..70e6ce5
Binary files /dev/null and b/_images_source/CodeIgniter-28.png differ
diff --git a/_images_source/CodeIgniter-29.png b/_images_source/CodeIgniter-29.png
new file mode 100644
index 0000000..8a5c323
Binary files /dev/null and b/_images_source/CodeIgniter-29.png differ
diff --git a/_images_source/CodeIgniter-30.png b/_images_source/CodeIgniter-30.png
new file mode 100644
index 0000000..4006fcc
Binary files /dev/null and b/_images_source/CodeIgniter-30.png differ
diff --git a/_images_source/CodeIgniter-31.png b/_images_source/CodeIgniter-31.png
new file mode 100644
index 0000000..f1d4569
Binary files /dev/null and b/_images_source/CodeIgniter-31.png differ
diff --git a/_images_source/CodeIgniter-32.png b/_images_source/CodeIgniter-32.png
new file mode 100644
index 0000000..b521249
Binary files /dev/null and b/_images_source/CodeIgniter-32.png differ
diff --git a/_images_source/CodeIgniter-33.png b/_images_source/CodeIgniter-33.png
new file mode 100644
index 0000000..3da077a
Binary files /dev/null and b/_images_source/CodeIgniter-33.png differ
diff --git a/_images_source/CodeIgniter-34.png b/_images_source/CodeIgniter-34.png
new file mode 100644
index 0000000..d95896e
Binary files /dev/null and b/_images_source/CodeIgniter-34.png differ
diff --git a/_images_source/CodeIgniter-35.png b/_images_source/CodeIgniter-35.png
new file mode 100644
index 0000000..4bb89ef
Binary files /dev/null and b/_images_source/CodeIgniter-35.png differ
diff --git a/_images_source/CodeIgniter-36.png b/_images_source/CodeIgniter-36.png
new file mode 100644
index 0000000..49c7f04
Binary files /dev/null and b/_images_source/CodeIgniter-36.png differ
diff --git a/_images_source/CodeIgniter-37.png b/_images_source/CodeIgniter-37.png
new file mode 100644
index 0000000..cecd371
Binary files /dev/null and b/_images_source/CodeIgniter-37.png differ
diff --git a/_images_source/CodeIgniter-38.png b/_images_source/CodeIgniter-38.png
new file mode 100644
index 0000000..553e327
Binary files /dev/null and b/_images_source/CodeIgniter-38.png differ
diff --git a/_images_source/CodeIgniter-39.png b/_images_source/CodeIgniter-39.png
new file mode 100644
index 0000000..27c415c
Binary files /dev/null and b/_images_source/CodeIgniter-39.png differ
diff --git a/_images_source/CodeIgniter-40.png b/_images_source/CodeIgniter-40.png
new file mode 100644
index 0000000..5f9a065
Binary files /dev/null and b/_images_source/CodeIgniter-40.png differ
diff --git a/_images_source/CodeIgniter_cover.png b/_images_source/CodeIgniter_cover.png
new file mode 100644
index 0000000..574b1d3
Binary files /dev/null and b/_images_source/CodeIgniter_cover.png differ
diff --git a/_images_source/header.png b/_images_source/header.png
new file mode 100644
index 0000000..06e263d
Binary files /dev/null and b/_images_source/header.png differ
diff --git a/_other/template.html b/_other/template.html
new file mode 100644
index 0000000..d261506
--- /dev/null
+++ b/_other/template.html
@@ -0,0 +1,30 @@
+
+
+
+
+模板
+
+
+
+
+
David Upton 是一家专业管理咨询公司的董事,公司总部设在伦敦,但工作在世界各地。他的客户包括一些世界上最大的公司。他对他的基于 Web 的工作越来越感兴趣了,并努力寻求最简便易行的把思想转化为健壮的专业应用的途径。他至今已为英国的两家重要的公司开发了应用。他还有一个兴趣是模拟技术,这个占用了他很多写博客的时间和思维。
+
+
我要感谢 Rick Ellis 编写并免费提供了 CI。这种对智慧成果的奉献精神创造了开源运动的一个成功案例,也为我们做出了榜样。
+
我还要感谢 Rick 和 Derek Allard,他们对这本书进行了技术复审,并提出了很多宝贵建议。
+
在很多周六夜晚举办的“聚会”上,Mark Barker 启发并帮助我理解面向对象的思想。
+
最后,但并非最不重要的,我要感谢 Julia、John 和 James 给我的爱、支持和耐心。
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/about_the_reviewers.html b/about_the_reviewers.html
new file mode 100644
index 0000000..4461d57
--- /dev/null
+++ b/about_the_reviewers.html
@@ -0,0 +1,29 @@
+
+
+
+
+关于审稿人
+
+
+
+
Rick Ellis 是 EllisLab.com 的创始人和 CEO,该公司开发了 CodeIgniter 和其他几个广泛使用的 Web 应用程序。Rick Ellis 有着丰富的媒体技术背景,曾在迪斯尼的一个交互项目中担任设计和技术执行的职能,为 Oliver Stone 制作专题片,以及完成其中几乎所有的基于 Web 的工作。
+
Derek Allard 是一名程序员、作家和广受好评的讲师,他居住在加拿大多伦多市。他编写 Web 应用程序,并且他是一个 Web 标准和可访问性的支持者,他是一个喜欢脚本和数据库的年轻人,也是一个 PHP 的狂热分子。他是一名很受欢迎的教师和自由作家,Derek 把他工作的大部分时间都花在了 XHTML、PHP、XML 和 JavaScript 上。
+
+
\ No newline at end of file
diff --git a/build.bat b/build.bat
new file mode 100644
index 0000000..9534686
--- /dev/null
+++ b/build.bat
@@ -0,0 +1,2 @@
+@ECHO OFF
+hhc codeigniter
\ No newline at end of file
diff --git a/credits.html b/credits.html
new file mode 100644
index 0000000..7f994b3
--- /dev/null
+++ b/credits.html
@@ -0,0 +1,34 @@
+
+
+
+
+制作人员名单
+
+
+
+
爽快地答应 Hex 为本书作序,目的很简单,推荐读者马上在自己的 PC 上安装 CI,并且遵照《20 分钟创建一个博客》的教程体验这套框架,看看你是不是真的也很喜欢它。
+
我在 95 年第一次接触 Web 开发,从制作 GIF 动画,到现在管理近万台的服务器集群,各种开发语言或多或少地接触过。虽然听说 CI 至今不超过两个月,也还没有通读过其代码,却在几天的文档阅读中数次感叹他巧妙的设计。我希望在今后的产品开发中使用它,原因很简单,因为有一套简单高效,能够协助你快速开发的框架很重要。它能有效地帮助你降低开发和未来的维护成本。