前言:

        其实语言并不重要,关键是开发思想,递增式的学习,会更加有收获;下面我将从分析一个电商项目进行整理一下自己所学的知识点,学会欣赏别人的代码!!!


1.框架意识

2.MVC设计思想

3.电商项目的分析



1.框架意识:

        其实我们​一直都在接触框架​,框架不是一件什么神奇的东西,首先:


  • 如果没有Apache这个服务器作为我们的底层框架,我们PHP的发送到服务器端的代码远远不止这么少。
  •  我们习以为常的_GET[]、_SESSION[]这些机制,其实也是封装好的东西,php预处理也是一种框架,只是不用我们去理会。

        下面介绍的这个项目,在PHP 层已经做得很好了,只是在VIEW层没有怎么处理(因为VIEW层现在技术可以达到模板语言,而不是传标签,解决IF语句,类似于node.js)


2.MVC设计思想:


本次分析的电商项目特点:

    抽象,​单入口​:

    index:php?p=xxx&c=xxx & a=xxx;

与我之前架构的:

    直接,​多入口​:(多个功能多个文件)


2.1单入口的分析:


相比于之前的架构方式:将PHP文件下的模块功能分开


Notes:

        现在流行的Restful规范(TP5等)并不是简单的但入口而是对URL进行了语义化,路径分析(路由)


3.电商项目的分析:

Notes:

        由于该项目的路径地址是是根目录因此文件夹需要均放在外面


3.1.index.php:

//项目调试开关

define('APP_DEBUG',true);

//引入框架基础类

require './framework/Framework.class.php';

//运行项目

Framework::run();

项目调试开关,true的时候是我们会将错误信息打印出来,在正常给用户运行的时候,这里是关掉的。

framework.class.php里的方法

private static function init(){

//开启调试时,显示错误报告

if(APP_DEBUG){

ini_set('display_errors',1);

error_reporting(E_ALL);

}else{

ini_set('display_errors',0);

error_reporting(0);

}


3.2.framework.class.php

//启动项目


public static function run(){

self::init(); //初始化

self::registerAutoLoad(); //注册自动加载

self::extend(); //扩展功能

self::dispatch(); //请求分发

}

类的静态方法和静态属性可以不用实例化对象直接使用(使用的方式是 类名::静态方法名 )

静态方法在读到这个类或者引入这个类文件的时候,就已经实例化并存放到内存中了,非静态类则需要new一下。

静态类在内存中即使有多个实例,静态的属性也只有一份。

::”这个叫范围解析操作符,又名域运算符   ‘- >’符号是“插入式解引用操作符”  用于类中,访问类里的函数或对象,比如:

<?php class Test { function do_test() { echo "Doing foo."; } } $bar = new Test; $bar->do_test(); ?>

PHP中=>和->以及::的用法实例可以参照这一篇文章

​​http://www.php.cn/php-weizijiaocheng-378631.html​​


3.2.1 路径配置:

//设置常量供项目内使用


define('DS', DIRECTORY_SEPARATOR); //路径分隔符

define('ROOT', getcwd().DS); //项目根目录

define('APP_PATH', ROOT.'app'.DS); //应用目录

define('FRAMEWORK_PATH', ROOT.'framework'.DS); //框架目录

define('LIBRARY_PATH', FRAMEWORK_PATH.'library'.DS);//类库目录

define('PUBLIC_PATH', ROOT.'public'.DS); //公开目录

define('COMMON_PATH', APP_PATH.'common'.DS); //公共目录

DIRECTORY_SEPARATOR 这个是PHP预处理的变量,相当于输出路径分隔符,为什么要这样操作呢?

因为在LINUX上,路径是‘/’


而实际上多多数的网站我们会将他们配置到LINUX服务器上,所以这一点非常重要


3.3.3 注册自动加载:

//注册自动加载


private static function registerAutoLoad(){

spl_autoload_register(function($class_name){

$class_name = ucwords($class_name); //自动转换类名首字母大写

if(strpos($class_name, 'Controller')){

$target = CONTROLLER_PATH."$class_name.class.php";

if(is_file($target)){

require $target;

}else{

E('您的访问参数有误!');

}

}elseif(strpos($class_name, 'Model')){

require MODEL_PATH."$class_name.class.php";

}else{

require LIBRARY_PATH."$class_name.class.php";

}

});

}


spl_autoload_register(function($class_name)

如不是有这个方法的帮助下,PHP 的面向对象将会变得法场头疼,自动加载注册类,

E 函数:

//遇到致命错误,输出错误信息并停止运行

function E($msg){

header('content-type:text/html;charset=utf-8');

die('<pre>'.htmlspecialchars($msg).'</pre>');

}


3.3.4 获取请求参数:

//获取请求参数

private static function getParams(){

//获取URL参数

$p = I('p','get','string','home');

$c = I('c','get','string','index');

$a = I('a','get','string','index');

//获取pathinfo

$pathinfo = self::getPathinfo();

//定义常量表示 URL Rewrite 开关

define('URL_REWRITE', (bool)$pathinfo);

//开启URL Rewrite时,从pathinfo获取参数

if(URL_REWRITE){

$params = self::getPathinfoParams($pathinfo);

//进行路由规则匹配

$start = 0; //取出参数的起始下标

if(isset($params[0]) && ($roule=C('URL_MAP_RULES')) && isset($roule[$params[0]])){

list($p,$c,$a) = explode('/', $roule[$params[0]]);

$start = (count($params)%2 > 0) ? 1 : 0; //根据参数个数,自动取出后面的参数

}

//将pathinfo参数保存到$_GET中

for($len=count($params);$start<$len;$start+=2){

if(isset($params[$start])){

$_GET[$params[$start]] = isset($params[$start+1]) ? $params[$start+1] : '';

}

}

}

//正则验证参数,要求以字母开头,后面跟0~20个 字母/数字/下划线 字符

foreach(array($p,$c,$a) as $v) {

preg_match('/^[A-Za-z]\w{0,20}$/',$v) || E('请求参数包含特殊字符!');

}

return array($p,$c,$a);

}

最关键是弄懂这个I方法:

//接收变量

//变量名,方法,数据类型,默认值

function I($var,$method='post',$type='text',$def=''){

switch($method){

case 'get': $method = &$_GET; break;

case 'post': $method = &$_POST; break;

case 'cookie': $method = &$_COOKIE; break;

case 'server': $method = &$_SERVER; break;

}

$value = isset($method[$var]) ? $method[$var] : $def;

switch($type){

//字符串类型

case 'string': //字符串 不进行过滤

$value = is_string($value) ? $value : '';

break;

case 'text': //字符串 进行HTML转义

$value = is_string($value) ? trim(htmlspecialchars($value)) : '';

break;

case 'int': //整数

$value = (int)$value;

break;

case 'id': //无符号整数

$value = max((int)$value,0);

break;

case 'float': //浮点数

$value = (float)$value;

break;

case 'bool': //布尔型

$value = (bool)$value;

break;

case 'array': //数组型

$value = is_array($value) ? $value : array();

break;

}

return $value;

}

举个栗子,如果I(' ','get','string','index');

那么我们将会直接进入主页面,但是同时也不会将路径暴露给用户


//从pathinfo中获取参数,进行路由匹配


private static function getPathinfoParams($pathinfo){

//过滤URL后缀

$suffix = C('URL_SUFFIX');

$len = strlen($suffix);

if(substr($pathinfo,-$len)==$suffix){

$pathinfo = substr($pathinfo, 0, strlen($pathinfo)-$len);

}

//将pathinfo转换为数组

return explode('/', trim($pathinfo, '/'));

}

Notes:

$len = strlen($suffix);

if(substr($pathinfo,-$len)==$suffix){

$pathinfo = substr($pathinfo, 0, strlen($pathinfo)-$len);

}

substr($pathinfo,-$len)==$suffix



//配置文件操作

function C($name,$value=null){

static $config = null; //保存项目中的设置

//函数首次被调用时载入配置文件

if(!$config){

$config = require COMMON_PATH.'config.php';

}

//省略value参数表示获取配置项,否则修改配置项

if(is_null($value)){

return isset($config[$name]) ? $config[$name] : '';

}else{

$config[$name] = $value;

}

}


3.3.5  难点却很实用的会话存储:

SESSION_DB

//扩展功能

private static function extend(){

//配置时区

date_default_timezone_set(C('DEFAULT_TIMEZONE'));

//设置HttpOnly

C('PHPSESSID_HTTPONLY') && ini_set('session.cookie_httponly', 1);

//Session入库

C('SESSION_DB') && new SessionDB();

//配置 mbstring 扩展内置字符集

mb_internal_encoding('UTF-8');

}



<?php

//Session数据库转储

class SessionDB {

/*

-- 数据库方式Session驱动

create table `prefix_session` (

`id` varchar(255) not null unique key,

`expire` int(11) not null,

`data` blob

);

*/

protected $lifeTime = '';

protected $table = '';

protected $db = null;

public function __construct(){

session_set_save_handler(

array(&$this, 'open'),

array(&$this, 'close'),

array(&$this, 'read'),

array(&$this, 'write'),

array(&$this, 'destroy'),

array(&$this, 'gc')

);

}

public function open($savePath, $sessName) {

$this->lifeTime = C('SESSION_EXPIRE') ? C('SESSION_EXPIRE') : ini_get('session.gc_maxlifetime');

$this->table = C('DB_PREFIX') . 'session';

$this->db = M();

return true;

}

public function close() {

$this->gc($this->lifeTime);

return true;

}

public function read($sessID) {

$sql = "select `data` from `$this->table` where `id`= :id and `expire` > :expire";

$data = array('id' => $sessID,'expire' => time());

$rst = $this->db->data($data)->fetchRow($sql);

return isset($rst['data']) ? $rst['data'] : '';

}

public function write($sessID, $sessData) {

$sql = "replace into `$this->table` (`id`,`expire`,`data`) values (:id, :expire, :data)";

$data = array('id'=>$sessID,'expire'=>time() + $this->lifeTime,'data'=>$sessData);

$this->db->data($data)->query($sql);

return true;

}

public function destroy($sessID) {

$sql = "delete from `$this->table` where `id`=:id";

$data = array('id'=>$sessID);

$this->db->data($data)->query($sql);

return true;

}

public function gc($sessMaxLifeTime) {

$sql = "delete from `$this->table` where `expire` < :expire";

$data = array('expire'=>time());

$this->db->data($data)->query($sql);

return true;

}

}

Session 数据木库转存需要按照PHP接口规范

Notes:

        php会话​存储默认在服务器内存中​,但是一旦服务器关闭就消失:

应用场景有:

        1.自动登录功能

        2.电商项目的购物车:本次案例重点讲解的是电商的购物车信息存储的问题