h2:first-child, body>h1:first-child, body>h1:first-child+h2, body>h3:first-child, body>h4:first-child, body>h5:first-child, body>h6:first-child {
margin-top: 0;
padding-top: 0;
}

a:first-child h1, a:first-child h2, a:first-child h3, a:first-child h4, a:first-child h5, a:first-child h6 {
margin-top: 0;
padding-top: 0;
}

h1+p, h2+p, h3+p, h4+p, h5+p, h6+p {
margin-top: 10px;
}

/* LINKS
=============================================================================*/

a {
color: #4183C4;
text-decoration: none;
}

a:hover {
text-decoration: underline;
}

/* LISTS
=============================================================================*/

ul, ol {
padding-left: 30px;
}

ul li > :first-child,
ol li > :first-child,
ul li ul:first-of-type,
ol li ol:first-of-type,
ul li ol:first-of-type,
ol li ul:first-of-type {
margin-top: 0px;
}

ul ul, ul ol, ol ol, ol ul {
margin-bottom: 0;
}

dl {
padding: 0;
}

dl dt {
font-size: 14px;
font-weight: bold;
font-style: italic;
padding: 0;
margin: 15px 0 5px;
}

dl dt:first-child {
padding: 0;
}

dl dt>:first-child {
margin-top: 0px;
}

dl dt>:last-child {
margin-bottom: 0px;
}

dl dd {
margin: 0 0 15px;
padding: 0 15px;
}

dl dd>:first-child {
margin-top: 0px;
}

dl dd>:last-child {
margin-bottom: 0px;
}

/* CODE
=============================================================================*/

pre, code, tt {
font-size: 12px;
font-family: Consolas, "Liberation Mono", Courier, monospace;
}

code, tt {
margin: 0 0px;
padding: 0px 0px;
white-space: nowrap;
border: 1px solid #eaeaea;
background-color: #f8f8f8;
border-radius: 3px;
}

pre>code {
margin: 0;
padding: 0;
white-space: pre;
border: none;
background: transparent;
}

pre {
background-color: #f8f8f8;
border: 1px solid #ccc;
font-size: 13px;
line-height: 19px;
overflow: auto;
padding: 6px 10px;
border-radius: 3px;
}

pre code, pre tt {
background-color: transparent;
border: none;
}

kbd {
-moz-border-bottom-colors: none;
-moz-border-left-colors: none;
-moz-border-right-colors: none;
-moz-border-top-colors: none;
background-color: #DDDDDD;
background-image: linear-gradient(#F1F1F1, #DDDDDD);
background-repeat: repeat-x;
border-color: #DDDDDD #CCCCCC #CCCCCC #DDDDDD;
border-image: none;
border-radius: 2px 2px 2px 2px;
border-style: solid;
border-width: 1px;
font-family: "Helvetica Neue",Helvetica,Arial,sans-serif;
line-height: 10px;
padding: 1px 4px;
}

/* QUOTES
=============================================================================*/

blockquote {
border-left: 4px solid #DDD;
padding: 0 15px;
color: #777;
}

blockquote>:first-child {
margin-top: 0px;
}

blockquote>:last-child {
margin-bottom: 0px;
}

/* HORIZONTAL RULES
=============================================================================*/

hr {
clear: both;
margin: 15px 0;
height: 0px;
overflow: hidden;
border: none;
background: transparent;
border-bottom: 4px solid #ddd;
padding: 0;
}

/* TABLES
=============================================================================*/

table th {
font-weight: bold;
}

table th, table td {
border: 1px solid #ccc;
padding: 6px 13px;
}

table tr {
border-top: 1px solid #ccc;
background-color: #fff;
}

table tr:nth-child(2n) {
background-color: #f8f8f8;
}

/* IMAGES
=============================================================================*/

img {
max-width: 100%
}
-->

前言

说到写PHP的MVC框架,大家想到的第一个词--“造轮子”,是的,一个还没有深厚功力的程序员,写出的PHP框架肯定不如那些出自大神们之手、经过时间和各种项目考验的框架。但我还是准备并且这么做了,主要是因为:

  • 认为有关PHP的方方面面都了解了,但自己学习PHP的时间还短,基础并不扎实,很多常用函数的参数还偶尔要查手册,而且对于PHP的一些较新的特性如命名空间、反射等只是简单的看过,并没有能实际应用过。
  • PHP的知识多且杂,一个普通的项目往住是业务逻辑代码为主,而框架是一个能把这些知识点能融汇在一起的项目。
  • 在自己写一个框架的时候,也会参考一些我使用过的框架如TP/CI/YII等的源码,在自己看源码时也能帮助自己理解框架,更容易接受以后要使用的框架。

所以说,这次造轮子的目的不是为了造轮子而是为了在造轮子的过程中熟悉其工艺,总结轮子特点,更好的使用轮子。

如果说写一个完整的PHP框架,那需要掌握的PHP知识点非常多,像设计模式、迭代器、事件与钩子等等,还有许多基础知识的灵活应用。我自认为这些还无法完全掌控,所以我的步骤是先自己搭建一个骨架,然后参考借鉴不同的PHP框架的特点,将其慢慢完善。因为工作原因,而且晚上还要补算法、网络等编程基础,PHP框架部分可能只有周末有时间更新,我会在进行框架功能更新之后,总结使用的知识点,更新博文。

首先放上框架的目前源码:GITHUB/zhenbianshu


框架整体

首先自己总结一下PHP的MVC框架的工作流程:

简单来说,它以一个入口文件来接受请求,选择路由,处理请求,返回结果。

当然,几句话总结完的东西实际上要做的工作很多,PHP框架会在每次接受请求时,定义常量,加载配置文件、基础类,根据访问的URL进行逻辑判断,选择对应的(模块)控制器和方法,并且自动加载对应类,处理完请求后,框架会选择并渲染对应的模板文件,以html页面的形式返回响应。在处理逻辑的时候,还要考虑到错误和异常的处理。

1、作为MVC框架,一定要有一个唯一的入口文件来统领全局,所有的访问请求都会首先进入这个入口文件,如我框架根目录的index.php,在里面,我定义了基本文件夹路径,当前环境,并根据当前环境定义错误报告的级别。

2、PHP中加载另外的文件,使用require和include,它们都是将目标文件内容加载到当前文件内,替换掉require或include语句,require是加载进来就执行,而include是加载进来在需要的时候执行,而它们的_once结构都是表示在写多次的时候只执行一次。

3、框架内的配置变量等使用专用的配置文件来保存,这里我仿照了TP里的数组返回法,用了一个compileConf()函数来解析数组,将数组的键定义为常量,值为数组的值。

if (!function_exists('compile_conf')) {
function compileConf($conf) {
foreach ($conf as $key => $val) {
if(is_array($val)){
compileConf($val);
}else{
define($key, $val);
}
}
}
}
compileConf(require_once CONF_PATH.'config.php');

命名空间和自动加载

为什么把命名空间和自动加载放到一块说呢?

在一个PHP项目中,类特别多的时候,如果类名重复的话就会造成混乱,而且相同文件夹内也不能存在同名的文件,所以这时候命名空间和文件夹就搭档出场了。文件夹就是一个一个的盒子,命名空间在我理解就像是一个标签,盒子对应标签。我们定义类时,把各种类用不同的盒子分别装好,并贴上对应的标签。而在自动加载类时,我们根据标签(命名空间)可以很轻易找到对应的盒子(文件夹)然后找到对应的类文件。

而类的自动加载,我们知道的__autoload()魔术函数,它会在你实例化一个当前路径找不到的对象时自动调用,根据传入的类名,在函数体内加载对应的类文件。

现在我们多用spl_autoload_register()函数,它可以注册多个函数来代替__autoload函数的功能,我们传入一个函数名为参数,spl_autoload_register会将这个函数压入栈中,在实例化一个当前路径内找不到的类时,系统将会将函数出栈依次调用,直到实例化成功。

spl_autoload_register('Sqier\Loader::autoLoad');

class Loader {
public static function autoLoad($class) {
//如果有的话,去除类最左侧的\
$class = ltrim($class, '\\');
//获取类的路径全名
$class_path = str_replace('\\', '/', $class) . EXT;
if (file_exists(SYS_PATH . $class_path)) {
include SYS_PATH . $class_path;
return;
}
if (file_exists(APP_PATH . $class_path)) {
include APP_PATH . $class_path;
return;
}
}

现在Loader类还是一个简单的类,待以后慢慢完善。


路由选择

接下来就是路由选择了,其本质是根据当前定义的全局URL模式选择合适的方法来分析传入的URI,加载对应的类,并实现对应的方法。

class Router {
public static $uri; public static function bootstrap() {
self::$uri = $_SERVER['REQUEST_URI'];
switch (URL_MODE) {
case 1: {
self::rBoot();
break;
}
default: {
self::rBoot();
}
}
} public static function rBoot() {
$router = isset($_GET['r']) ? explode('/', $_GET['r']) : [
'index',
'index'
];
$cName = 'Controller\\' . ucfirst($router[0]);
$aName = isset($router[1]) ? strtolower($router[1]) . 'Action' : 'indexAction';
$controller = new $cName();
$controller->$aName();
}
}

这样,我在地址栏输入 zbs.com/index.php?r=index/login 后,系统会自动调用/app/Controller/Index.php下的login方法。完成了这么一个简单的路由。


后续

接下来我会优化现有的工具类,添加显示层,添加数据库类,还会将一些别的框架里非常cool的功能移植进来~

待续...

搭建自己的PHP框架心得(一)的更多相关文章

  1. 搭建自己的PHP框架心得——转载

    原文:http://www.cnblogs.com/zhenbianshu/p/5331165.html 前言 说到写PHP的MVC框架,大家想到的第一个词--“造轮子”,是的,一个还没有深厚功力的程 ...

  2. 搭建自己的PHP框架心得(三)

    h2:first-child, body>h1:first-child, body>h1:first-child+h2, body>h3:first-child, body>h ...

  3. 搭建自己的PHP框架心得(二)

    h2:first-child, body>h1:first-child, body>h1:first-child+h2, body>h3:first-child, body>h ...

  4. 使用DotNetOpenAuth搭建OAuth2.0授权框架

    标题还是一如既往的难取. 我认为对于一个普遍问题,必有对应的一个简洁优美的解决方案.当然这也许只是我的一厢情愿,因为根据宇宙法则,所有事物总归趋于混沌,而OAuth协议就是混沌中的产物,不管是1.0. ...

  5. ASP.NET MVC搭建项目后台UI框架—1、后台主框架

    目录 ASP.NET MVC搭建项目后台UI框架—1.后台主框架 ASP.NET MVC搭建项目后台UI框架—2.菜单特效 ASP.NET MVC搭建项目后台UI框架—3.面板折叠和展开 ASP.NE ...

  6. ASP.NET MVC搭建项目后台UI框架—11、自动加载下拉框查询

    ASP.NET MVC搭建项目后台UI框架—1.后台主框架 需求:在查询记录的时候,输入第一个字,就自动把以这个字开头的相关记录查找出来,输入2个字就过滤以这两个子开头的记录,依次类推. 突然要用到这 ...

  7. 第三篇 基于.net搭建热插拔式web框架(重造Controller)

    由于.net MVC 的controller 依赖于HttpContext,而我们在上一篇中的沙箱模式已经把一次http请求转换为反射调用,并且http上下文不支持跨域,所以我们要重造一个contro ...

  8. 第二篇 基于.net搭建热插拔式web框架(沙箱的构建)

    上周五写了一个实现原理篇,在评论中看到有朋友也遇到了我的问题,真的是有种他乡遇知己的感觉,整个系列我一定会坚持写完,并在最后把代码开源到git中.上一篇文章很多人看了以后,都表示不解,觉得不知道我到底 ...

  9. 基于.net搭建热插拔式web框架(实现原理)

    第一节:我们为什么需要一个热插拔式的web框架? 模块之间独立开发 假设我们要做一个后台管理系统,其中包括“用户活跃度”.“产品管理”."账单管理"等模块.每个模块中有自己的业务特 ...

随机推荐

  1. CloudNotes:一个云端个人笔记系统

    很长时间没有更新博客了,一直在忙着工作和生活琐事,虽然偶尔也有闲暇之时,但短短的几个小时空闲又未必能够静下心来.最近一个多月突发奇想,将自己在近一年前做的一个自己用的云端个人笔记系统重构美化了一下,增 ...

  2. JAVA错误汇总

    1.Slf4J-API版本兼容 问题描述 Exception in thread "main" java.lang.NoSuchMethodError: org.slf4j.hel ...

  3. Xamarin.Android和UWP之MVVM的简单使用(一)

    0x01 前言 就目前而言,MVVM可以说是挺流行的,无论是web端还是移动端,web端的主要代表angularjs,avalonjs等, 移动端(xamarin,uwp)的代表应该是mvvmligh ...

  4. javaMail使用163邮箱报535 Error: authentication failed

    javaMail使用网易163邮箱或者是126或者是网易其他邮箱报535 Error: authentication failed javax.mail.AuthenticationFailedExc ...

  5. nginx concat模块配置 页面返回400 bad request

    在1.4.x版本的nginx没有发现这个问题,但是在1.5.x版本就遇到了这个问题 由于Nginx在新版本中,使用了标准的 MIME-Type:application/javascript.而在ngi ...

  6. php中return的用法实例分析

    本文实例讲述了php中return的用法.分享给大家供大家参考.具体分析如下: 首先,它的意思就是返回;return()是语言结构而不是函数,仅在参数包含表达式时才需要用括号将其括起来.当返回一个变量 ...

  7. 【夯实PHP基础系列】linux下yum安装PHP APC

    Alternative PHP Cache(可选PHP缓存),依赖于 PECL扩展库 用源码方式安装,直接yum就行了:首先要安装apc的依赖包:yum install php-pear php-de ...

  8. jQuery:详解jQuery中的事件(二)

    上一篇讲到jQuery中的事件,深入学习了加载DOM和事件绑定的相关知识,这篇主要深入讨论jQuery事件中的合成事件.事件冒泡和事件移除等内容. 接上篇jQuery:详解jQuery中的事件(一) ...

  9. OData V4 学习目录

    开放数据协议(Open Data Protocol,缩写OData)是一种描述如何创建和访问Restful服务的OASIS标准. Open Data Protocol (开放数据协议,OData)是用 ...

  10. HTML5 学习笔记(四)——canvas绘图、WebGL、SVG

    一.Canvas canvas是HTML5中新增一个HTML5标签与操作canvas的javascript API,它可以实现在网页中完成动态的2D与3D图像技术.<canvas> 标记和 ...