如何使用PHP的codeigniter验证码框架来编写API接口

&?require(APPPATH'.libraries/REST_Controller.php');class Example_api extends REST_Controller {}?&&  接着我们看其中两个获得资源的方法,分别是user_get()和users_get(),其中user_get()是根据用户id去获得某个用户的信息,而users_get()则获得多个用户的信息,代码框架如下:&?phprequire(APPPATH'.libraries/REST_Controller.php');class Example_api extends REST_Controller {&&&&function user_get()&&&&{&&&&&&&&// 获得一个用户的信息&&&&}&&&&function users_get()&&&&{&&&&&&&&//获得多个用户的信息&&&&}}?&  接着我们复习下在RESTful架构中的四类关于资源的操作。  GET:使用GET去获得一个已经存在的资源的信息。通常我们在浏览器中输入url其实即发出了一个GET的请求。  POST:使用POST去更新一个已经存在的资源。比如表单中的提交数据行为,都属于POST类型。  PUT:使用HTTP的报文头的PUT,可以去新建一种资源,目前不是所有浏览器支持,所以本文不作讨论。  DELETE:使用DELETE去删除一种资源,同样目前不是所有浏览器都支持。  现在我们可以根据四种HTTP RESTful语义去形成如下框架:&?require(APPPATH'.libraries/REST_Controller.php');class Example_api extends REST_Controller {&&&&function user_get()&&&&{&&&&// 获得一个用户的信息&&&&}&&&&function user_put()&&&&{&&&&&&&&// 创建一个新用户&&&&}&&&&function user_post()&&&&{&&&&&&&&//更新用户信息&&&&}&&&&function user_delete()&&&&{&&&&&&&&//删除用户信息&&&&}}?&&  接下来我们充分利用CodeIgniter框架自身的REST_Controller的优势,完善为如下代码: &?phprequire(APPPATH'.libraries/REST_Controller.php');class Example_api extends REST_Controller {&&&&function user_get()&&&&{&&&&&&&&$data = array('returned: '. $this-&get('id'));&&&&&&&&$this-&response($data);&&&&}&&&&function user_post()&&&&{&&&&&&&&$data = array('returned: '. $this-&post('id'));&&&&&&&&$this-&response($data);&&&&}&&&&function user_put()&&&&{&&&&&&&&$data = array('returned: '. $this-&put('id'));&&&&&&&&$this-&response($data;&&&&}&&&&function user_delete()&&&&{&&&&&&&&$data = array('returned: '. $this-&delete('id'));&&&&&&&&$this-&response($data);&&&&}}?&&  以上代码中包含了如下几个片段,下面逐一讲解:  $this-&get() &&& 其中,使用这个从形如index.php/example_api/user?id=1或者如index.php/example_api/user/id/1的连接中获得资源,比如这里就获得其id的值,然后在数组中返回该id对应的数值。  $this-&post()&&& 其实是CodeIgniter中调用了其框架的$this-&input-&post()方法,进行提交操作,并且利用了XSS保护特性。  $this-&put()&&& 取curl中提交的或者HTTP协议头的PUT参数的内容。  $this-&delete()&&& 取curl中提交的或者HTTP协议头的delete参数的内容。  $this-&response()&&& 个方法中,主要是将处理的数据返回给浏览器,你可以指定一个HTTP状态码去表示该次返回结果的状态,比如在数据库中找不到某个记录,可以使用如$this-&response(array(&error& =& &User not found.&)去返回结果。  步骤4 与现有应用整合  在下载的示例程序中,我们刚才讲解了重要的部分,接下来讲解如何将下载程序中的关键类与现有的应用整合。下载的应用中,结构如下图:  其实只需要把rest.php和libraries目录下的REST_Controller.php复制到你的应用中的相应位置即可。这里假设要从你的应用的后端实体模型中进行相关操作,并将操作发布成RESTful的webservice,因此修改代码如下:&?phprequire(APPPATH.'/libraries/REST_Controller.php');class Api extends REST_Controller{&&&&function user_get()&&&&{&&&&&&&&if(!$this-&get('id'))&&&&&&&&{&&&&&&&&&&&&$this-&response(NULL, <span style="color: #0);&&&&&&&&}&&&&&&&&$user = $this-&user_model-&get( $this-&get('id') );&&&&&&&&if($user)&&&&&&&&{&&&&&&&&&&&&$this-&response($user, <span style="color: #0); // 200 being the HTTP response code&&&&&&&&}&&&&&&&&else&&&&&&&&{&&&&&&&&&&&&$this-&response(NULL, <span style="color: #4);&&&&&&&&}&&&&}&&&&function user_post()&&&&{&&&&&&&&$result = $this-&user_model-&update( $this-&post('id'), array(&&&&&&&&&&&&'name' =& $this-&post('name'),&&&&&&&&&&&&'email' =& $this-&post('email')&&&&&&&&));&&&&&&&&if($result === FALSE)&&&&&&&&{&&&&&&&&&&&&$this-&response(array('status' =& 'failed'));&&&&&&&&}&&&&&&&&else&&&&&&&&{&&&&&&&&&&&&$this-&response(array('status' =& 'success'));&&&&&&&&}&&&&}&&&&function users_get()&&&&{&&&&&&&&$users = $this-&user_model-&get_all();&&&&&&&&if($users)&&&&&&&&{&&&&&&&&&&&&$this-&response($users, <span style="color: #0);&&&&&&&&}&&&&&&&&else&&&&&&&&{&&&&&&&&&&&&$this-&response(NULL, <span style="color: #4);&&&&&&&&}&&&&}}?&&  以上的代码段其实之前都已经介绍过,只不过这次在每个方法中,读取的是后端数据库中的实体类而已。  步骤5 保护RESTful API  为了保护RESTful API,可以在application/config/rest.php中设置安全保护级别,如下所示:$config['rest_auth'] = 'basic';&  其中保护级别有如下设置:  None:任何人都可以访问这个API  BASIC:这个设置中,只提供基本的验证方式,适合在内部网络使用  Digest:使用用户名和密码进行验证,比如:$config['rest_valid_logins'] = array('admin' =& '1234');  第二部分 调用RESTful服务  接下来,我们讲解如何调用RESTful服务,因为RESTful服务是通过HTTP协议进行发布服务,因此有多种方法进行调用。  1) 通过file_get_contents()调用  可以通过PHP中最简单的file_get_contents()去调用RESTful,比如:$user = json_decode(file_get_contents('/index.php/api/user/id/1/format/json'));echo $user;  要注意的是,要是访问一个受密码保护的RESTful的话,需要用如下形式访问:$user = json_decode(&&&&file_get_contents('http://admin:/index.php/api/user/id/1/format/json'));echo $user-&  2)使用cUrl访问RESTful  使用php中的cUrl去访问RESTful服务时最方便稳定的,下面的代码指导如何使用cUrl去设置HTTP协议头,去更新一个指定用户的信息,如下代码所示:&&&&function native_curl($new_name, $new_email)&&&&{&&&&&&&&$username = 'admin';&&&&&&&&$password = '<span style="color: #34';&&&&&&&&// Alternative JSON version&&&&&&&&// $url = '/statuses/update.json';&&&&&&&&// Set up and execute the curl process&&&&&&&&$curl_handle = curl_init();&&&&&&&&curl_setopt($curl_handle, CURLOPT_URL, 'http://localhost/restserver/index.php/example_api/user/id/1/format/json');&&&&&&&&curl_setopt($curl_handle, CURLOPT_RETURNTRANSFER, <span style="color: #);&&&&&&&&curl_setopt($curl_handle, CURLOPT_POST, <span style="color: #);&&&&&&&&curl_setopt($curl_handle, CURLOPT_POSTFIELDS, array(&&&&&&&&&&&&'name' =& $new_name,&&&&&&&&&&&&'email' =& $new_email&&&&&&&&));&&&&&&&&//本行可选,如果你的RESTful API是开放的,则请删除该行&&&&&&&&curl_setopt($curl_handle, CURLOPT_USERPWD, $username . ':' . $password);&&&&&&&&$buffer = curl_exec($curl_handle);&&&&&&&&curl_close($curl_handle);&&&&&&&&$result = json_decode($buffer);&&&&&&&&if(isset($result-&status) && $result-&status == 'success')&&&&&&&&{&&&&&&&&&&&&echo 'User has been updated.';&&&&&&&&}&&&&&&&&else&&&&&&&&{&&&&&&&&&&&&echo 'Something has gone wrong';&&&&&&&&}&&&&}&  用过PHP的cUrl的朋友应该知道,这里其实是使用cUrl对我们已经发布的RESTful地址进行访问,并提交相关要更新的参数。但是,强大的CodeIgniter为我们封装了更强大的cUrl类库cUrl library(/wiki/Curl_library/),上面的代码可以简化如下:function ci_curl($new_name, $new_email)&&&&{&&&&&&&&$username = 'admin';&&&&&&&&$password = '<span style="color: #34';&&&&&&&&$this-&load-&library('curl');&&&&$this-&curl-&create('http://localhost/restserver/index.php/example_api/user/id/1/format/json');&&&&&&&&//本行可选,如果你的RESTful API是开放的,则请删除该行&&&&&&&&$this-&curl-&http_login($username, $password);&&&&&&&&$this-&curl-&post(array(&&&&&&&&&&&&'name' =& $new_name,&&&&&&&&&&&&'email' =& $new_email&&&&&&&&));&&&&&&&&$result = json_decode($this-&curl-&execute());&&&&&&&&if(isset($result-&status) && $result-&status == 'success')&&&&&&&&{&&&&&&&&&&&&echo 'User has been updated.';&&&&&&&&}&&&&&&&&else&&&&&&&&{&&&&&&&&&&&&echo 'Something has gone wrong';&&&&&&&&}&&&&}&  3)使用REST Client类库访问  第三种方法是可以使用开源的REST Client类库()去访问REST,比如同样上面的代码,可以写成:&&&&function rest_client_example($id)&&&&{&&&&&&&&$this-&load-&library('rest', array(&&&&&&&&&&&&'server' =& 'http://localhost/restserver/index.php/example_api/',&&&&&&&&&&&&'http_user' =& 'admin',&&&&&&&&&&&&'http_pass' =& '<span style="color: #34',&&&&&&&&&&&&'http_auth' =& 'basic' // 或者使用'digest'&&&&&&&&));&&&&&&&&$user = $this-&rest-&get('user', array('id' =& $id), 'json');&&&&&&&&echo $user-&&&&&}  看,是不是更简单了!这里是示例说明了调用GET方法,并且说明返回的形式是JSON的,当然你也可以指定其他形式的返回结果,比如xml,php,csv等,比如: $user = $this-&rest-&get('user', array('id' =& $id), 'application/json');  同理,可以使用$this-&rest-&post(),$this-&rest-&put(),$this-&rest-&delete()等。  最后,我们学习下如何跟twitter的RESTful API打交道,使用RESTful Client library,只需要如下这样的编写简单代码即可:&&&&&&&&&&$this-&load-&library('rest', array('server' =& '/'));&&&&&&&&$user = $this-&rest-&get('users/show', array('screen_name' =& 'philsturgeon'));&  这个是调用twitter的RESTful API去获得某个用户ID的资料,&&&&&&&&$this-&load-&library('rest', array(&&&&&&&&&&&&'server' =& '/',&&&&&&&&&&&&'http_user' =& 'username',&&&&&&&&&&&&'http_pass' =& 'password',&&&&&&&&&&&&'http_auth' =& 'basic'&&&&&&&&));&&&&&&&&$user = $this-&rest-&post('statuses/update.json', array('status' =& 'Using the REST client to do stuff'));&  这个代码段则是更新某个用户的状态。
本文欢迎转载,转载请注明:转载自IT168: []
正在努力加载文档,请稍等…
点击或扫描下载
点击或扫描下载
点击或扫描下载CodeIgniter框架中关于DB事务处理的设计缺陷
在我们线上的某个业务中,使用较老版本的CodeIgniter框架,其中的DB类中,对DB事物处理部分存在着一个设计上的缺陷,或许也算不上缺陷吧。但他却影响了我们生产环境,导致连锁反应。对业务产生较大影响,且不容易排查。这个问题,我在今年的3月中旬,曾向的站长 报告过,之后,我也忘记这件事情了。直到今天,我们线上业务又一次以为这个问题,害的我又排查一次。具体原因,各位且先听我慢慢说完。(这个问题同样存在于最新版本Version 2.1.0中)
以CodeIgniter框架Version 2.1.0为例,在system\database\DB_driver.php的CI_DB_driver类中第58行有个$_trans_status属性。
//system\database\DB_driver.php
var $trans_strict = TRUE;
var $_trans_depth = 0;
var $_trans_status = TRUE; // Used with transactions to determine if a rollback should occur
var $cache_on
同时,这个类的query方法中,有赋值此属性的代码,见文件306、307行
// This will trigger a rollback if transactions are being used
$this-&_trans_status = FALSE;
这里也给了注释,告诉我们,如果使用了事物处理,那么这属性将成为一个回滚的决定条件。
在520行的事物提交方法trans_complete中,如下代码
* Complete Transaction
* @access public
* @return bool
function trans_complete()
if ( ! $this-&trans_enabled)
return FALSE;
// When transactions are nested we only begin/commit/rollback the outermost ones
if ($this-&_trans_depth & 1)
$this-&_trans_depth -= 1;
return TRUE;
// The query() function will set this flag to FALSE in the event that a query failed
if ($this-&_trans_status === FALSE)
$this-&trans_rollback();
// If we are NOT running in strict mode, we will reset
// the _trans_status flag so that subsequent groups of transactions
// will be permitted.
if ($this-&trans_strict === FALSE)
$this-&_trans_status = TRUE;
log_message('debug', 'DB Transaction Failure');
return FALSE;
$this-&trans_commit();
return TRUE;
在535行中,如果_trans_status属性如果是false,那么将发生回滚,并且返回false。
在我们的业务代码中,由于程序员疏忽,没有判断trans_complete()方法是否正确执行,直接告诉用户操作成功,但实际上,程序已经向DB下达回滚指令,并未成功更新DB记录。当用户执行下一步操作时,程序又发现相应记录并未更新,又提醒用户上个操作没有完成,通知用户重新执行。如此反复&#8230;
CodeIgniter框架的设计缺陷
排查的过程,也是挺有意思的,起初从PHP代码中,总是不能确定问题所在,并没有把焦点放到trans_complete()方法的返回上。直到后来strace抓包分析,才知道是因为此属性而导致了回滚。
22:54:08.380085 write(9, &_&#92;&#48;&#92;&#48;&#92;&#48;\3UPDATE `cfc4n_user_info` SET `cfc4n_user_lock` = 1\nWHERE `cfc4n_user_id` = \'6154\'\nAND `cfc4n_user_lock` = 0&, 99) = 99
//执行更新命令
22:54:08.380089 read(9, &:&#92;&#48;&#92;&#48;\1\377\36\4#42S22Unknown column \'cfc4n_user_lock\' in \'where clause\'&, 16384) = 62
//不存在字段,SQL执行错误
22:54:08.381791 write(9, &\21&#92;&#48;&#92;&#48;&#92;&#48;\3SET AUTOCOMMIT=0&, 21) = 21
//禁止自动提交
22:54:08.381891 read(9, &\7&#92;&#48;&#92;&#48;\1&#92;&#48;&#92;&#48;&#92;&#48;&#92;&#48;&#92;&#48;&#92;&#48;&#92;&#48;&, 16384) = 11
22:54:08.382186 poll([{fd=9, events=POLLIN|POLLPRI}], 1, 0) = 0
22:54:08.382258 write(9, &\v&#92;&#48;&#92;&#48;&#92;&#48;\2jv01_roles&, 15) = 15
22:54:08.382343 read(9, &\7&#92;&#48;&#92;&#48;\1&#92;&#48;&#92;&#48;&#92;&#48;&#92;&#48;&#92;&#48;&#92;&#48;&#92;&#48;&, 16384) = 11
22:54:08.382631 poll([{fd=9, events=POLLIN|POLLPRI}], 1, 0) = 0
22:54:08.382703 write(9, &\22&#92;&#48;&#92;&#48;&#92;&#48;\3START TRANSACTION&, 22) = 22
//开始事务处理
22:54:08.401954 write(9, &\v&#92;&#48;&#92;&#48;&#92;&#48;\2database_demo&, 15) = 15
22:54:08.402043 read(9, &\7&#92;&#48;&#92;&#48;\1&#92;&#48;&#92;&#48;&#92;&#48;\1&#92;&#48;\1&#92;&#48;&, 16384) = 11
22:54:08.417773 write(9, &\v&#92;&#48;&#92;&#48;&#92;&#48;\2database_demo&, 15) = 15
22:54:08.417872 read(9, &\7&#92;&#48;&#92;&#48;\1&#92;&#48;&#92;&#48;&#92;&#48;\1&#92;&#48;&#92;&#48;&#92;&#48;&, 16384) = 11
22:54:08.418256 write(9, &[&#92;&#48;&#92;&#48;&#92;&#48;\3UPDATE `cfc4n_user_info` SET `silver` = CAST( silver + (5) as signed )\nWHERE `cfc4n_user_id` = \'6154\'&, 95) = 95
//执行其他SQL语句
22:54:08.418363 read(9, &0&#92;&#48;&#92;&#48;\1&#92;&#48;\1&#92;&#48;\1&#92;&#48;&#92;&#48;&#92;&#48;(Rows matched: 1
Changed: 1
Warnings: 0&, 16384) = 52
//成功更新,影响条数1.
22:54:08.430212 write(9, &\v&#92;&#48;&#92;&#48;&#92;&#48;\2database_demo&, 15) = 15
22:54:08.430314 read(9, &\7&#92;&#48;&#92;&#48;\1&#92;&#48;&#92;&#48;&#92;&#48;\1&#92;&#48;&#92;&#48;&#92;&#48;&, 16384) = 11
22:54:08.430698 write(9, &B&#92;&#48;&#92;&#48;&#92;&#48;\3UPDATE `cfc4n_user_info` SET `exp` = exp + 26\nWHERE `cfc4n_user_id` = \'6154\'&, 70) = 70
//执行其他SQK语句
22:54:08.430814 read(9, &0&#92;&#48;&#92;&#48;\1&#92;&#48;\1&#92;&#48;\1&#92;&#48;&#92;&#48;&#92;&#48;(Rows matched: 1
Changed: 1
Warnings: 0&, 16384) = 52
//成功更新,影响条数1.
22:54:08.432130 write(9, &\v&#92;&#48;&#92;&#48;&#92;&#48;\2database_demo&, 15) = 15
22:54:08.432231 read(9, &\7&#92;&#48;&#92;&#48;\1&#92;&#48;&#92;&#48;&#92;&#48;\1&#92;&#48;&#92;&#48;&#92;&#48;&, 16384) = 11
22:54:08.432602 write(9, &\244&#92;&#48;&#92;&#48;&#92;&#48;\3UPDATE `cfc4n_user_quest` SET `rew` = 1, `retable` = retable + 1, `re_time` = \nWHERE `cfc4n_user_id` = \'6154\'\nAND `quest_id` = \'300001\'\nAND `rew` = 0&, 168) = 168
//执行其他SQK语句
22:54:08.432743 read(9, &0&#92;&#48;&#92;&#48;\1&#92;&#48;\1&#92;&#48;\1&#92;&#48;&#92;&#48;&#92;&#48;(Rows matched: 1
Changed: 1
Warnings: 0&, 16384) = 52
//成功更新,影响条数1.
22:54:08.433517 write(9, &\v&#92;&#48;&#92;&#48;&#92;&#48;\2database_demo&, 15) = 15
22:54:08.433620 read(9, &\7&#92;&#48;&#92;&#48;\1&#92;&#48;&#92;&#48;&#92;&#48;\1&#92;&#48;&#92;&#48;&#92;&#48;&, 16384) = 11
22:54:08.433954 write(9, &\t&#92;&#48;&#92;&#48;&#92;&#48;\3ROLLBACK&, 13) = 13
//回滚事务 #注意看这里
22:54:08.434041 read(9, &\7&#92;&#48;&#92;&#48;\1&#92;&#48;&#92;&#48;&#92;&#48;&#92;&#48;&#92;&#48;&#92;&#48;&#92;&#48;&, 16384) = 11
22:54:08.434914 write(9, &\v&#92;&#48;&#92;&#48;&#92;&#48;\2database_demo&, 15) = 15
22:54:08.434999 read(9, &\7&#92;&#48;&#92;&#48;\1&#92;&#48;&#92;&#48;&#92;&#48;&#92;&#48;&#92;&#48;&#92;&#48;&#92;&#48;&, 16384) = 11
22:54:08.435342 write(9, &\21&#92;&#48;&#92;&#48;&#92;&#48;\3SET AUTOCOMMIT=1&, 21) = 21
//恢复自动提交
22:54:08.435430 read(9, &\7&#92;&#48;&#92;&#48;\1&#92;&#48;&#92;&#48;&#92;&#48;\2&#92;&#48;&#92;&#48;&#92;&#48;&, 16384) = 11
22:54:08.436923 write(9, &\1&#92;&#48;&#92;&#48;&#92;&#48;\1&, 5) = 5
可以看到,在22:54:08.380085时间点处,发送更新SQL语句指令,在22:54:08.380089时间读取返回结果,得到SQL执行错误,不存在字段&#8221;cfc4n_user_lock&#8221;;22:54:08.:54:08.382703两个时间点,PHP发送停止“自动提交”与“开始事务处理”指令,在 22:54:08.433954 发送“事务回滚”指令。
配合如上的代码分析,可以清楚的知道,因为“UPDATE `cfc4n_user_info` SET `cfc4n_user_lock` = 1 WHERE `cfc4n_user_id` = &#&#8217; AND `cfc4n_user_lock` = 0”这句SQL执行错误,导致$_trans_status属性被设置为FALSE,当代码提交事务时,被trans_complete()方法判断,认为“上一个事务处理”(下面将仔细分析)中存在SQL语句执行失败,决定回滚事务,不提交。
刚刚提到“上一个事务处理”,可能有些朋友不能理解,我们先继续回到代码中来,继续看该属性,同样在trans_complete方法中,542-545行:
// If we are NOT running in strict mode, we will reset
// the _trans_status flag so that subsequent groups of transactions
// will be permitted.
if ($this-&trans_strict === FALSE)
$this-&_trans_status = TRUE;
也可以很容易的从注释中看明白,设置CI的设计者,为了更严谨的处理 同一个脚本中,存在多个事务时,事务间彼此关系重要,一荣俱荣,一损俱损。这里的trans_strict属性,是个开关,当 trans_strict为false,便是非严格模式,意味着多个事务之间,彼此关系不重要,不影响。当前一个事务中有SQL语句执行失败,影响不到自己。便将_trans_status 设置为TRUE。
毫无疑问,这是个非常周全的考虑。考虑了多个事务之间的关系,保证业务跑在更严谨的代码上。
可是,我们的代码中,错误的SQL语句是执行在事务处理以外的,并不是事务之内。按照我们对事务的认识,可以很清晰的知道,事务之外的SQL相比事务之内的SQL来说,事务之内的SQL更重要,之外的可以允许出错,但事务之内的,务必要正确,不受外界干扰。但CI的框架中,因为一个事务以外的语句执行失败,却导致整个事务回滚&#8230;当然,我们的程序员没有对事务提交方法的返回做判断,这也是个问题。
问题已经很清晰了,那么解决方法想必对你来说,是多么的简单。
比如在trans_start方法中,对_trans_status 属性赋值,设置为TRUE,不理会事务外的问题。
function trans_start($test_mode = FALSE)
if ($this-&trans_strict === FALSE)
$this-&_trans_status = TRUE;
//在开始事务处理时,重新设定这个属性的值为TRUE
// 18:00 经过CI中文社区网友 /forums/space-uid-5721.html指正,这里修改为增加trans_strict 属性判断 ,在决定是否重设_trans_status 为好。
if ( ! $this-&trans_enabled)
return FALSE;
// When transactions are nested we only begin/commit/rollback the outermost ones
if ($this-&_trans_depth & 0)
$this-&_trans_depth += 1;
$this-&trans_begin($test_mode);
在不明白对方设计意图的情况下,不能盲目的定义对方的代码评价,不管程序作者的水平如何。比自己强,也不能盲目崇拜;比自己弱,更不能乱加指责;理解读懂设计意图,学习他人优秀的设计思路、代码风格、算法效率,这才是一个好习惯。当然codeigniter框架是优秀的。
Search by Tags!
2015年七月 &(2)
2015年六月 &(1)
2015年五月 &(1)
2015年一月 &(1)
2014年一月 &(1)
2013年十月 &(1)
2013年九月 &(1)
2013年八月 &(2)
2013年二月 &(2)
2013年一月 &(2)
2012年十二月 &(3)
2012年十一月 &(1)
2012年五月 &(1)
2012年四月 &(1)
2012年三月 &(1)
2012年一月 &(1)
2011年十一月 &(1)
2011年九月 &(1)
2011年八月 &(1)
2011年七月 &(1)
2011年三月 &(1)
2011年二月 &(1)
2011年一月 &(4)
2010年十二月 &(1)
2010年十一月 &(2)
2010年十月 &(2)
2010年九月 &(2)
2010年八月 &(2)
2010年七月 &(7)
2010年六月 &(7)
2010年五月 &(2)
2010年四月 &(1)
2010年三月 &(2)
2010年二月 &(1)
2010年一月 &(1)
2009年十二月 &(1)
2009年十一月 &(2)
2009年十月 &(1)
2009年九月 &(3)
2009年七月 &(2)
2009年六月 &(1)
2009年五月 &(1)
2009年四月 &(1)
2009年三月 &(1)
2009年一月 &(1)
2008年十二月 &(3)
2008年十一月 &(2)
2008年六月 &(1)
2008年四月 &(3)
2008年三月 &(7)
2008年二月 &(2)
2008年一月 &(10)
2007年十二月 &(11)
2007年十一月 &(6)
2007年九月 &(1)
2007年八月 &(1)
2007年六月 &(1)
2007年五月 &(3)
2007年四月 &(9)
2007年三月 &(11)
2007年二月 &(4)
2007年一月 &(5)
2006年十二月 &(6)
2006年十一月 &(8)
2006年十月 &(13)
2006年九月 &(6)
2006年八月 &(16)
2006年七月 &(12)
2006年六月 &(24)
莿鸟栖草堂 由
创作,采用 进行许可。基于上的作品创作。Please click
if you are not redirected within a few seconds.
你可能喜欢
来自外部的引用: 1
yii、yaf、ci等php框架性能对比 &#8211; Linux学习
12345678910
技术交流/招聘 QQ群①: QQ群②:6690706 QQ群③: 微信:ttlsacom
您还未添加分享代码,请到主题选项中,添加百度分享代码!本教程示例代码见 回顾经过了上一个 系列教程 &《利用 Composer 一步一步构建自己的 PHP 框架》 ,我们组建了一个具有 &路由 、 MVC 架构 &和 &ORM &功能的基础框架 &MFFC 。接下来我们继续完善这个项目。我们先从目前用着 &最不爽 &的地方——视图装载下手。我们将封装一个视图装载类,让它来帮我们装载视图,并把变量传递进视图。这个类将只暴露出来几个简单的接口,让我们在控制器里面用的爽,让我们一边写代码一边笑。真的笑,笑出声。:-D正文构思视图装载类要做的工作其实很简单:1. 根据视图名称找到视图文件,支持文件夹2. 更加方便,更加优雅地把变量的值传递进视图本文中我们将不会不引入模板引擎,只做装载文件和传递变量的功能。基础准备我们要引入视图装载器,这就正式打开了组件化的大门,所以我们需要做一些准备工作。启动流程组件化将 &public/index.php &里面的代码分离一部分到启动器(bootstrap),新建 &MFFC/bootstrap.php &文件:&?php
use Illuminate\Database\Capsule\Manager as C
// 定义 BASE_PATH
define('BASE_PATH', __DIR__);
// Autoload 自动载入
require BASE_PATH.'/vendor/autoload.php';
// Eloquent ORM
$capsule = new C
$capsule-&addConnection(require BASE_PATH.'/config/database.php');
$capsule-&bootEloquent();
& &修改 &public/index.php &为:& &&?php
// 定义 PUBLIC_PATH
define('PUBLIC_PATH', __DIR__);
require PUBLIC_PATH.'/../bootstrap.php';
// 路由配置、开始处理
require BASE_PATH.'/config/routes.php';
这时候我们就完成了 入口文件 和 启动器 的分离,并定义了两个全局常量 &BASE_PATH &和 &PUBLIC_PATH 。 在这里我们需要特别注意一点:“引入路由配置文件” 这一步并不只是简单地引入了一个配置文件,路由文件的最后一行 &Macaw::dispatch(); &才是 &真正执行某个控制器中某个 function & 的地方,所有准备条件都应该在载入路由文件之前完成,例如 Eloquent 的初始化,还有以后我们要使用的 Composer 包的初始化等等。引入错误页面提示组件我们选择
作为我们错误提示组件包。修改 &composer.json :"require": {
"codingbean/macaw": "dev-master",
"illuminate/database": "*",
"filp/whoops": "*"
运行 &composer update ,然后在 &bootstrap.php &的最后添加:// whoops 错误提示
$whoops = new \Whoops\R
$whoops-&pushHandler(new \Whoops\Handler\PrettyPageHandler);
$whoops-&register();
,你得到的应该还是这个页面:下面我们将增加路由配置中 &无匹配项 &的错误页面,修改 &config/routes.php :&?php
use NoahBuscher\Macaw\M
Macaw::get('', 'HomeController@home');
Macaw::get('fuck', function() {
echo "成功!";
Macaw::$error_callback = function() {
throw new Exception("路由无匹配项 404 Not Found");
Macaw::dispatch();
现在访问一个随意输入的 URL,例如
,我们会看到以下画面:是不是有一种很熟悉的感觉!很不幸,这个错误提示包正是 Laravel 采用的那个,所以,我们可爱的 &MFFC &框架在长大以后还是成了 &Laravel &的样子。%&_&%实现装载器完成基础准备以后我们正式开始制造视图装载器。视图装载器是一个可插拔组件,我们应该把所有可插拔组件全部归到一处,在 MFFC 中建议放在 &MFFC/services &下。CI 框架提供的基础组件库叫 &helpers ,Laravel 使用 &illuminate/support &包提供一些可重用的系统函数。实际上 “illuminate/support” 这个包已经被我们的 ORM 包 “illuminate/database” 依赖了,现在 MFFC 框架里面已经可以直接使用。,这个包的中文文档见:我们并没有像 CI 框架那样把视图装载器放到系统核心,有以下两个原因:基于命名空间与自动加载的调用方式更加节省资源在移动互联网和大前端愈演愈烈的时代,后端越来越 API 化、 json 化。很多时候都不到视图,没有必要再增加无畏的消耗。下面开始着手实现视图装载器。新建 &MFFC/services &文件夹,并修改 &composer.json &把这个文件夹下的所有类自动归入根命名空间:"autoload": {
"classmap": [
"app/controllers",
"app/models",
"services"
新建 &services/View.php &文件,内容如下:&?php
class View
const VIEW_BASE_PATH = '/app/views/';
public function __construct($view)
$this-&view = $
public static function make($viewName = null)
if ( ! $viewName ) {
throw new InvalidArgumentException("视图名称不能为空!");
$viewFilePath = self::getFilePath($viewName);
if ( is_file($viewFilePath) ) {
return new View($viewFilePath);
throw new UnexpectedValueException("视图文件不存在!");
public function with($key, $value = null)
$this-&data[$key] = $
private static function getFilePath($viewName)
$filePath = str_replace('.', '/', $viewName);
return BASE_PATH.self::VIEW_BASE_PATH.$filePath.'.php';
public function __call($method, $parameters)
if (starts_with($method, 'with'))
return $this-&with(snake_case(substr($method, 4)), $parameters[0]);
throw new BadMethodCallException("方法 [$method] 不存在!.");
运行 &composer dump-autoload ,完成以后,我们就可以在控制器中直接调用这个类了。修改 &controllers/HomeController.php :&?php
* \HomeController
class HomeController extends BaseController
public function home()
$this-&view = View::make('home')-&with('article',Article::first())
-&withTitle('MFFC :-D')
-&withFuckMe('OK!');
修改 &controllers/BaseController.php :&?php
* \BaseController
class BaseController
protected $
public function __construct()
public function __destruct()
$view = $this-&
if ( $view instanceof View ) {
extract($view-&data);
require $view-&
修改 &app/views/home.php :&!DOCTYPE html&
&html lang="en"&
&meta charset="UTF-8"&
&title&&?php echo $title ?&&/title&
&div class="article"&
&h1&&?php echo $article['title'] ?&&/h1&
&div class="content"&
&?php echo $article['content'] ?&
&ul class="fuckme"&
&li&Fuck Me !&/li&
&?php echo $fuck_me ?&
& &刷新,你将看到以下页面:至此,视图装载器实现完成。下面我大致说一下设计视图装载器的基本思路:这个视图装载器类模仿了 Laravel 的 View 类,它实现了一个静态方法 &make ,接受视图名称作为参数,以 &. &作为目录的间隔符。make 静态方法会检查视图名称是否为空,检查视图文件是否存在,并给出相应的异常。这就是我们引入异常处理包的原因。视图名称合法且文件存在时,实例化一个 View 类的对象,返回。使用 &with('key', $value) &或者优雅的 &withKey($value) &来给这个 View 对象插入要在视图里调用的变量。 withFuckMe($value) &将采用蛇形命名法被转化成 &$fuck_me &供视图使用。最终组装好的 View 对象会被赋给 &HomeController &的成员变量 &$view ,这个变量是从 &BaseController &中继承得来。父类 &BaseController &中的析构函数 &__destruct() &将在 &function home() &执行完成后处理这个成员变量: extract &出视图要用到的变量, require &视图文件,将最终运算结果发送给浏览器,流程结束。下一步:
WRITTEN BY
Web 程序员,PHP 框架
作者。iOS & OS X 开发者。
相关日志:
Powered by}

我要回帖

更多关于 codeigniter手册 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信