断断续续花了7天勉勉强强的把ThinkPHP视频教程看完了,现在尝试着用框架来搭建一个网上商城网站。
运行环境如下:
Apache Version : 2.4.9 PHP Version : 5.5.12 MySQL Version : 5.6.17 ThinkPHP Version :3.2.3
下载ThinkPHP完毕,解压到站点目录,开始配置入口 index.php 文件
// 检测PHP环境 if(version_compare(PHP_VERSION,'5.3.0','<')) die('require PHP > 5.3.0 !'); // 开启调试模式 建议开发阶段开启 部署阶段注释或者设为false define('APP_DEBUG',True); // 定义应用目录 define('APP_PATH','./NutStore/'); // 定义自动生成的安全文件 define('DIR_SECURE_FILENAME', 'default.html'); define('DIR_SECURE_CONTENT', 'Dir!'); // 引入ThinkPHP入口文件 require './ThinkPHP/ThinkPHP.php';
修改了自动生成的文件为default.html,方便区分index,安全文件里面内容为“Dir!”,但是发现服务器不认default.html,那么修改下Apache配置文件在末尾加上default.html
<IfModule dir_module> DirectoryIndex index.php index.php3 index.html index.htm default.html </IfModule>
既然来了,顺便修改下服务器支持路径重写。
加载路径重写模块,去掉#的注释符
LoadModule rewrite_module modules/mod_rewrite.so
授权
<Directory "F:/wamp2/bin/apache/apache2.4.9/cgi-bin"> AllowOverride None #改为All Options None Require all granted </Directory>
在配置文件中新增字段
'URL_MODEL' => 2,
在入口文件新增.htaccess文件(花生的这个版本的ThinkPHP集成了),内容为
<IfModule mod_rewrite.c> Options +FollowSymlinks RewriteEngine On RewriteCond %{REQUEST_FILENAME} !-d RewriteCond %{REQUEST_FILENAME} !-f RewriteRule ^(.*)$ index.php/$1 [QSA,PT,L] </IfModule>
重启下Apache,应该就可以了,下面的路径完整路径是这样的
站点/index.php/home/Index/index
这时去掉index.php应该也是可以的了
站点/home/Index/index
只要执行了入口文件,那么就会生成一个NutStore应用,里面有个默认的Home模块,模块里有个Index控制器,控制器里有个index()方法,花生想要再建立一个模块,复制一份Home,改名为Admin,并且修改Controller里面的命名空间。
下面为站点NutStoreAdminControllerIndexController.class.php
<?php namespace AdminController; use ThinkController; class IndexController extends Controller { public function index(){ echo 'AdminIndexController -> index()'; } public function test(){ echo 'AdminIndexController -> test()'; } }
那么在URL中 站点/Admin/Index/test ,相当于执行了NutStore应用下的Admin模块下的Index控制器下的test方法,会打印出AdminIndexController -> test()
这里花生还要新建一个入口文件,复制index.php入口文件命名为admin.php,在引入ThinkPHP入口文件之前添加如下代码,来将入口文件绑定到Admin模块。
$_GET['m']='Admin';
m参数表示模块,c参数表示控制器,a参数表示操作,这些都是在入口文件可以绑定的,根据需要有爱自绑吧。
接下来配置数据库信息。
因为在本地调试用本地的数据库,而远程肯定用远程仓库,而在本地测试时肯定是开启调试模式的,开启调试模式会自动加载 站点ThinkPHPConfdebug.php 配置文件,而无论是否开启调试模式都会加载 站点ThinkPHPConfconvention.php 配置文件,那么我们就把远程的数据库信息写在convention.php里面,本地的卸载debug.php里面,因为debug.php仅会在开启调试模式下生效,且优先级大于convention.php,那么就没有问题了
我们这里使用pdo数据库配置,下面是debug.php新增的字段,顺便把调试工具打开
'DB_TYPE' => 'pdo', //数据库类型 'DB_USER' => 'root', //用户名 'DB_PWD' => '', //密码 'DB_PREFIX' => 'think_', //数据库表前缀 'DB_DSN' => 'mysql:host=localhost;dbname=think;charset=UTF8', //开启调试工具 'SHOW_PAGE_TRACE' =>true,
使用过程中发现了一些bug,下面尝试修复下
1.Think\Model::getTableName()方法不会读取由Think\Model::table()方法创建的数据表,因此修改下判断规则
public function getTableName() { if(empty($this->trueTableName)) { $tableName = !empty($this->tablePrefix) ? $this->tablePrefix : ''; if(!empty($this->tableName)) { $tableName .= $this->tableName; }else if(!empty($this->options['table'])){ $tableName = $this->options['table']; }else{ $tableName .= parse_name($this->name); } $this->trueTableName = strtolower($tableName); } return (!empty($this->dbName)?$this->dbName.'.':'').$this->trueTableName; }
2.如果调用过Think\Model::add()方法,会自动调用Think\Model::getTableName()方法来生成Think\Model::trueTableName属性,这会导致如果之后调用Think\Model::table()会完全不起作用,因此将Think\Model::table()方法开头加入一行代码清掉trueTableName值
public function table($table) { $this->trueTableName=null; $prefix = $this->tablePrefix; if(is_array($table)) { $this->options['table'] = $table; }elseif(!empty($table)) { //将__TABLE_NAME__替换成带前缀的表名 $table = preg_replace_callback("/__([A-Z0-9_-]+)__/sU", function($match) use($prefix){ return $prefix.strtolower($match[1]);}, $table); $this->options['table'] = $table; } return $this; }
3.发现使用/ThinkPHP/Library/Think/Verify.class.php生成的验证码第一个字符总是有很大的边距,查看源代码发现ThinkPHP使用的是左边距来缩进,而第一个字符不需要左边距,它也缩进了,因此我们修改一下类里面entry方法
$codeNX = -$this->fontSize*1.0;
这样第一个字符就只有一点点的缩进了
发现ThinkPHP的图片和CSS等文件都默认的应该放在./Public/文件夹,感觉很不科学,因此花生自己在./Nutjs/Home/下建立了一个Public文件夹,里面存放着各个控制器要使用的资源文件
花生还希望能够有一个模板常量可以直接指向这个文件夹,因此在.\Nutjs\Common\Conf\config.php文件中定义了一个常量
'TMPL_PARSE_STRING' =>array( '__public__' => __ROOT__.str_replace('.','', APP_PATH).'Public/'.CONTROLLER_NAME, )
后来发现在配置文件中定义的话,有很多常量都不认,比如CONTROLLER_NAME,花生希望这个小写的__self__可以直接指向花生新建的资源文件夹下的当前控制器。
好吧,换个地方定义,定义在.\ThinkPHP\Library\Behavior\ContentReplaceBehavior.class.php里面就好了
// 系统默认的特殊变量替换 $replace = array( '__ROOT__' => __ROOT__, // 当前网站地址 '__APP__' => __APP__, // 当前应用地址 '__MODULE__' => __MODULE__, '__ACTION__' => __ACTION__, // 当前操作地址 '__SELF__' => htmlentities(__SELF__), // 当前页面地址 '__CONTROLLER__'=> __CONTROLLER__, '__URL__' => __CONTROLLER__, '__PUBLIC__' => __ROOT__.'/Public',// 站点公共目录 //自己定义的模板常量 '__public__' => __ROOT__.str_replace('.','', APP_PATH).BIND_MODULE.'Public/',// 站点公共目录, '__self__' => __ROOT__.str_replace('.','', APP_PATH).BIND_MODULE.'/Public/'.CONTROLLER_NAME,// 站点公共目录+当前控制器名, );
定义好了路径,发现ZendStudio会对模板文件报错,说没有这个CSS/JS文件,这个简直不能忍啊,必须要屏蔽报错!
在一位高手的指点下,找到了屏蔽报错的位置
发现ThinkPHP的模板中内置标签不支持__XXX__这种常量,花生之前定义的失效的,失效的原因有2
那么就这么做,首先还是在.\ThinkPHP\Library\Behavior\ContentReplaceBehavior.class.php增加
'__INCLUDE__' => APP_PATH.BIND_MODULE.'/Public/Include/'.CONTROLLER_NAME,//公共引用文件夹
注意这个常量和前面的几个不一样,是./开头的PHP路径
然后修改.\ThinkPHP\Library\Think\Template.class.php文件里面的函数如下
protected function compiler($tmplContent) { // 模版编译过滤标签,替换各种__XXX__常量 //这里在开始解析一遍,末尾再解析一遍,可以确保include等特殊标签页支持__XXX__常量 //var_dump($tmplContent); Hook::listen('template_filter',$tmplContent); //模板解析 $tmplContent = $this->parse($tmplContent); // 还原被替换的Literal标签 $tmplContent = preg_replace_callback('/<!--###literal(\d+)###-->/is', array($this, 'restoreLiteral'), $tmplContent); // 添加安全代码 $tmplContent = '<?php if (!defined(\'THINK_PATH\')) exit();?>'.$tmplContent; // 优化生成的php代码 $tmplContent = str_replace('?><?php','',$tmplContent); // 模版编译过滤标签,替换各种__XXX__常量 Hook::listen('template_filter',$tmplContent); return strip_whitespace($tmplContent); }
问题就解决了,不过也带来了而额外的性能消耗
又发现如果控制器调用create方法后,提交的数据没有被临时的存储,调用起来比较麻烦,比如在数据表模型类中无法方便的取得,因此修改
//修改文件:.\ThinkPHP\Library\Think\Model.class.php /** * 通过create提交的未经过滤的数据 * @var submit * @access public * */ public $submit=array(); public function create($data='',$type='') { //PeA:首先将传来的数据放到一个字段中 $this->submit=$data; //......
又发现ThinkPHP在执行create的时候会强制执行所有的自动验证$_validate,哪怕是提前用field()方法指定都不行,这个明显不科学!
改吧。。。
//修改文件:.\ThinkPHP\Library\Think\Model.class.php //autoValidation方法 //... foreach($_validate as $key=>$val) { //PeA:让自动验证读取$this->options['field'] if(!empty($this->options['field']) && !in_array($val[0], $this->options['field'])) continue; // 验证因子定义格式 // array(field,rule,message,condition,type,when,params) // 判断是否需要执行验证 if(empty($val[5]) || ( $val[5]== self::MODEL_BOTH && $type < 3 ) || $val[5]== $type ) { //... //create方法也要改 //PeA:让自动验证读取$this->options['field'] $this->options['field']=$fields; // 数据自动验证 if(!$this->autoValidation($data,$type)) return false; //PeA:释放配置 unset($this->options['field']); // 表单令牌验证 if(!$this->autoCheckToken($data)) { $this->error = L('_TOKEN_ERROR_'); return false; }