http://code.google.com/p/ncache/
一个基于nginx的插件,在考虑一个大型文件系统时候,开发可以参考一下,相关连接
http://code.google.com/p/ncache/wiki/HowToNcacheV2
不过感觉测试还不够多,不敢轻易使用
一个基于nginx的插件,在考虑一个大型文件系统时候,开发可以参考一下,相关连接
http://code.google.com/p/ncache/wiki/HowToNcacheV2
不过感觉测试还不够多,不敢轻易使用
self访问类内部static变量 ,不想用全局变量, 暂时这样搞了!
阅读全文
阅读全文
也许是待得太久,就像被一桶草莓酱从头浇到脚,尝哪里都是甜味一样,当初次看到Code Review成为一个如此重型并且低效的活动的时候,我才知道,草莓酱的外面,是空气,裹着大地的气息,大部分无味,又或者是烟草味,或者汽车的尾气。
先看一看我们看到的一个代码审查过程:
- 开发人员领到任务。
- 一周之后,代码写完了。他觉得没底,需要找业务专家技术专家来评审一下。这个时候他代码还没有提交。于是他把本地所有没有提交的、修改过后的文件,放到一起,压缩成rar包。找到他认为的技术高手业务高手(们),定会议室,发邮件通知。2小时过去了。
- 技术专家业务专家收到了邮件,由于缺乏上下文理解,以及长达数千行的源代码,这类邮件一般不看——因为看了也是白看。
- 终于到了Code Review的那一天。七七八八的来了几个人。一般来不全。因为高手之所以是高手,表现之一就是超级忙。
- 于是代码的作者开始,一行一行的将代码讲下去。前几十分钟高手们也没办法理解——毕竟是一个星期的工作沉淀,哪有那么快理解的。大约30分钟之后,专家开始提出建议意见。这些意见一般涵盖了语法、编码规范、可能的业务错误、模块间关系等。专家们毕竟是专家。2个小时之后,专家们离去。
- 开发人员虚心的把这些意见、建议写到小本子上。
- 开发人员可能根据专家的建议进行相应的代码修改并且提交,也可能不;可能改对了,也可能不。评审过后,后续的实施成为黑洞……
没了。
先思考一下,这个过程中的问题。
======== 思考的分割线 ========
首先必须承认Code Review的价值。经验丰富的专家们在做代码审查的时候,能够根据以往经验,规避重大缺陷的发生,对开发人员给予有价值的指导。然而,这个过程,太冗长,太低效。
- Code Review必须基于事实。这里的事实,就是,源代码库。SVN Repository, 或者HG/Git Repository. 在多人协作环境中,对于一份不在源代码库代码是基本不可信的 —— 你无法预知,他是否将会成为最终可工作软件的一部分。
- 积攒下来众多的代码修改,使得产生重型、低效的沟通方式成为必然。这类众型的沟通方式往往成本惊人 - 需要占用最好的人很长时间。
- 过分夸大专家的作用。根据以往经验,许多最终发现问题,回溯上来,其实是一些简单的逻辑问题。这些问题如果分散在平时结对或者更频繁的过程中,则更容易发现。很多情况下,是开发人员对常见的bad smell了解和修炼不足,而这些bad smell常常是导致问题的地方。例如在一个已经有3重循环的方法中加入了新的判断而没有测试;修改了函数的返回值而没有任何说明;if 判断中包含了多达4-5个变量的比较判断而没有抽象为一个更具业务含义的方法,等等。
- Review手段的原始落后。Review必须基于变化。会看报表的人都知道,看报表只需要看两个东西:趋势和拐点。Code Review也一样,只需要看变化。SVN/Hg/Git这类现代化的工具给我们提供了丰富的,基于changeset的compare工具。查看一天,整个团队的check in情况,顶多只需要10分钟-15分钟。
在敏捷过程中,Code Review几乎是一个被忽视的环节——不是不做,而是时时在做。结对时,我们会对结对伙伴的编码习惯、新写的类、变量表示质疑;提交之后,有代码静态检查工具、单元测试工具、覆盖率工具帮助我们检查有没有犯简单愚蠢的错误、有没有破坏既有功能;持续集成服务器则中立、不知疲倦的在每次我们提交之后运行所有的过程。
Code Review不是一个审查环节。不是一个考核环节。它是交流和反馈环节。
先看一看我们看到的一个代码审查过程:
- 开发人员领到任务。
- 一周之后,代码写完了。他觉得没底,需要找业务专家技术专家来评审一下。这个时候他代码还没有提交。于是他把本地所有没有提交的、修改过后的文件,放到一起,压缩成rar包。找到他认为的技术高手业务高手(们),定会议室,发邮件通知。2小时过去了。
- 技术专家业务专家收到了邮件,由于缺乏上下文理解,以及长达数千行的源代码,这类邮件一般不看——因为看了也是白看。
- 终于到了Code Review的那一天。七七八八的来了几个人。一般来不全。因为高手之所以是高手,表现之一就是超级忙。
- 于是代码的作者开始,一行一行的将代码讲下去。前几十分钟高手们也没办法理解——毕竟是一个星期的工作沉淀,哪有那么快理解的。大约30分钟之后,专家开始提出建议意见。这些意见一般涵盖了语法、编码规范、可能的业务错误、模块间关系等。专家们毕竟是专家。2个小时之后,专家们离去。
- 开发人员虚心的把这些意见、建议写到小本子上。
- 开发人员可能根据专家的建议进行相应的代码修改并且提交,也可能不;可能改对了,也可能不。评审过后,后续的实施成为黑洞……
没了。
先思考一下,这个过程中的问题。
======== 思考的分割线 ========
首先必须承认Code Review的价值。经验丰富的专家们在做代码审查的时候,能够根据以往经验,规避重大缺陷的发生,对开发人员给予有价值的指导。然而,这个过程,太冗长,太低效。
- Code Review必须基于事实。这里的事实,就是,源代码库。SVN Repository, 或者HG/Git Repository. 在多人协作环境中,对于一份不在源代码库代码是基本不可信的 —— 你无法预知,他是否将会成为最终可工作软件的一部分。
- 积攒下来众多的代码修改,使得产生重型、低效的沟通方式成为必然。这类众型的沟通方式往往成本惊人 - 需要占用最好的人很长时间。
- 过分夸大专家的作用。根据以往经验,许多最终发现问题,回溯上来,其实是一些简单的逻辑问题。这些问题如果分散在平时结对或者更频繁的过程中,则更容易发现。很多情况下,是开发人员对常见的bad smell了解和修炼不足,而这些bad smell常常是导致问题的地方。例如在一个已经有3重循环的方法中加入了新的判断而没有测试;修改了函数的返回值而没有任何说明;if 判断中包含了多达4-5个变量的比较判断而没有抽象为一个更具业务含义的方法,等等。
- Review手段的原始落后。Review必须基于变化。会看报表的人都知道,看报表只需要看两个东西:趋势和拐点。Code Review也一样,只需要看变化。SVN/Hg/Git这类现代化的工具给我们提供了丰富的,基于changeset的compare工具。查看一天,整个团队的check in情况,顶多只需要10分钟-15分钟。
在敏捷过程中,Code Review几乎是一个被忽视的环节——不是不做,而是时时在做。结对时,我们会对结对伙伴的编码习惯、新写的类、变量表示质疑;提交之后,有代码静态检查工具、单元测试工具、覆盖率工具帮助我们检查有没有犯简单愚蠢的错误、有没有破坏既有功能;持续集成服务器则中立、不知疲倦的在每次我们提交之后运行所有的过程。
Code Review不是一个审查环节。不是一个考核环节。它是交流和反馈环节。
我姓--]苏(14044522) 10:16:52
全局设计!与规范设计
膘叔(19129540) 10:17:03
高级。。。
我姓--]苏(14044522) 10:19:41
我个人理解
高级编程不再单纯的程序功能的编写,还要有全局的规范,如OOP的结构规范,还有可扩展的思考,整体逻辑设计,代码冗余,执行效率,二次重构的可行性
我姓--]苏(14044522) 10:19:44
等
我姓--]苏(14044522) 10:19:55
很多 总觉得很多
我姓--]苏(14044522) 10:20:04
还有对外的接口
懒宝宝(3296320) 10:20:03
对
懒宝宝(3296320) 10:20:06
我同意
我姓--]苏(14044522) 10:21:05
特别是多人一起开发的时候!!结构规范很重要!
我姓--]苏(14044522) 10:22:02
像小公司!前期没那么时间去做需要分析,很多需要一个比较有经验的人去主导
膘叔(19129540) 10:22:08
代码冗余是很正常的。
膘叔(19129540) 10:22:27
只是冗余的部分以后要能够被统一的优化就行了。
我姓--]苏(14044522) 10:22:29
这个是正常!!但要尽量减少
我姓--]苏(14044522) 10:22:58
多个人写的很难优化,每个人写的模式不一样!
膘叔(19129540) 10:23:40
有一个人统筹一下就行了。
我姓--]苏(14044522) 10:23:47
我在前面的一项目中就是这样!!没一个主导!做的很乱!!
膘叔(19129540) 10:24:49
多个人在开发,加上一个人不停的规划,修补,优化
会好很多
膘叔(19129540) 10:25:03
同时把修补的意见贴出来,以后改进
我姓--]苏(14044522) 10:25:11
有时候那么好!!成本比较大
我姓--]苏(14044522) 10:25:18
小公司不可能
膘叔(19129540) 10:25:39
呵呵。写到哪里算哪里了。
我姓--]苏(14044522) 10:25:49
人都控制的很紧!!
膘叔(19129540) 10:26:11
第一期,多人写同一个模块。首次整合后,调整各人的风格。再磨合一次。然后各写各的模块 。
我姓--]苏(14044522) 10:27:12
这是不错的想法!!但有时候老板天天要看到新东西!如果这样!他会觉得你没做东西
膘叔(19129540) 10:29:16
每一个月别想看新东西。
我姓--]苏(14044522) 10:29:41
是呀!!我前面一周就要让老板看到新东西
我姓--]苏(14044522) 10:29:53
不能管他好坏
膘叔(19129540) 10:31:15
那就做页面,让老板看页面,美工辛苦点。
膘叔(19129540) 10:31:22
这样,程序就可以先借美工挡着点了。
我姓--]苏(14044522) 10:32:12
我最不喜欢老板懂又不懂那种!!那种很累人的!!有时候要解释半天
我姓--]苏(14044522) 10:32:50
我现在基本是说几天看一次!!其它时间不要问!给你看程序你也看不懂!没用
我姓--]苏(14044522) 10:34:27
我现在最怕美工!每次我这边模块什么写好了!!跟BOSS说明天可以看成品!美工每次都要拖今天晚上才做好!这样我又得做前端!前端模板要写一下!很受累
我姓--]苏(14044522) 10:35:43
有时候我是页设计不好看!不然真他妈的想自己设计
全局设计!与规范设计
膘叔(19129540) 10:17:03
高级。。。
我姓--]苏(14044522) 10:19:41
我个人理解
高级编程不再单纯的程序功能的编写,还要有全局的规范,如OOP的结构规范,还有可扩展的思考,整体逻辑设计,代码冗余,执行效率,二次重构的可行性
我姓--]苏(14044522) 10:19:44
等
我姓--]苏(14044522) 10:19:55
很多 总觉得很多
我姓--]苏(14044522) 10:20:04
还有对外的接口
懒宝宝(3296320) 10:20:03
对
懒宝宝(3296320) 10:20:06
我同意
我姓--]苏(14044522) 10:21:05
特别是多人一起开发的时候!!结构规范很重要!
我姓--]苏(14044522) 10:22:02
像小公司!前期没那么时间去做需要分析,很多需要一个比较有经验的人去主导
膘叔(19129540) 10:22:08
代码冗余是很正常的。
膘叔(19129540) 10:22:27
只是冗余的部分以后要能够被统一的优化就行了。
我姓--]苏(14044522) 10:22:29
这个是正常!!但要尽量减少
我姓--]苏(14044522) 10:22:58
多个人写的很难优化,每个人写的模式不一样!
膘叔(19129540) 10:23:40
有一个人统筹一下就行了。
我姓--]苏(14044522) 10:23:47
我在前面的一项目中就是这样!!没一个主导!做的很乱!!
膘叔(19129540) 10:24:49
多个人在开发,加上一个人不停的规划,修补,优化
会好很多
膘叔(19129540) 10:25:03
同时把修补的意见贴出来,以后改进
我姓--]苏(14044522) 10:25:11
有时候那么好!!成本比较大
我姓--]苏(14044522) 10:25:18
小公司不可能
膘叔(19129540) 10:25:39
呵呵。写到哪里算哪里了。
我姓--]苏(14044522) 10:25:49
人都控制的很紧!!
膘叔(19129540) 10:26:11
第一期,多人写同一个模块。首次整合后,调整各人的风格。再磨合一次。然后各写各的模块 。
我姓--]苏(14044522) 10:27:12
这是不错的想法!!但有时候老板天天要看到新东西!如果这样!他会觉得你没做东西
膘叔(19129540) 10:29:16
每一个月别想看新东西。
我姓--]苏(14044522) 10:29:41
是呀!!我前面一周就要让老板看到新东西
我姓--]苏(14044522) 10:29:53
不能管他好坏
膘叔(19129540) 10:31:15
那就做页面,让老板看页面,美工辛苦点。
膘叔(19129540) 10:31:22
这样,程序就可以先借美工挡着点了。
我姓--]苏(14044522) 10:32:12
我最不喜欢老板懂又不懂那种!!那种很累人的!!有时候要解释半天
我姓--]苏(14044522) 10:32:50
我现在基本是说几天看一次!!其它时间不要问!给你看程序你也看不懂!没用
我姓--]苏(14044522) 10:34:27
我现在最怕美工!每次我这边模块什么写好了!!跟BOSS说明天可以看成品!美工每次都要拖今天晚上才做好!这样我又得做前端!前端模板要写一下!很受累
我姓--]苏(14044522) 10:35:43
有时候我是页设计不好看!不然真他妈的想自己设计
################################################################################
#
# Common definitions for Makefiles of CGIs of Business Monitor Framework.
#
# History:
# <author> <time> <version > <desc>
# moky mo 2005/07/13 0.1 create
################################################################################
CC=g++
CFLAGS = -g -Wall -DDEBUG -DLINUX_OS -lrt -lpthread
BINARY = $(patsubst %.c,%,$(wildcard *.c))
all:$(BINARY)
%:%.cpp
$(CC) $(CFLAGS) -o $@ $<
strip $@
dir:
mkdir -p ${CGI_BIN_ALL}
install:$(BINARY)
cp $(BINARY) $(CGI_BIN_ALL)
clean:
rm -f $(BINARY)
#EOF
#
# Common definitions for Makefiles of CGIs of Business Monitor Framework.
#
# History:
# <author> <time> <version > <desc>
# moky mo 2005/07/13 0.1 create
################################################################################
CC=g++
CFLAGS = -g -Wall -DDEBUG -DLINUX_OS -lrt -lpthread
BINARY = $(patsubst %.c,%,$(wildcard *.c))
all:$(BINARY)
%:%.cpp
$(CC) $(CFLAGS) -o $@ $<
strip $@
dir:
mkdir -p ${CGI_BIN_ALL}
install:$(BINARY)
cp $(BINARY) $(CGI_BIN_ALL)
clean:
rm -f $(BINARY)
#EOF
解释如下,重点解释:
BINARY = $(patsubst %.c,%,$(wildcard *.c))
和:
$(CC) $(CFLAGS) -o $@ $<
里面的$@是当前文件夹里所有的文件名(不包换.c),而$<则表示该文件夹里面所有的.c文件!
参考:http://blog.csdn.net/yang_dk/archive/2008/02/24/2117414.aspx
http://linux.chinaunix.net/bbs/thread-976329-1-1.html
我在txt1中输了2,在txt2中输了3,运算cal()后,结果txt3中得到的是23而不是5,请教各位怎么处理?代码如下:
<script language=JavaScript>
function cal()
{
if (document.form1.txt1.value.length>0&&document.form1.txt2.value.length>0)
{
document.form1.txt3.value=document.form1.txt1.value+document.form1.txt2.value;
}
}
</script>
parseInt(numstring, [radix])
The parseInt method syntax has these parts:
Part Description
numstring Required. A string to convert into a number.
radix Optional. A value between 2 and 36 indicating the base of the number contained in numstring. If not supplied, strings with a prefix of '0x' are considered hexidecimal and strings with a prefix of '0' are considered octal. All other strings are considered decimal.
把字符串转换成数字,要习惯去查msdn或者chm文件
document.form1.txt3.value=parseInt(document.form1.txt1.value)+parseInt(document.form1.txt2.value);
function cal()
{
var a = document.form1.txt1.value;
var b = document.form1.txt2.value;
if (a.length>0 && b.length>0)
{
document.form1.txt3.value = parseFloat(a) + parseFloat(b);
}
}
<script language=JavaScript>
function cal()
{
if (document.form1.txt1.value.length>0&&document.form1.txt2.value.length>0)
{
document.form1.txt3.value=document.form1.txt1.value+document.form1.txt2.value;
}
}
</script>
parseInt(numstring, [radix])
The parseInt method syntax has these parts:
Part Description
numstring Required. A string to convert into a number.
radix Optional. A value between 2 and 36 indicating the base of the number contained in numstring. If not supplied, strings with a prefix of '0x' are considered hexidecimal and strings with a prefix of '0' are considered octal. All other strings are considered decimal.
把字符串转换成数字,要习惯去查msdn或者chm文件
document.form1.txt3.value=parseInt(document.form1.txt1.value)+parseInt(document.form1.txt2.value);
function cal()
{
var a = document.form1.txt1.value;
var b = document.form1.txt2.value;
if (a.length>0 && b.length>0)
{
document.form1.txt3.value = parseFloat(a) + parseFloat(b);
}
}
[root@my htdocs]# php -v
PHP 5.3.27 (cli) (built: Aug 12 2013 12:22:48)
升级后:
[root@iZ25z0ugwgtZ ~]# php -v
PHP 5.6.18 (cli) (built: Feb 13 2016 13:05:48)
升级后,出现:
ErrorException [ 2 ]: fopen(Unknown): failed to open stream: No such file or directory ~ SYS_PATH/core.php [ 865 ]
===================================================================
回退回5.3版本,如下:
[ZendGuardLoader-php-5.3-linux-glibc23-x86_64.tar.gz] found
ZendGuardLoader module install successfully!
Gracefully shutting down php-fpm . done
Starting php-fpm done
####################Congratulations########################
PHP install dir: /usr/local/php
eAccelerator Control Panel url: http://101.200.189.210/control.php
eAccelerator user: admin
eAccelerator password: eAccelerator
——————————————————————————————————————————————————————————
第一较大步骤:
学习这个Kohana这个框架的真正在于它就像雨燕一样的灵活,所以,先了解一下这个是怎么通过路由实现了其多域名共用一套完整代码的松耦合,且以mvc的高内聚的框架思想是首页步骤,如下:
只要三个步骤实现一套代码两套域名,还可实现看似一套代码且可独立部署,且还实现了域名的分离,这也是Kohana这个框架的灵活之处,更多请看:
http://jackxiang.com/post/7122/
http://jackxiang.com/post/5977/
____________________________________在controller里建立interface目录____________________________________
步骤一:mkdir /data/htdocs/boosh.com/application/controllers/internal (注意controller调用里面的类名前缀和路由保持一致。)
问题:框架controller层层调用情况,以新加一个接口域名internal.boosh.com,但代码还得放一块,而这个域名不一致,怎么办?
解决办法,先看框架controller层层调用情况:
./application/controllers/internal/base.php:abstract class internal_BaseController extends InitController //类名和文件夹名一样第一个字大写是规范,而和前面建立的目录对应的前缀表明这个类是在这个目录下的,这块的目录internal定后,其下面的类名必须以目录名且首字母大写作为前缀,这是因为request.php里定死了的,如下代码:
$prefix .= ucfirst($directory) . '_';//形成Internal_
$class = new ReflectionClass(ucfirst($prefix) . ucfirst($this->controller) . 'Controller');//这个controller也就是文件名如/internal/user.php里也就是User,class Internal_UserController extends Internal_BaseController ,user里的U大写即可。
——————————————————————————————————————————————————
./application/controllers/global.php:class InitController extends Controller
./libraries/controller.php abstract class Controller
再次对这个
./application/controllers/internal/base.php:abstract class internal_BaseController extends InitController //类名和文件夹名一样?这个是和路由的php代码设置及nginx的转写规则有关。
require_once dirname(dirname(__FILE__)) . '/global.php';
继承自:
./application/controllers/global.php:class InitController extends Controller
继承自:
./libraries/controller.php abstract class Controller
这个名称都可随便写,如base.php,但里面的类前缀部分也得变得有关联更符合要求,这块不规范像这样:
如:./application/controllers/internal/my.php
require_once 'base.php';
class Internal_MyController extends Internal2_BaseController
//class Internal_MyController extends Internal_BaseController
./application/controllers/internal/base.php也一样得有关联:
require_once dirname(dirname(__FILE__)) . '/global.php';//前面上一层目录的global.php
//abstract class Internal_BaseController extends InitController
abstract class Internal2_BaseController extends InitController //加个2后,在my.php里也加个2继承下,也没关系运行没有问题,只是不规范罢了。
为何直接写没有自己写那个new 这个controller类就运行了呢?是因为两个地方:
1)\libraries\request.php里的execute对目录进行分析,对controller类进行反射类实例化:
(1)internal目录进行处理:
if ($this->directory) {
// Add the directory name to the class prefix
$directory = str_replace (array('\\', '/', '//'), '_', trim($this->directory, '/'));
$directories = explode('_', $directory);
foreach ($directories as $directory) {
$prefix .= ucfirst($directory) . '_';
}
}
(2)对反射类进行处理,找不到文件则到core.php来进行查找定位处理:
try {
// Load the controller using reflection
$class = new ReflectionClass(ucfirst($prefix) . ucfirst($this->controller) . 'Controller');
if ($class->isAbstract()) {
throw new KoException('Cannot create instances of abstract controller: :error', array(':error' => ucfirst($this->controller) . 'Controller'));
}
} catch (Exception $e) {
if ($e instanceof ReflectionException) {
$this->status = 404;
} else {
$this->status = 500;
}
$this->sendHeaders();
exit(0);
}
用了do while串连起这个反射类,如知识点如下:
(3)为何不同的目录可以实现根据nginx里的d参数两个不同的域名呢,是因为下面这块实现的levoo.com/openapi === api.levoo.com:
一套代码通过nginx转写d到不同目录后,最终调用的都是:global.php,如下:
/data/htdocs/levoo.com/application/controllers/manage.php//class ManageController extends FrontController
/data/htdocs/xiyou.cntv.cn/application/controllers/front.php//class FrontController extends InitController
/data/htdocs/xiyou.cntv.cn/application/controllers/global.php//class FrontController extends InitController
/data/htdocs/levoo.com/application/controllers/openapi/manage.php
class Openapi_ManageController extends Openapi_BaseController //它们最终都直接继承了global.php文件
require_once dirname(dirname(__FILE__)) . '/global.php'; //class InitController extends Controller
最后,execute全部代码如下,试图调度控制器/动作。如果要求表示它需要被调度,移动到下一个请求行动。
(2)core.php的autoload和findFile找文件这个查找的core.php是在fontend里的index.php包含bootstrap.php再包含core.php引入的,给反射类的查找文件指名了方向,在bootstrap.php最后一行(Request::instance()->dispatch();)便是自动根据core.php里的__autoload,找到request.php文件进行操作里面的相关函数的一个立即体现(这块的core.php直接引入太关键了),\application\bootstrap.php:
自己重新定了autoload,而不是__autoload:
将__autoload换成loadprint函数。但是loadprint不会像__autoload自动触发,这时spl_autoload_register()就起作用了,它告诉PHP碰到没有定义的类就执行loadprint()。
/data/htdocs/xiyou.cntv.cn/application/bootstrap.php:spl_autoload_register(array('Ko', 'autoload')); //这个是调用静态的方法:
spl_autoload_register() 调用静态方法
摘自:http://blog.csdn.net/panpan639944806/article/details/23192267
更多分析再参考自己写的这篇文章:http://jackxiang.com/post/5877/
步骤二:路由配置如下
/data/htdocs/my.boosh.com/application/bootstrap.php //把internal的路由指向前面建立的目录
Route::set('internal_route', 'internal(/<controller>(/<action>(/<__KO_VARS__>)))', array('__KO_VARS__' => '.+'))
->defaults(array(
'directory' => 'internal',//下面配置的Nginx指向这个目录并作转写即可:d=internal。
'controller' => isset($default_controller) ? $default_controller : 'index',
'action' => isset($default_action) ? $default_action : 'index',
));
这块路由的名称其实不重要,加个2也是可以的:
Route::set('internal2_route', 'internal(/<controller>(/<action>(/<__KO_VARS__>)))', array('__KO_VARS__' => '.+'))
为何配置这个bootstrap.php呢?htdocs的入口查看:
/data/htdocs/my.boosh.com/frontend/index.php
// Bootstrap the application
require_once APP_PATH . 'bootstrap.php';
步骤三:Nginx的配置文件
server
{
listen 80;
server_name i.boosh.com boosh.com;
index index.html index.htm index.php;
root /data/htdocs/my.boosh.com/frontend;///data/htdocs/boosh.com/frontend;被域名www.boosh.com用了,所以,my.boosh.com用作http://boosh.com使用。
location / {
rewrite "^/api/(album|general|partner|rank|search|user|video|stb|navigation|app|mobile|public|oauth|my|activity|manage|oauth2|favorite)\.([a-zA-Z]{4,})$" /index.php?d=internal&c=$1&a=$2&$args last;
rewrite "^/(album|general|partner|rank|search|user|video|stb|navigation|app|mobile|public|oauth|my|activity|manage|oauth2|favorite)/([a-zA-Z]{4,})$" /index.php?d=internal&c=$1&a=$2&$args last; //这一行必须要有,否则就没法通过get的d参数实现路由,也就是说即使后面分离后,前面新加的internal目录不用挪动,是nginx转写时加上了d,对album这些写死在nginx配置里的action进行加上d=internal,进而框架里的bootstrap.php进行了路由设置且支持路由。
前端访问情况:
直接通过api的接口访问:
http://i.boosh.com/album/addalbum (新加的域名访问A接口)
直接通过主站进行加上一个api进行访问:
http://boosh.com/api/album/addalbum(主站直接也可访问这个A接口)
对于接口输出比如ajax的json示例,此时一般都是关闭了模板,直接用自带函数进行输出,如下:
http://levoo.com/activityvideo/getactivityvideo/eid/14544893367247/typeid/1/pagenum/11/pagesize/20/sortby/new/format/jsonp/jsoncallback/cmscallback
=================View与smarty的一个接合问题=======================
前面新的一个interface没有和主站一个目录,而是另一个目录里,如何与smarty搭上边呢?
vi base.php
require_once dirname(dirname(__FILE__)) . '/global.php'; //与上一层目录的global.php进行引用,也就是主站的global.php
这个global.php主要干些啥?
1)获取进来的各种jsonP参数:
$this->varname = trim($this->getRequest()->getParam('varname'));
$this->jsoncallback = trim($this->getRequest()->getParam('jsoncallback'));
2)失败输出json/输出xml。
3)读取写入配置文件。
4)设置语言包位置及路径:css/js/images。
5)是否自动输出模板。
6) 一些共性的函数操作放里面,特别是像登录啥的。
interface/my.php
这就是真正的controller了,里面写这个页面特别的一些输出及模板渲染,经典的controller/action。
总之,就是通过view的工厂类, $this->autoRender(FALSE); 这个是不输出模板不需要smarty。要输出刚好是True,输出用了smarty,主站需要用,它是写在/controller/front.php里的,$this->view = View::factory($this->themesPath['themePath'] . '/' . $this->request->getController() . '/' . $this->request->getAction());,这一行调用了:\libraries\view.php,它引入了:require_once 'smarty/Smarty.class.php';且view.php初始化了smarty这个类,如下:
public function __construct ($file = NULL, array $data = NULL)
{
if (! empty($file)) {
$this->setView($file);
}
$this->_smarty = new Smarty();
if ($data !== NULL) {
$this->_smarty->assign($data);
}
$this->_smarty->template_dir = Ko::config('smarty.template_path');//这个是绝对路径前缀读取的是./smarty.php: 'template_path' => APP_PATH. 'views/',静态的KO类实现读取配置文件,Nginx指向->\frontend\index.php 包含=》\application\bootstrap.php=》它里面引用:require_once SYS_PATH . 'core.php'; =》public static function config ($group)静态函数实现。。
$this->_smarty->cache_dir = Ko::config('smarty.cache_path');
$this->_smarty->compile_dir = Ko::config('smarty.compile_path');
$this->_smarty->config_dir = Ko::config('smarty.configs_path');
并提供了,__set ,__get,render这些基础函数的二次根据这个view类的smarty封装。实现了smarty模板类。
模板问题再细一点:
出现模板找不到:The requested view themes/zh/my/video.html could not be found ~ SYS_PATH/view.php [ 174 ]
(这里的view.php并没有被直接包含,而是框架core.php里autoload和findFile经过在new类时查找到\libraries\目录并引入:public static function autoload ($class),根据文件名及类名引入的, Ko::autoload('My_Class_Name');class_exists找不到则去findFile查找并引入:require_once $path;。)
这就是这些类查找也好,命名也好的规范,对Controller Model Service,影响了其类名的写法必须符合,否则找不到,见libraries/core.php:
controller和Models和server分层在类名上体现是必要的,
无聊咔咔(5**773145) 17:52:56
__autoload
回忆未来-向东-Jàck(372647693) 17:53:21
加上findFile
一般框架都这样,MVC啥的全是这样搞的,有谁有新方法找我。
把找到的文件目录给缓存起来,下次就不用去再找一次了。
再把controller /action的类名加上比如Controller之类,
规范规范,加个smarty,一个PHP的架子就搭建起来了嘛要。
回忆未来-向东-Jàck(372647693) 17:57:30
controller和Models和server分层在类名上体现是必要的,如下:
class EventModel extends Model
class NewsService extends AdminBaseService
class ActController extends FrontController
findFile这个是围绕着环境变量且伴随是否caching进行编写的,主要是这两个核心的全局数组,步骤如下:
private static $_paths = array(APP_PATH , SYS_PATH);// Include paths that are used to find files
private static $_files = array(); // File path cache
1)如果是Cahceing就在self::$_files就是一个一个的文件以文件名为key,值就是文件绝对路径的数组里根据传入的文件名进行查找对应的路径,有则直接返回。
2)没有则对'config' 'i18n' 'messages'进行查找,这三个地方估计没法cache进来,得特殊处理。
3)还找不到,则self::$_paths ,这个是一堆的路径数组,一个一个去把文件名放进去拼成绝对路径,再用is_file去循环所有路径判断这个文件到底在哪儿,直到找到这个文件的绝对路径。
4)对这个文件的路径再放回到第1部里的self::$_files里去,同时返回这个文件的绝对路径,autoload 里的require_once $path;就是引入经过一堆查找到的文件,真心不容易啊。
__上面描述为何能自动找到这个View.php,Smarty被它重新封装并形成factory进行重新对象化,如何使用smarty如下:______
public function video()
{
$themesPath = $this->getLang(); //global.php里写好路径,且是相对路径,所以报错也是相对的,但输入smarty时拼成一个绝对路径,如下:
//[root@iZ25z0ugwgtZ config]# grep -r "views" ./
//./smarty.php: 'template_path' => APP_PATH. 'views/',
$this->themesPath = $themesPath;
$this->view = View::factory($this->themesPath['themePath'] . '/' . $this->request->getController() . '/' . $this->request->getAction());
$this->view->textlinkhotmax = "jackxiang";
$this->view->specialmax = "xiangdong";
$this->setFile("my/video");//如果不写,默认则是http://xxx.com/controller/funciton.html为模板。 https://boosh.com/my/video =>my目录下的video.html。
}
模板路径:/application/views/themes/zh/my/video.html 前面的这个路径是从/application/controllers/global.php 里写死一数组定义的:
[root@iZ25z0ugwgtZ my]# cat ~+/video.html
jack
<{$textlinkhotmax}>
<{$specialmax}>
访问输出:
jack jackxiang xiangdong
下面主要是core.php和request.php两个,core.php主要是autoload和findFile的一个结合,及reques.php是路由Route和反射类ReflectionClass的一个集合,最后是两个文件对新纳入文件的文件命名和类命名的的约束,形成KO的核心的框架。
第二大步,再谈一谈其高内聚合、低耦合的一些设计模式,如下:
在很多时候反射也是唯一的选择。为什么我们会选择使用反射?因为我们没有办法在编译期通过静态绑定的方式来确定我们要调用的对象。例如一个ORM框架,它要面对的是通用的模型,此时无论是方法也好属性也罢都是随应用场景而改变的,这种完全需要动态绑定的场景下自然需要运用反射。还例如插件系统,在完全不知道外部插件究竟是什么东西的情况下,是一定无法在编译期确定的,因此会使用反射进行加载。其实,包同学的反驳文章里也是持这种观点的:
什么是php反射类,顾名思义,可以理解为一个类的映射。
举个例子:
class fuc { //定义一个类
static function ec() {
echo '我是一个类';
}
}
$class=new ReflectionClass('fuc'); //建立 fuc这个类的反射类
echo $class; //输出这反射类
Class [ class A ] { @@ F:\phpweb\myPHP\test.php 23-30 - Constants [0] { } - Static properties [0] { } - Static methods [0] { } - Properties [0] { } - Methods [1] { Method [ public method __construct ] { @@ F:\phpweb\myPHP\test.php 26 - 29 } } }
$fuc=$class->newInstance(); //相当于实例化 fuc 类
$fuc->ec(); //执行 fuc 里的方法ec
/*最后输出:我是一个类*/
其中还有一些更高级的用法
$ec=$class->getmethod('ec'); //获取fuc 类中的ec方法
$fuc=$class->newInstance(); //实例化
$ec->invoke($fuc); //执行ec 方法
----------------------------------------------------------------------------------------------------------------------------------------------------------------------
$class->getmethod('ec')-> invokeArgs($fuc, $params); //有点绕:class是反射类,获取这个反射类里的ec方法,用实例化后的$fuc,并传入相关ec的参数。
对该方法的传入参数及调用的方法实践OK如下:
输出:
---------- 调试PHP ----------
getAPI函数里的apiNO参数:1
Array
(
[apiName] => getVideo
[apiIntro] => getVideo from Server
)
是通过数组进行扩展多个参数的测试:第三个参数
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
$methodsArrAll = $class->getMethods(); //获取所有的方法名
Reflection::export($class);//通过此方法可以看到ReflectionClass也就是fuc类中所有的属性和方法
上面的过程很熟悉吧。其实和调用对象的方法类似
只不过这里是反着来的,方法在前,对象在后
举例: 这样的方法实现在面像对象的框架里有这样的用法,见:http://kohanaframework.org/ request.php
框架核心摘录解析\libraries\request.php,涉及到before/after(写日志)函数的先后,特别注意:
来自:http://blog.csdn.net/21aspnet/article/details/6952432
给redirect函数里加上日志:
找不到action和对应方法时调用404.
/**
* 当调用的action不存在时调用该方法
* Overload
*
* @see application/controllers/InitController::__call()
*/
public function __call($method, $arguments=NULL)
{
$this->redirect(KO::config('url.main_url') . 'error.html');
}
———————————直接访问Url里的一个controler下用action直接输出调用forward时在调核心request类里的函数返回$this是什么的理解。—————————
这里的 $this->request是从controller.php里继承来的:
class ApiController extends InitController,class InitController extends Controller。
public function __construct (Request $request)
{
// Assign the request to the controller
$this->request = $request;
}
而forward函数是request.php里的:
/**
* Forward to another controller/action.
*
* @param string $action
* @param string $controller
* @param string $directory
* @param array $params
* @return void
*/
public function forward($action, $controller = null, $directory = null, array $params = null)
{//方法,文件类名,文件路径(默认是框架路径,这儿可在框架路径下建立自己的路径),参数(是通过__call: public function __call($method, $arguments = null) 传入的)
。。。。。。
这些都是教简单,而return正是在request.php里函数常用到,如下:
/**
* Set parameters
*
* Set one or more parameters. Parameters are set as userland parameters,
* using the keys specified in the array.
*
* @param array $params
* @return Request
*/
public function setParams(array $params)
{
foreach ($params as $key => $value) {
$this->setParam($key, $value);
}
return $this;
}
/**
* Set request directory
*
* @param string
* @return Request
*/
public function setDirectory ($directory)
{
echo "<br>".__FILE__.__LINE__.$directory."<br>";
$this->directory = $directory;
return $this;
}
setParams和setDirectory都是forward里的,如下:
public function forward($action, $controller = null, $directory = null, array $params = null)
{
if (null !== $params && is_array($params)) {
$this->setParams($params);
}
if (null !== $controller) {
$this->setController($controller);
// Directory should only be reset if controller has been specified
if (null !== $directory) {
echo "Here,Jack...";
$this->setDirectory($directory);
}
}
$this->setAction($action)
->setDispatched(false);
}
这里的return this如何理解:
http://zhidao.baidu.com/link?url=QU-yClJDjln0YRwEZr8nO2wBLUIqDx4oPeYL6BykgCZvrclh78YpY9qnsfRaYWZam87QCaMEdmk06ZGd_DKY2q
这是类里面的一个函数,没有形参,函数里最后一句是return $this; 请问这个是返回了什么东西啊?返回了谁的自身?
public function getmodule() {
$this->cache->key = 'sdmodule';
$result;
.....
....//中间很多句子。
....
$this->smarty->assign("modules", $result);
return $this;
}
就是返回这个对象,在PHP类里面$this关键字就是代表这个类内部的引用。如你上面所说的return $this;就是相当于把该对象返回到方法getmodule() 中。
比如:
$abc=new class; //class是指你那个类
echo $abc->getmodule()->cache->key; //输出sdmodule
也就是getmodule() 拥有了该类的所有的成员和方法。
—————————通过forward方法传入和通过Request::instance()->dispatch();的区别(instance获取uri后调__construct进行action,controller,路径path的类变量赋值实现了类似forward的传参)—————————
/**
* Main request singleton instance. If no URI is provided, the URI will
* be automatically detected using PATH_INFO, REQUEST_URI, or PHP_SELF.
*
* @param string URI of the request
* @return Request
*/
public static function instance ($uri = TRUE)
{
static $instance;
if ($instance === NULL) {
.......//各种获取Uri的方法
$instance = new self($uri);//把uri传入类中,下面:
// public function __construct ($uri)//它就是用来对uri进行解析得到forward里的a,c,d。forward只是直接传入罢了,不用在这儿初始化就在于此,直接传入修改了类需要运行的相关变量,除开get参数外,都在这里进行分析并写入类全局变量,通过this作为句柄作操作。
//
}
return $instance;
}
forward和__construct的相同点和不同点:
public function __construct ($uri)
{
Ko::log('debug', __CLASS__ . ' Library loaded');
// Are query strings enabled in the config file?
// If so, we're done since segment based URIs are not used with query strings.
if (Ko::$enable_query_strings && isset($_GET['c'])) {
$this->setController(Security::xss_clean($_GET['c']));
if (isset($_GET['a'])) {
$this->setAction(Security::xss_clean($_GET['a']));
}
if (isset($_GET['d'])) {
$this->setDirectory(Security::xss_clean($_GET['d']));
}
$this->params = array();
return;
}
......对接受到的变量赋值到类变量后立即进行安全检查并过滤......
foreach ($routes as $route) {
if (($params = $route->matches($uri)) !== false) {
$this->directory = Security::xss_clean($params['directory']);
$this->controller = Security::xss_clean($params['controller']);
$this->action = Security::xss_clean($params['action']);
$this->params = $params;//变量从uri里获取到的
return;
}
}
//上面找不到的则进行404处理并输出找不到的头页面处理
}
而在执行时这个this-;>params还会用到:
function excute(){
if ($this->isDispatched()) {
$class->getMethod($action)->invokeArgs($controller, $this->params);//这儿
}
你会问这个POST,GET参数去哪儿了?在request;.php里有:
public function getParam ($key, $default = NULL)
{//对各种变量都作了xss检测
if (isset($this->params[$key])) {
return Security::xss_clean($this->params[$key]);
} elseif (isset($_GET[$key])) {
return Security::xss_clean($_GET[$key]);
} elseif (isset($_POST[$key])) {
return Security::xss_clean($_POST[$key]);
} else {
return $default;
}
}
再就是通过_set,_get进行属性读取,参看:php面向对象_get(),_set()的用法,http://blog.sina.com.cn/s/blog_4565cc770100bv2u.html
http://jackxiang.com/post/2766/ 里面有代码对其实践和论述。
在实际编写代码调用:
public function minialbum(){
$uuid = $this->getRequest()->getParam('uuid');
public function setController ($controller)
{
$this->controller = $controller;
return $this;
}
//而forward是自己写的,所以就不用进行安全的xss检测
public function forward($action, $controller = null, $directory = null, array $params = null)
{
if (null !== $params && is_array($params)) {
$this->setParams($params);
}
if (null !== $controller) {
$this->setController($controller);
// Directory should only be reset if controller has been specified
if (null !== $directory) {
$this->setDirectory($directory);
}
}
$this->setAction($action)
->setDispatched(false);
}
六:错误报告:
$show_debug_errors = false;
在配置文件:/data/htdocs/jackxiang.com/application/bootstrap.php
Ko::init( array(
'base_url' => '/',
'errors' => isset($show_debug_errors) && $show_debug_errors,
后赋值给Library/core.php里的:Ko::$errors,后在/library里的request;.php里面决定是否输出错误:
public function errorPage($msg = null, $data = array())
{
if(Ko::$errors === TRUE) {
throw new KoException($msg, $data);
exit;
} else {
return $this->forward('index', 'error');
}
}
七:文件位置缓存实现:
查找文件路径的缓存:'caching' => TRUE,
core.php
isset($settings['caching']) && self::$caching = (bool) $settings['caching'];
if (self::$caching === TRUE) {
// Use the default cache directory
self::$cache_dir = DATA_PATH . 'cache';
self::$_files = self::cache('Ko::findFile()');
}
self::$_files = self::cache('Ko::findFile()');
public static function cache ($name, $data = NULL, $lifetime = 3600)
{
// Cache file is a hash of the name
$file = sha1($name) . '.txt';
// Cache directories are split by keys to prevent filesystem overload
$dir = self::$cache_dir . DIRECTORY_SEPARATOR . "{$file[0]}{$f
配置文件:
/data/htdocs/jackxiang.com/application/bootstrap.php
Ko::init( array(
'base_url' => '/',
'errors' => isset($show_debug_errors) && $show_debug_errors,
'index_file' => isset($index_file) ? $index_file : 'index.php',
'caching' => TRUE,
'enable_query_strings' => TRUE,
'threshold' => isset($log_threshold) ? $log_threshold : 0,
));
——————————————————————————————————————————
八.自制session:
protected $session;
$this->session = Session::instance();
操作session:
protected function checkLogin ()//检查登录
{
if ($verifycode != $this->session->verifycode || !$this->session->userinfo ) {//从session里取不到
$this->session->userinfo = $this->ssoCheck($verifycode);//从接口里取并存入session
$this->session->verifycode = $verifycode;
......
}
return $this->session->userinfo;
}
protected function ssoCheck($verifycode){
......//查接口返回数据:'passport_url'=> 'http://passport.cntv.cn/ssocheck.jsp',Curl
$userinfo = json_decode($ret,true);
return $userinfo;//返回
}
九:写错误日志函数及错误的统一捕获:
场景:问,最近我做了一个日志系统,PHP在调用时出现因为异常错误导致没有运行到日志函数就退出了,
请问:怎么实现对这个错误的捕获?还问:PHP里的try catch可能并不能捕获里面的所有错误的实际情况。
这个问题其实是想问对PHP的set_error_handler函数,及错误处理函数,它捕获能导致php脚本停止运行的严重错误(这类错误是不能被set_error_handler ()捕获的),
一旦脚本停止运行,customend()函数就会被调用,在customend()函数中通过error_get_last()来判断脚本是正常结束还是发生严重错误而中断,如果是发生严重错误而中断,则运行错误处理程序。try-catch 无法在类的自动加载函数 __autoload() 内生效。try-catch 用于捕获异常,无法捕获错误,例如 trigger_error() 触发的错误,异常和错误是不一样的。
摘自:http://jingyan.baidu.com/article/046a7b3ed5e233f9c37fa969.html
参考:PHP 的异常处理、错误处理:error_reporting,try-catch,trigger_error,set_error_handler,set_exception_handler,register_shutdown_function
http://www.php-note.com/article/detail/779
******web端打开写SQL日志及分析。———show_debug_errors是前端错误打开于否的开关,
而那个threshold是分开关,如果要写Mysql日志则:$log_threshold = 3;小于3的threshold则不打印日志的,
上面两变量均在index.php入口配置,在application下的bootopen.php进行init()函数的赋值。******
如果等于threshold的值等于4则:debug: Request Library loaded ,也就是相当于debug的日志可打印到日志里来了,等于1只报error错误日志,如等于0则全关闭了即使有错。
* Log Thresholds:
* 0 - Disables logging completely
* 1 - Error Messages (including PHP errors)
* 2 - Alert Messages
* 3 - Informational Messages
* 4 - Debug Messages
*/
$log_threshold = 3;
对于这个;show_debug_errors这个值,则应该用Nginx给屏蔽掉,当然上线后关闭是最好的,提高运行效率及稳定性,Nginx配置如下:
实践Ok如下:
如错误页面在这儿:http://my.jackxiang.com/error
vi my.cnf
在server里添加:
error_page 404 = ./error;
error_page 500 502 503 504 = ./error;
#下面也行:
error_page 404 = http://my.jackxiang.com/error.html;
error_page 500 502 503 504 = http://my.jackxiang.com/error.html;
来自自己的参考:http://jackxiang.com/post/6769/
要想写日志得满足三个条件:
1./application/bootopen.php 里的'errors' 是TRUE,这样就注册了register_shutdown_function函数,进而在log.php里写日志
2.如果是后台程序立即触发就写,前台程序是关闭的写的。
register_shutdown_function这个的用法见,好多单例都给整了这个东东在上面:http://jackxiang.com/post/6784/
例1:核心都有用到register_shutdown_function
/libraries/core.php
// Enable the Ko shutdown handler, which catches E_FATAL errors.
register_shutdown_function(array('Ko' , 'shutdown_handler'));
if (self::$errors === TRUE) {
// Enable the Ko shutdown handler, which catches E_FATAL errors.
register_shutdown_function(array('Ko' , 'shutdown_handler'));
// Enable Ko exception handling, adds stack traces and error source.
set_exception_handler(array('Ko' , 'exception_handler'));
// Enable Ko error handling, converts all PHP errors to exceptions.
set_error_handler(array('Ko' , 'error_handler'));
}
例2:web运行完再写日志更是常用:
/libraries/log.php
// Write the logs at shutdown
register_shutdown_function(array(self::$_instance[$group], 'write'));
frontend/index.php //这儿打开的******(这块打开如果action类和方法不存在则会直接报错)
// Define wether show debug message.
$show_debug_errors = true;
/application/bootopen.php
Ko::init( array(
'base_url' => '/',
'errors' => isset($show_debug_errors) && $show_debug_errors,
/libraries/core.php
if (self::$errors === TRUE) {
// Enable the Ko shutdown handler, which catches E_FATAL errors.
register_shutdown_function(array('Ko' , 'shutdown_handler'));
/libraries/log.php
public static function instance($group ='default')
{
if (!isset(self::$_instance[$group]) ||self::$_instance[$group] === NULL) {
// Create a new instance
self::$_instance[$group] = new self($group);
// Write the logs at shutdown
register_shutdown_function(array(self::$_instance[$group], 'write'));
}
return self::$_instance[$group];
}
\libraries\database.php
// Log levels
private static $log_levels = array (
'error' => 1,
'alert' => 2,
'info' => 3,
'debug' => 4,
);
public static function log($type, $message)
{
// Should we log these to file?
if (self::$log_levels[$type] <= self::$threshold) {//type==debug<====4<=2
self::$log->add($type, $message);
}
}
\libraries\database.php
// Log query
Ko::log('info', str_replace(array("\r\n", "\r", "\n"), " ", $sql));
self::$log->add($type, $message);
}
十:上完静态文件,要改config.php里的version,以防止那个CDN了旧的,没法查看。
<?php
defined('SYS_PATH') or die('No direct access allowed.');
return array(
'version' => '2.005',
————————————————————————————
jquery.Jcrop.js?ver=<?php echo $this->_tpl_vars['version']; ?>
直接在框架初始化时赋值比较方便编写些,多层继承的最上层写上:
class Site_PartinController extends FrontBaseController
class FrontBaseController extends FrontController
class FrontController extends InitController
class InitController extends Controller(init.php里进行模板赋值的)
/application/controllers/init.php
/**
* Overload
*/
public function after ()
{
$this->view->version = KO::config('config.version');
模板里:
<script src="<{$staticUrl}>js/My97TimePicker/WdatePicker_time.js?ver=<{$version}>" type="text/javascript"></script>
flashvars["config"] ="<{$flashXmlUrl}>?ver=<{$version}>";// 如上传组件初始化参数的读取加上版本号
十一:单例模式,一些常规操人作,多用单例模式,这种模式不适合用于写PHP的daemon,容易出现内存不够,如不及时释放:
尽管PHP退出后就释放了,不像Java,但好处还是有的:http://blog.csdn.net/jungsagacity/article/details/7618587
http://blog.sina.com.cn/s/blog_6f49a3c30100qgiy.html
\libraries\session.php
public static function instance ($group = 'default')
protected function __construct ($group = 'default')
register_shutdown_function(array($this , 'write_close'));
public function sessionid ()
public function create ()
public function regenerate ()
public function unsetAll()
public function destroy ()
public function write_close ()
public function delete ($keys)
使用方法:
// Init Session
$this->session = Session::instance();
$this->session->set('findemail',$userInfo['email']);
$findemail = $this->session->get('findemail');
$this->session->unsetAll();
\libraries\config.php
public static function instance ($group = 'default')
final private function __construct ($group = 'default')
public function attach (Config_Driver $reader, $first = TRUE)
public function detach (Config_Driver $reader)
public function load ($group)
public function save ($file, $config = NULL)
public function copy ($group)
final private function __clone ()
使用方法:
$config[$group] = Config::instance('messages')->attach(new Config_File('messages'))->load($group);
Config::instance('lang')->attach(new Config_File('i18n'))->load($lang . DIRECTORY_SEPARATOR . $group);
Config::instance('messages')->attach(new Config_File('messages'))->save($file, $data);
更高级别封装readConfig:$this->_pbConfig = $this->readConfig('pbconfig');
controllers/global.php
/**
* 读取数组配置文件信息
* $this->readConfig('config'); ==> data/messages/config.php
* @param string $group 配置文件名,不带扩展名.php
*/
protected function readConfig($group)
{
\libraries\log.php
public static function instance($group ='default')
final private function __construct()
register_shutdown_function(array(self::$_instance[$group], 'write'));
public function attach(Log_Writer $writer, $types = NULL)
public function detach(Log_Writer $writer)
public function add($type, $message)
public function write ()
private function __clone()
使用方法:
$returnlog = Log::instance('videoreturn')->attach(new Log_File(DATA_PATH . 'logs/' . date('Ymd') . '/', 'setCheckVideoreturn-' . date('Ymd') . '.php'));
$logger = Log::instance(strtoupper($prefix))->attach(new Log_File(DATA_PATH . 'logs/' . date('Ymd') . '/', $prefix . '_' . date('Ymd') . '.php'));
$sharelog = Log::instance('share')->attach(new Log_File(DATA_PATH . 'logs/' . date('Ymd') . '/', 'share-' . date('Ymd') . '.php'));
$sharelog->add('succ:',' from vid:'.$vid.' to uuid :'.$videoData['video']['uuid']);
分析该写日志的实现:
$log = Log::instance(__CLASS__)->attach(new Log_File(DATA_PATH . '/logs/' . date('Ymd') . '/mysql-error-' . date('Ymd') . '.log'));
1)文件及目录都是777:
new Log_File:\libraries\log\file.php, 分析文件函数:
(1)目录777:__construct里直接创建目录: if (! mkdir($directory, 0777, TRUE) || ! is_writable($directory)) {
(2)文件777:public function write(){
\libraries\cookie.php
final private function __construct ()
public static function get ($key, $default = NULL)
public static function set ($name, $value, $expiration = NULL)
public static function delete ($name)
使用方法:
Cookie::set('language1',$language);
$lang = Cookie::get('language1');
十二:关于前端代码的路径等配置:前端Js/css静态域名版本号(反CDNl:version)的配置:
/controllers/site/preview.php
关于版本号及前端,每个controller下都有一个preview.php层层去继承InitController在具体的controller下配置相关前端Js输出及CSS输出的路径:
$this->view->version = KO::config('config.version');
public function after()
{
//$this->getwatch();
$this->view->themePath = $this->themePath;
$this->view->publicPath = 'themes/common';
$this->view->cssPath = 'themes/zh';
$this->view->jsPath = 'themes/zh';
$this->view->lang = (array)Ko::lang('common','zh');
$this->view->staticURL = KO::config('url.static_url');
$this->view->mainURL = KO::config('url.main_url');
$this->view->flashURL = KO::config('url.flash_player_url');
$this->view->basePath = $this->getBaseUrl();
$this->view->version = KO::config('config.version');
$this->view->render();
}
十三:smarty模板输出:
最上层的controller下的before还负责smarty模板的指定:
\libraries\controller.php
public function before ()
{
if ($this->auto_render === TRUE) {
// Load the template
$this->template = $this->request->getController() . '/' . $this->request->getAction();
$this->view = View::factory($this->template);
}
}
继承后再重写:
public function before ()
{
// Auto render , use user defined template.
$this->autoRender(TRUE);
parent::before();
———————上面的before,下面的after都是在Library/request.php里进行按before在前,action在中间,after在后面处理的__________
Before处理如下前期问题,参数获取/模板工厂(smarty)等层层继承,层层parent::before();:
//Overload:英文是重载,国人:同名函数,叫 方法重写
class ActController extends FrontController
{
public function before()
{
parent::before();//访问InitController里的after
}
}
class FrontController extends InitController
{
/**
* Overload
*/
public function before()
{
$lang = '';
parent::before();// 初始化模板等...
class InitController extends Controller
{
public function before()
{
// Init Session
$this->session = Session::instance();//获取参数等全局逻辑准备
$this->varname = trim($this->getRequest()->getParam('varname'));
$this->jsoncallback = trim($this->getRequest()->getParam('jsoncallback'));
}
abstract class Controller
public function before ()
{
if ($this->auto_render === TRUE) {
// Load the template
$this->template = $this->request->getController() . '/' . $this->request->getAction();
$this->view = View::factory($this->template);//模板进入工厂
}
}
十四:关于after:
这块输出头也是放after里的:
\controllers\global.php
public function after()
{
// Send headers: Cache-Control & Expire.
$max_age = $this->getRequest()->getParam('max_age', 0);
if ($max_age > 0) {
$this->getRequest()->addHeader('Cache-Control', 'max-age=' . $max_age);
$this->getRequest()->addHeader('Expires', gmdate('D, d M Y H:i:s', time() + $max_age) . ' GMT');
}
else {
$this->getRequest()->addHeader('Cache-Control', 'no-cache');
$this->getRequest()->addHeader('Expires', '0');
$this->getRequest()->addHeader('Pragma', 'No-cache');
}
parent::after();
}
上面的after函数被:\controllers\front.php重写并parent调用之:
/**
* Overload
*/
public function after()
{
......
$this->view->version = KO::config('config.version');
$this->view->pageCode = $this->getPageCode();
$this->autoRender(TRUE);
parent::after();
}
它也是在后面才输出的,调用在Library/request.php:
上面的日志在后面after写的:
libraries\request.php
$class->getMethod('after')->invoke($controller);
摘录自: \libraries\request.php
try {
// Load the controller using reflection
$class = new ReflectionClass(ucfirst($prefix) . ucfirst($this->controller) . 'Controller');
if ($class->isAbstract()) {
$this->errorPage('Cannot create instances of abstract controller: :error',
array(':error' => $prefix . ucfirst($this->controller) . 'Controller'));
}
// Create a new instance of the controller, Interrupted by dispatcher anytime
$controller = $class->newInstance($this);
if ($this->isDispatched()) {
$class->getMethod('before')->invoke($controller);
}
try {
$action = empty($this->action) ? Route::$default_action : $this->action;
if ($this->isDispatched()) {
$class->getMethod($action)->invokeArgs($controller, $this->params);
}
} catch (ReflectionException $e) {
$class->getMethod('__call')->invokeArgs($controller, array($this->getAction() , $this->params));
}
if ($this->isDispatched()) {
$class->getMethod('after')->invoke($controller);
}
}
十五:关于日志(新加daemon日志实现立即写,不用注册的退出时才写:register_shutdown_function):
日志输出在web时是执行完后再输出,而cli模式是立即输出,(关于错误级别$type定义在第九有备注到,如info级别:Ko::log('info', str_replace(array("\r\n", "\r", "\n"), " ", $sql));),如下:
\libraries\log.php
public function add($type, $message)
{
// Create a new message and timestamp it
$this->_messages[] = array (
'time' => date(self::$timestamp),
'type' => $type,
'body' => is_string($message) ? $message : var_export($message, true),
);
if(Ko::$is_cli === TRUE) {
$this->write();
}
return $this;
}
WEB没有立即写,而是后面才写,并没有在request中的after里,而是注册了shutdown函数write:
public static function instance($group ='default')
{
......
// Write the logs at shutdown
register_shutdown_function(array(self::$_instance[$group], 'write'));//这儿调用写
}
return self::$_instance[$group];
}
调用:\controllers\audit.php
$returnlog->add('check start', '-----------------');
$returnlog->add('data post', $post);
这种实现方法在如果用php做daemon时会遇到从不退出就没有调用写导致里面的数据太大出现php内存不够并意外退出问题:
遇到问题,当守护进程时写日志时往往会出现因为框架是web的,在最后退出才写,而daemon是不退出的,导致内存暴增,解决办法如下:
修改为,如果是cli模式立即就写了得了:
十六:使用modules外挂模块方法:
Ko::modules( array(
'weibo' => 'weibo',
'libspace' => 'libspace',
'face' => 'face',
'nusoap' => 'nusoap',
'sdk' => 'sdk',
));
jackxiang.com/modules/ //jackxiang.com是根目录
face/ libspace/ nusoap/ sdk/ weibo/
ls modules/sdk/ //init.php文件名是必须要有的。
common.php init.php justwinitapi.ex.class.php
调用:
./application/models/app.php: $justwinit = new justwinit();
./application/controllers/front/front.php: $justwinit = new justwinit();
/**
* Overload
*/
public function before()
{
$this->checkToken();
}
public function checkToken()
{
$justwinit = new justwinit();
.......
}
十七:框架扩展:在\libraries\core.php里加一个写日志函数,以给daemon程序写当是cli模式时写:
十八:图片缩放之gd,示例:
关于图片上传及缩小,用到上传类libraries//upload.php,缩小gd类libraries/image.php ,
这儿遇到一个问题是上传后缀是大写的JPG时会报错,最后用strtolower($ext)保存为小写就没这个问题,
我从其它博客试了一下确实存在警告,最好还是小写的jpg后缀为好:http://jackxiang.com/post/3022/
缩小为148.148的等比例缩放,其使用方法如下:
十九:关于单例模式里和观察者模式一块用的问题:
1)写法于传统的PHP写法背离,不像C了,在写得少的人看来很是怪异:
$log = Log::instance(__CLASS__)->attach(new Log_File(DATA_PATH . 'logs/' . date('Ymd'), __CLASS__.'-' . date('Ymd') . '.log'));
2)这种单例与观察模式在代码提示时出现无法跳转,因为再智能的编辑器也没法知道前面这个数组是哪个具体对象了:
$writer['object']->write($messages);//这样当于一个写的类句柄通过观察者数组中取出后:Log_File->write($messages);
3)但为了什么耦合,为了少几个句柄,为了提高效率等,还得用,自己作下分析性参考:http://jackxiang.com/post/6825/ 。
二十:观察者模式:
http://www.cnblogs.com/baochuan/archive/2012/02/22/2362668.html
框架里的观察者模式:
/libraries/log.php
片段如下,这里的writer就是一个进来的类对象数组的一个集合(全是对象),这个:
这个对象去分别调用对象类里的方法,和上面的原始摘录Url里的示例一样的,如下用foreach去一个个的调用write方法,$writer['object']->write($messages);:
二十一:关于memcache或ttserver的缓存实现及配置:
配置文件这样写:
config/cache.php
使用时要这样用:
class TagModel extends Model
{
protected $cachePermanent = 'tt_server';// Permanent Cache KEY,来自配置文件的数组key,及config/cache.php tt_server对应。
return (array)$this->getPermanentCache($userid);
return $this->setPermanentCache($userid, $tags);
为什么可以这样用,因为前面的extends Model这个类的libraries/model.php里有:
[root@iZ25z0ugwgtZ libraries]# grep -r "setPermanentCache" ./
./model.php: protected function setPermanentCache($key, $data)
// Cache KEY
protected $cacheConfig = 'default';
protected function getCache($key)
{
return Cache::instance($this->cacheConfig)->get($this->makeCacheKey($key));//静态调用instance方法获取该类的实例
}
这个cache是有各种cache,这个是ttserver,我们来看它是如何实现这个实例的文件,libraries/cache/ttcache.php :
我都说这个直接传入一个string的tt_server,是怎么从前面配置文件里读取到这个数组的,不仅仅是前面的这个instance函数对这各种cache设备
类进行实例化外,还有对这个下面挂的设备本身的相关配置进行初始化,代码如下:
框架分析到此完结。EOF
AddTime:2016-05-28
加载外部modules方法及入口文件:
// Set the current module list
self::$_modules = $modules;
foreach (self::$_modules as $path) {
$init = $path . DIRECTORY_SEPARATOR . 'init.php';
if (is_file($init)) {
require_once $init;
}
}
也就是说会核心框架会自动加载这个init.php
D:\www\xiyou.cntv.cn[新svn代码]\trunk\codes\modules下的OAuth weibo qiniu 里面都有一个init.php,而这前面三个目录是怎么找的呢?
D:\www\xiyou.cntv.cn[新svn代码]\trunk\codes\application\bootstrap.php 里有让modules相对或绝对路径。 relative or absolute path.:
==================================
概述以上分析,从更高层次来分析其入口的调用层次分析:
model独立,model目录里文件均无互相调用(缓存也在这层,也就是数据层),
service加上业务调用model model/open,service之前也可相互调用给application提供数据。
application可以有多个目录,如多了个openapi的目录,这个目录可通过路由来配置指向即可:
./bootstrap.php:Route::set('openapi_route', 'openapi(/<controller>(/<action>(/<__KO_VARS__>)))', array('__KO_VARS__' => '.+'))
这个对像是传入/libraries/request.php,它又和/libraries/core.php接合,接合autoload和反射类到上面这些model、service、controller里找对应,
找不到时用try..catch出来错即可,而这个bootstrap.php,则被./frontend/index.php ./open/index.php包含进来(它里面有错谴责级别设置)后,进行:
// Bootstrap the application
require_once APP_PATH . 'bootopen.php'; ,于是对外可呈现出多个域名,因为在nginx里直接配置:root /data/htdocs/xiyou_alpha/frontend;就会从
fronted/index.php单入口里穿透并进入到各个层里,运行了,在层次上清晰,在代码上可灵活配置,这就是Ko的优势了,MVC加server层次间的调用规定如下:
一)model目录一个目录:open
二)service目录里调model的open里的类用前面加Open_,调open一级的就不用了。
三)controller里有open openapi两个目录,均调用了service里的open目录:
比如:
controller
./openapi/partner.php: $Open_OpenService = new Open_OpenService();
./open/usercenter.php: $open_OpenService = new Open_OpenService();
_________Model_________
.........//主站的model
/model/open/...开放平台
也就是说model最好是简单一点,service文件间可互相调用:
vi services/open/open.php Open_OpenService 原型可被同级的server其它文件调用如下:
vi services/audit.php: $Open_OpenService = new Open_OpenService();
而这个service呢,也调用了model层里的open目录下的modle:
$tokenModel = new Open_TokensModel();
$info = $tokenModel->getTokenInfoByToken($token);
上面两行的位置在哪儿?能通过类前面加上Open知道是model/open/位置:
/application/models/open/tokens.php class Open_TokensModel extends Model
为何getCache得在services目录里的base.php和library目录下的model.php里有:
protected function getCache($key)
{
return Cache::instance($this->cacheConfig)->get($this->makeCacheKey($key));
}
getCurrentPageVideosFromDB在model里不需要cache,但是getCurrentPageVideos就需要cache了:
public function getVideosByUserId($userId)
{
$userVideos = $this->getCache($userId);
if ($userVideos) {
return $userVideos;
}
$userVideos = $this->getVideosByUserIdFromDB($userId);
if ($userVideos && count($userVideos) > 0) {
$this->setCache($userId, $userVideos);
}
return $userVideos;
}
同样,在service里有些变量也需要cache,如cookie变量从cache里读取,为何需要专门函数实现?因为怕cache重复了,结合函数名,查询参数确保唯一。
/**
* 写入cookie - 您最近看过的
*/
public function setCookieHistoryVideoList($uuid)
{
//设置显示“您最近看过的”视频的个数
$MAX_COUNT = 5;
$cookieId = Cookie::get('xiyou_ck_id');
$cookieData = $this->getCache($cookieId);
if(!$cookieData){
$cookieData = array();
}
$videolist = isset($cookieData['history_list']) ? $cookieData['history_list'] :false;
if(!is_array($videolist)){
$videolist = array();
PHP 5.3.27 (cli) (built: Aug 12 2013 12:22:48)
升级后:
[root@iZ25z0ugwgtZ ~]# php -v
PHP 5.6.18 (cli) (built: Feb 13 2016 13:05:48)
升级后,出现:
ErrorException [ 2 ]: fopen(Unknown): failed to open stream: No such file or directory ~ SYS_PATH/core.php [ 865 ]
===================================================================
回退回5.3版本,如下:
[ZendGuardLoader-php-5.3-linux-glibc23-x86_64.tar.gz] found
ZendGuardLoader module install successfully!
Gracefully shutting down php-fpm . done
Starting php-fpm done
####################Congratulations########################
PHP install dir: /usr/local/php
eAccelerator Control Panel url: http://101.200.189.210/control.php
eAccelerator user: admin
eAccelerator password: eAccelerator
——————————————————————————————————————————————————————————
第一较大步骤:
学习这个Kohana这个框架的真正在于它就像雨燕一样的灵活,所以,先了解一下这个是怎么通过路由实现了其多域名共用一套完整代码的松耦合,且以mvc的高内聚的框架思想是首页步骤,如下:
只要三个步骤实现一套代码两套域名,还可实现看似一套代码且可独立部署,且还实现了域名的分离,这也是Kohana这个框架的灵活之处,更多请看:
http://jackxiang.com/post/7122/
http://jackxiang.com/post/5977/
____________________________________在controller里建立interface目录____________________________________
步骤一:mkdir /data/htdocs/boosh.com/application/controllers/internal (注意controller调用里面的类名前缀和路由保持一致。)
问题:框架controller层层调用情况,以新加一个接口域名internal.boosh.com,但代码还得放一块,而这个域名不一致,怎么办?
解决办法,先看框架controller层层调用情况:
./application/controllers/internal/base.php:abstract class internal_BaseController extends InitController //类名和文件夹名一样第一个字大写是规范,而和前面建立的目录对应的前缀表明这个类是在这个目录下的,这块的目录internal定后,其下面的类名必须以目录名且首字母大写作为前缀,这是因为request.php里定死了的,如下代码:
$prefix .= ucfirst($directory) . '_';//形成Internal_
$class = new ReflectionClass(ucfirst($prefix) . ucfirst($this->controller) . 'Controller');//这个controller也就是文件名如/internal/user.php里也就是User,class Internal_UserController extends Internal_BaseController ,user里的U大写即可。
——————————————————————————————————————————————————
./application/controllers/global.php:class InitController extends Controller
./libraries/controller.php abstract class Controller
再次对这个
./application/controllers/internal/base.php:abstract class internal_BaseController extends InitController //类名和文件夹名一样?这个是和路由的php代码设置及nginx的转写规则有关。
require_once dirname(dirname(__FILE__)) . '/global.php';
继承自:
./application/controllers/global.php:class InitController extends Controller
继承自:
./libraries/controller.php abstract class Controller
这个名称都可随便写,如base.php,但里面的类前缀部分也得变得有关联更符合要求,这块不规范像这样:
如:./application/controllers/internal/my.php
require_once 'base.php';
class Internal_MyController extends Internal2_BaseController
//class Internal_MyController extends Internal_BaseController
./application/controllers/internal/base.php也一样得有关联:
require_once dirname(dirname(__FILE__)) . '/global.php';//前面上一层目录的global.php
//abstract class Internal_BaseController extends InitController
abstract class Internal2_BaseController extends InitController //加个2后,在my.php里也加个2继承下,也没关系运行没有问题,只是不规范罢了。
为何直接写没有自己写那个new 这个controller类就运行了呢?是因为两个地方:
1)\libraries\request.php里的execute对目录进行分析,对controller类进行反射类实例化:
(1)internal目录进行处理:
if ($this->directory) {
// Add the directory name to the class prefix
$directory = str_replace (array('\\', '/', '//'), '_', trim($this->directory, '/'));
$directories = explode('_', $directory);
foreach ($directories as $directory) {
$prefix .= ucfirst($directory) . '_';
}
}
(2)对反射类进行处理,找不到文件则到core.php来进行查找定位处理:
try {
// Load the controller using reflection
$class = new ReflectionClass(ucfirst($prefix) . ucfirst($this->controller) . 'Controller');
if ($class->isAbstract()) {
throw new KoException('Cannot create instances of abstract controller: :error', array(':error' => ucfirst($this->controller) . 'Controller'));
}
} catch (Exception $e) {
if ($e instanceof ReflectionException) {
$this->status = 404;
} else {
$this->status = 500;
}
$this->sendHeaders();
exit(0);
}
用了do while串连起这个反射类,如知识点如下:
(3)为何不同的目录可以实现根据nginx里的d参数两个不同的域名呢,是因为下面这块实现的levoo.com/openapi === api.levoo.com:
一套代码通过nginx转写d到不同目录后,最终调用的都是:global.php,如下:
/data/htdocs/levoo.com/application/controllers/manage.php//class ManageController extends FrontController
/data/htdocs/xiyou.cntv.cn/application/controllers/front.php//class FrontController extends InitController
/data/htdocs/xiyou.cntv.cn/application/controllers/global.php//class FrontController extends InitController
/data/htdocs/levoo.com/application/controllers/openapi/manage.php
class Openapi_ManageController extends Openapi_BaseController //它们最终都直接继承了global.php文件
require_once dirname(dirname(__FILE__)) . '/global.php'; //class InitController extends Controller
最后,execute全部代码如下,试图调度控制器/动作。如果要求表示它需要被调度,移动到下一个请求行动。
(2)core.php的autoload和findFile找文件这个查找的core.php是在fontend里的index.php包含bootstrap.php再包含core.php引入的,给反射类的查找文件指名了方向,在bootstrap.php最后一行(Request::instance()->dispatch();)便是自动根据core.php里的__autoload,找到request.php文件进行操作里面的相关函数的一个立即体现(这块的core.php直接引入太关键了),\application\bootstrap.php:
自己重新定了autoload,而不是__autoload:
将__autoload换成loadprint函数。但是loadprint不会像__autoload自动触发,这时spl_autoload_register()就起作用了,它告诉PHP碰到没有定义的类就执行loadprint()。
/data/htdocs/xiyou.cntv.cn/application/bootstrap.php:spl_autoload_register(array('Ko', 'autoload')); //这个是调用静态的方法:
spl_autoload_register() 调用静态方法
摘自:http://blog.csdn.net/panpan639944806/article/details/23192267
更多分析再参考自己写的这篇文章:http://jackxiang.com/post/5877/
步骤二:路由配置如下
/data/htdocs/my.boosh.com/application/bootstrap.php //把internal的路由指向前面建立的目录
Route::set('internal_route', 'internal(/<controller>(/<action>(/<__KO_VARS__>)))', array('__KO_VARS__' => '.+'))
->defaults(array(
'directory' => 'internal',//下面配置的Nginx指向这个目录并作转写即可:d=internal。
'controller' => isset($default_controller) ? $default_controller : 'index',
'action' => isset($default_action) ? $default_action : 'index',
));
这块路由的名称其实不重要,加个2也是可以的:
Route::set('internal2_route', 'internal(/<controller>(/<action>(/<__KO_VARS__>)))', array('__KO_VARS__' => '.+'))
为何配置这个bootstrap.php呢?htdocs的入口查看:
/data/htdocs/my.boosh.com/frontend/index.php
// Bootstrap the application
require_once APP_PATH . 'bootstrap.php';
步骤三:Nginx的配置文件
server
{
listen 80;
server_name i.boosh.com boosh.com;
index index.html index.htm index.php;
root /data/htdocs/my.boosh.com/frontend;///data/htdocs/boosh.com/frontend;被域名www.boosh.com用了,所以,my.boosh.com用作http://boosh.com使用。
location / {
rewrite "^/api/(album|general|partner|rank|search|user|video|stb|navigation|app|mobile|public|oauth|my|activity|manage|oauth2|favorite)\.([a-zA-Z]{4,})$" /index.php?d=internal&c=$1&a=$2&$args last;
rewrite "^/(album|general|partner|rank|search|user|video|stb|navigation|app|mobile|public|oauth|my|activity|manage|oauth2|favorite)/([a-zA-Z]{4,})$" /index.php?d=internal&c=$1&a=$2&$args last; //这一行必须要有,否则就没法通过get的d参数实现路由,也就是说即使后面分离后,前面新加的internal目录不用挪动,是nginx转写时加上了d,对album这些写死在nginx配置里的action进行加上d=internal,进而框架里的bootstrap.php进行了路由设置且支持路由。
前端访问情况:
直接通过api的接口访问:
http://i.boosh.com/album/addalbum (新加的域名访问A接口)
直接通过主站进行加上一个api进行访问:
http://boosh.com/api/album/addalbum(主站直接也可访问这个A接口)
对于接口输出比如ajax的json示例,此时一般都是关闭了模板,直接用自带函数进行输出,如下:
http://levoo.com/activityvideo/getactivityvideo/eid/14544893367247/typeid/1/pagenum/11/pagesize/20/sortby/new/format/jsonp/jsoncallback/cmscallback
=================View与smarty的一个接合问题=======================
前面新的一个interface没有和主站一个目录,而是另一个目录里,如何与smarty搭上边呢?
vi base.php
require_once dirname(dirname(__FILE__)) . '/global.php'; //与上一层目录的global.php进行引用,也就是主站的global.php
这个global.php主要干些啥?
1)获取进来的各种jsonP参数:
$this->varname = trim($this->getRequest()->getParam('varname'));
$this->jsoncallback = trim($this->getRequest()->getParam('jsoncallback'));
2)失败输出json/输出xml。
3)读取写入配置文件。
4)设置语言包位置及路径:css/js/images。
5)是否自动输出模板。
6) 一些共性的函数操作放里面,特别是像登录啥的。
interface/my.php
这就是真正的controller了,里面写这个页面特别的一些输出及模板渲染,经典的controller/action。
总之,就是通过view的工厂类, $this->autoRender(FALSE); 这个是不输出模板不需要smarty。要输出刚好是True,输出用了smarty,主站需要用,它是写在/controller/front.php里的,$this->view = View::factory($this->themesPath['themePath'] . '/' . $this->request->getController() . '/' . $this->request->getAction());,这一行调用了:\libraries\view.php,它引入了:require_once 'smarty/Smarty.class.php';且view.php初始化了smarty这个类,如下:
public function __construct ($file = NULL, array $data = NULL)
{
if (! empty($file)) {
$this->setView($file);
}
$this->_smarty = new Smarty();
if ($data !== NULL) {
$this->_smarty->assign($data);
}
$this->_smarty->template_dir = Ko::config('smarty.template_path');//这个是绝对路径前缀读取的是./smarty.php: 'template_path' => APP_PATH. 'views/',静态的KO类实现读取配置文件,Nginx指向->\frontend\index.php 包含=》\application\bootstrap.php=》它里面引用:require_once SYS_PATH . 'core.php'; =》public static function config ($group)静态函数实现。。
$this->_smarty->cache_dir = Ko::config('smarty.cache_path');
$this->_smarty->compile_dir = Ko::config('smarty.compile_path');
$this->_smarty->config_dir = Ko::config('smarty.configs_path');
并提供了,__set ,__get,render这些基础函数的二次根据这个view类的smarty封装。实现了smarty模板类。
模板问题再细一点:
出现模板找不到:The requested view themes/zh/my/video.html could not be found ~ SYS_PATH/view.php [ 174 ]
(这里的view.php并没有被直接包含,而是框架core.php里autoload和findFile经过在new类时查找到\libraries\目录并引入:public static function autoload ($class),根据文件名及类名引入的, Ko::autoload('My_Class_Name');class_exists找不到则去findFile查找并引入:require_once $path;。)
这就是这些类查找也好,命名也好的规范,对Controller Model Service,影响了其类名的写法必须符合,否则找不到,见libraries/core.php:
controller和Models和server分层在类名上体现是必要的,
无聊咔咔(5**773145) 17:52:56
__autoload
回忆未来-向东-Jàck(372647693) 17:53:21
加上findFile
一般框架都这样,MVC啥的全是这样搞的,有谁有新方法找我。
把找到的文件目录给缓存起来,下次就不用去再找一次了。
再把controller /action的类名加上比如Controller之类,
规范规范,加个smarty,一个PHP的架子就搭建起来了嘛要。
回忆未来-向东-Jàck(372647693) 17:57:30
controller和Models和server分层在类名上体现是必要的,如下:
class EventModel extends Model
class NewsService extends AdminBaseService
class ActController extends FrontController
findFile这个是围绕着环境变量且伴随是否caching进行编写的,主要是这两个核心的全局数组,步骤如下:
private static $_paths = array(APP_PATH , SYS_PATH);// Include paths that are used to find files
private static $_files = array(); // File path cache
1)如果是Cahceing就在self::$_files就是一个一个的文件以文件名为key,值就是文件绝对路径的数组里根据传入的文件名进行查找对应的路径,有则直接返回。
2)没有则对'config' 'i18n' 'messages'进行查找,这三个地方估计没法cache进来,得特殊处理。
3)还找不到,则self::$_paths ,这个是一堆的路径数组,一个一个去把文件名放进去拼成绝对路径,再用is_file去循环所有路径判断这个文件到底在哪儿,直到找到这个文件的绝对路径。
4)对这个文件的路径再放回到第1部里的self::$_files里去,同时返回这个文件的绝对路径,autoload 里的require_once $path;就是引入经过一堆查找到的文件,真心不容易啊。
__上面描述为何能自动找到这个View.php,Smarty被它重新封装并形成factory进行重新对象化,如何使用smarty如下:______
public function video()
{
$themesPath = $this->getLang(); //global.php里写好路径,且是相对路径,所以报错也是相对的,但输入smarty时拼成一个绝对路径,如下:
//[root@iZ25z0ugwgtZ config]# grep -r "views" ./
//./smarty.php: 'template_path' => APP_PATH. 'views/',
$this->themesPath = $themesPath;
$this->view = View::factory($this->themesPath['themePath'] . '/' . $this->request->getController() . '/' . $this->request->getAction());
$this->view->textlinkhotmax = "jackxiang";
$this->view->specialmax = "xiangdong";
$this->setFile("my/video");//如果不写,默认则是http://xxx.com/controller/funciton.html为模板。 https://boosh.com/my/video =>my目录下的video.html。
}
模板路径:/application/views/themes/zh/my/video.html 前面的这个路径是从/application/controllers/global.php 里写死一数组定义的:
[root@iZ25z0ugwgtZ my]# cat ~+/video.html
jack
<{$textlinkhotmax}>
<{$specialmax}>
访问输出:
jack jackxiang xiangdong
下面主要是core.php和request.php两个,core.php主要是autoload和findFile的一个结合,及reques.php是路由Route和反射类ReflectionClass的一个集合,最后是两个文件对新纳入文件的文件命名和类命名的的约束,形成KO的核心的框架。
第二大步,再谈一谈其高内聚合、低耦合的一些设计模式,如下:
在很多时候反射也是唯一的选择。为什么我们会选择使用反射?因为我们没有办法在编译期通过静态绑定的方式来确定我们要调用的对象。例如一个ORM框架,它要面对的是通用的模型,此时无论是方法也好属性也罢都是随应用场景而改变的,这种完全需要动态绑定的场景下自然需要运用反射。还例如插件系统,在完全不知道外部插件究竟是什么东西的情况下,是一定无法在编译期确定的,因此会使用反射进行加载。其实,包同学的反驳文章里也是持这种观点的:
什么是php反射类,顾名思义,可以理解为一个类的映射。
举个例子:
class fuc { //定义一个类
static function ec() {
echo '我是一个类';
}
}
$class=new ReflectionClass('fuc'); //建立 fuc这个类的反射类
echo $class; //输出这反射类
Class [ class A ] { @@ F:\phpweb\myPHP\test.php 23-30 - Constants [0] { } - Static properties [0] { } - Static methods [0] { } - Properties [0] { } - Methods [1] { Method [ public method __construct ] { @@ F:\phpweb\myPHP\test.php 26 - 29 } } }
$fuc=$class->newInstance(); //相当于实例化 fuc 类
$fuc->ec(); //执行 fuc 里的方法ec
/*最后输出:我是一个类*/
其中还有一些更高级的用法
$ec=$class->getmethod('ec'); //获取fuc 类中的ec方法
$fuc=$class->newInstance(); //实例化
$ec->invoke($fuc); //执行ec 方法
----------------------------------------------------------------------------------------------------------------------------------------------------------------------
$class->getmethod('ec')-> invokeArgs($fuc, $params); //有点绕:class是反射类,获取这个反射类里的ec方法,用实例化后的$fuc,并传入相关ec的参数。
对该方法的传入参数及调用的方法实践OK如下:
输出:
---------- 调试PHP ----------
getAPI函数里的apiNO参数:1
Array
(
[apiName] => getVideo
[apiIntro] => getVideo from Server
)
是通过数组进行扩展多个参数的测试:第三个参数
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
$methodsArrAll = $class->getMethods(); //获取所有的方法名
Reflection::export($class);//通过此方法可以看到ReflectionClass也就是fuc类中所有的属性和方法
上面的过程很熟悉吧。其实和调用对象的方法类似
只不过这里是反着来的,方法在前,对象在后
举例: 这样的方法实现在面像对象的框架里有这样的用法,见:http://kohanaframework.org/ request.php
框架核心摘录解析\libraries\request.php,涉及到before/after(写日志)函数的先后,特别注意:
来自:http://blog.csdn.net/21aspnet/article/details/6952432
给redirect函数里加上日志:
找不到action和对应方法时调用404.
/**
* 当调用的action不存在时调用该方法
* Overload
*
* @see application/controllers/InitController::__call()
*/
public function __call($method, $arguments=NULL)
{
$this->redirect(KO::config('url.main_url') . 'error.html');
}
———————————直接访问Url里的一个controler下用action直接输出调用forward时在调核心request类里的函数返回$this是什么的理解。—————————
这里的 $this->request是从controller.php里继承来的:
class ApiController extends InitController,class InitController extends Controller。
public function __construct (Request $request)
{
// Assign the request to the controller
$this->request = $request;
}
而forward函数是request.php里的:
/**
* Forward to another controller/action.
*
* @param string $action
* @param string $controller
* @param string $directory
* @param array $params
* @return void
*/
public function forward($action, $controller = null, $directory = null, array $params = null)
{//方法,文件类名,文件路径(默认是框架路径,这儿可在框架路径下建立自己的路径),参数(是通过__call: public function __call($method, $arguments = null) 传入的)
。。。。。。
这些都是教简单,而return正是在request.php里函数常用到,如下:
/**
* Set parameters
*
* Set one or more parameters. Parameters are set as userland parameters,
* using the keys specified in the array.
*
* @param array $params
* @return Request
*/
public function setParams(array $params)
{
foreach ($params as $key => $value) {
$this->setParam($key, $value);
}
return $this;
}
/**
* Set request directory
*
* @param string
* @return Request
*/
public function setDirectory ($directory)
{
echo "<br>".__FILE__.__LINE__.$directory."<br>";
$this->directory = $directory;
return $this;
}
setParams和setDirectory都是forward里的,如下:
public function forward($action, $controller = null, $directory = null, array $params = null)
{
if (null !== $params && is_array($params)) {
$this->setParams($params);
}
if (null !== $controller) {
$this->setController($controller);
// Directory should only be reset if controller has been specified
if (null !== $directory) {
echo "Here,Jack...";
$this->setDirectory($directory);
}
}
$this->setAction($action)
->setDispatched(false);
}
这里的return this如何理解:
http://zhidao.baidu.com/link?url=QU-yClJDjln0YRwEZr8nO2wBLUIqDx4oPeYL6BykgCZvrclh78YpY9qnsfRaYWZam87QCaMEdmk06ZGd_DKY2q
这是类里面的一个函数,没有形参,函数里最后一句是return $this; 请问这个是返回了什么东西啊?返回了谁的自身?
public function getmodule() {
$this->cache->key = 'sdmodule';
$result;
.....
....//中间很多句子。
....
$this->smarty->assign("modules", $result);
return $this;
}
就是返回这个对象,在PHP类里面$this关键字就是代表这个类内部的引用。如你上面所说的return $this;就是相当于把该对象返回到方法getmodule() 中。
比如:
$abc=new class; //class是指你那个类
echo $abc->getmodule()->cache->key; //输出sdmodule
也就是getmodule() 拥有了该类的所有的成员和方法。
—————————通过forward方法传入和通过Request::instance()->dispatch();的区别(instance获取uri后调__construct进行action,controller,路径path的类变量赋值实现了类似forward的传参)—————————
/**
* Main request singleton instance. If no URI is provided, the URI will
* be automatically detected using PATH_INFO, REQUEST_URI, or PHP_SELF.
*
* @param string URI of the request
* @return Request
*/
public static function instance ($uri = TRUE)
{
static $instance;
if ($instance === NULL) {
.......//各种获取Uri的方法
$instance = new self($uri);//把uri传入类中,下面:
// public function __construct ($uri)//它就是用来对uri进行解析得到forward里的a,c,d。forward只是直接传入罢了,不用在这儿初始化就在于此,直接传入修改了类需要运行的相关变量,除开get参数外,都在这里进行分析并写入类全局变量,通过this作为句柄作操作。
//
}
return $instance;
}
forward和__construct的相同点和不同点:
public function __construct ($uri)
{
Ko::log('debug', __CLASS__ . ' Library loaded');
// Are query strings enabled in the config file?
// If so, we're done since segment based URIs are not used with query strings.
if (Ko::$enable_query_strings && isset($_GET['c'])) {
$this->setController(Security::xss_clean($_GET['c']));
if (isset($_GET['a'])) {
$this->setAction(Security::xss_clean($_GET['a']));
}
if (isset($_GET['d'])) {
$this->setDirectory(Security::xss_clean($_GET['d']));
}
$this->params = array();
return;
}
......对接受到的变量赋值到类变量后立即进行安全检查并过滤......
foreach ($routes as $route) {
if (($params = $route->matches($uri)) !== false) {
$this->directory = Security::xss_clean($params['directory']);
$this->controller = Security::xss_clean($params['controller']);
$this->action = Security::xss_clean($params['action']);
$this->params = $params;//变量从uri里获取到的
return;
}
}
//上面找不到的则进行404处理并输出找不到的头页面处理
}
而在执行时这个this-;>params还会用到:
function excute(){
if ($this->isDispatched()) {
$class->getMethod($action)->invokeArgs($controller, $this->params);//这儿
}
你会问这个POST,GET参数去哪儿了?在request;.php里有:
public function getParam ($key, $default = NULL)
{//对各种变量都作了xss检测
if (isset($this->params[$key])) {
return Security::xss_clean($this->params[$key]);
} elseif (isset($_GET[$key])) {
return Security::xss_clean($_GET[$key]);
} elseif (isset($_POST[$key])) {
return Security::xss_clean($_POST[$key]);
} else {
return $default;
}
}
再就是通过_set,_get进行属性读取,参看:php面向对象_get(),_set()的用法,http://blog.sina.com.cn/s/blog_4565cc770100bv2u.html
http://jackxiang.com/post/2766/ 里面有代码对其实践和论述。
在实际编写代码调用:
public function minialbum(){
$uuid = $this->getRequest()->getParam('uuid');
public function setController ($controller)
{
$this->controller = $controller;
return $this;
}
//而forward是自己写的,所以就不用进行安全的xss检测
public function forward($action, $controller = null, $directory = null, array $params = null)
{
if (null !== $params && is_array($params)) {
$this->setParams($params);
}
if (null !== $controller) {
$this->setController($controller);
// Directory should only be reset if controller has been specified
if (null !== $directory) {
$this->setDirectory($directory);
}
}
$this->setAction($action)
->setDispatched(false);
}
六:错误报告:
$show_debug_errors = false;
在配置文件:/data/htdocs/jackxiang.com/application/bootstrap.php
Ko::init( array(
'base_url' => '/',
'errors' => isset($show_debug_errors) && $show_debug_errors,
后赋值给Library/core.php里的:Ko::$errors,后在/library里的request;.php里面决定是否输出错误:
public function errorPage($msg = null, $data = array())
{
if(Ko::$errors === TRUE) {
throw new KoException($msg, $data);
exit;
} else {
return $this->forward('index', 'error');
}
}
七:文件位置缓存实现:
查找文件路径的缓存:'caching' => TRUE,
core.php
isset($settings['caching']) && self::$caching = (bool) $settings['caching'];
if (self::$caching === TRUE) {
// Use the default cache directory
self::$cache_dir = DATA_PATH . 'cache';
self::$_files = self::cache('Ko::findFile()');
}
self::$_files = self::cache('Ko::findFile()');
public static function cache ($name, $data = NULL, $lifetime = 3600)
{
// Cache file is a hash of the name
$file = sha1($name) . '.txt';
// Cache directories are split by keys to prevent filesystem overload
$dir = self::$cache_dir . DIRECTORY_SEPARATOR . "{$file[0]}{$f
配置文件:
/data/htdocs/jackxiang.com/application/bootstrap.php
Ko::init( array(
'base_url' => '/',
'errors' => isset($show_debug_errors) && $show_debug_errors,
'index_file' => isset($index_file) ? $index_file : 'index.php',
'caching' => TRUE,
'enable_query_strings' => TRUE,
'threshold' => isset($log_threshold) ? $log_threshold : 0,
));
——————————————————————————————————————————
八.自制session:
protected $session;
$this->session = Session::instance();
操作session:
protected function checkLogin ()//检查登录
{
if ($verifycode != $this->session->verifycode || !$this->session->userinfo ) {//从session里取不到
$this->session->userinfo = $this->ssoCheck($verifycode);//从接口里取并存入session
$this->session->verifycode = $verifycode;
......
}
return $this->session->userinfo;
}
protected function ssoCheck($verifycode){
......//查接口返回数据:'passport_url'=> 'http://passport.cntv.cn/ssocheck.jsp',Curl
$userinfo = json_decode($ret,true);
return $userinfo;//返回
}
九:写错误日志函数及错误的统一捕获:
场景:问,最近我做了一个日志系统,PHP在调用时出现因为异常错误导致没有运行到日志函数就退出了,
请问:怎么实现对这个错误的捕获?还问:PHP里的try catch可能并不能捕获里面的所有错误的实际情况。
这个问题其实是想问对PHP的set_error_handler函数,及错误处理函数,它捕获能导致php脚本停止运行的严重错误(这类错误是不能被set_error_handler ()捕获的),
一旦脚本停止运行,customend()函数就会被调用,在customend()函数中通过error_get_last()来判断脚本是正常结束还是发生严重错误而中断,如果是发生严重错误而中断,则运行错误处理程序。try-catch 无法在类的自动加载函数 __autoload() 内生效。try-catch 用于捕获异常,无法捕获错误,例如 trigger_error() 触发的错误,异常和错误是不一样的。
摘自:http://jingyan.baidu.com/article/046a7b3ed5e233f9c37fa969.html
参考:PHP 的异常处理、错误处理:error_reporting,try-catch,trigger_error,set_error_handler,set_exception_handler,register_shutdown_function
http://www.php-note.com/article/detail/779
******web端打开写SQL日志及分析。———show_debug_errors是前端错误打开于否的开关,
而那个threshold是分开关,如果要写Mysql日志则:$log_threshold = 3;小于3的threshold则不打印日志的,
上面两变量均在index.php入口配置,在application下的bootopen.php进行init()函数的赋值。******
如果等于threshold的值等于4则:debug: Request Library loaded ,也就是相当于debug的日志可打印到日志里来了,等于1只报error错误日志,如等于0则全关闭了即使有错。
* Log Thresholds:
* 0 - Disables logging completely
* 1 - Error Messages (including PHP errors)
* 2 - Alert Messages
* 3 - Informational Messages
* 4 - Debug Messages
*/
$log_threshold = 3;
对于这个;show_debug_errors这个值,则应该用Nginx给屏蔽掉,当然上线后关闭是最好的,提高运行效率及稳定性,Nginx配置如下:
实践Ok如下:
如错误页面在这儿:http://my.jackxiang.com/error
vi my.cnf
在server里添加:
error_page 404 = ./error;
error_page 500 502 503 504 = ./error;
#下面也行:
error_page 404 = http://my.jackxiang.com/error.html;
error_page 500 502 503 504 = http://my.jackxiang.com/error.html;
来自自己的参考:http://jackxiang.com/post/6769/
要想写日志得满足三个条件:
1./application/bootopen.php 里的'errors' 是TRUE,这样就注册了register_shutdown_function函数,进而在log.php里写日志
2.如果是后台程序立即触发就写,前台程序是关闭的写的。
register_shutdown_function这个的用法见,好多单例都给整了这个东东在上面:http://jackxiang.com/post/6784/
例1:核心都有用到register_shutdown_function
/libraries/core.php
// Enable the Ko shutdown handler, which catches E_FATAL errors.
register_shutdown_function(array('Ko' , 'shutdown_handler'));
if (self::$errors === TRUE) {
// Enable the Ko shutdown handler, which catches E_FATAL errors.
register_shutdown_function(array('Ko' , 'shutdown_handler'));
// Enable Ko exception handling, adds stack traces and error source.
set_exception_handler(array('Ko' , 'exception_handler'));
// Enable Ko error handling, converts all PHP errors to exceptions.
set_error_handler(array('Ko' , 'error_handler'));
}
例2:web运行完再写日志更是常用:
/libraries/log.php
// Write the logs at shutdown
register_shutdown_function(array(self::$_instance[$group], 'write'));
frontend/index.php //这儿打开的******(这块打开如果action类和方法不存在则会直接报错)
// Define wether show debug message.
$show_debug_errors = true;
/application/bootopen.php
Ko::init( array(
'base_url' => '/',
'errors' => isset($show_debug_errors) && $show_debug_errors,
/libraries/core.php
if (self::$errors === TRUE) {
// Enable the Ko shutdown handler, which catches E_FATAL errors.
register_shutdown_function(array('Ko' , 'shutdown_handler'));
/libraries/log.php
public static function instance($group ='default')
{
if (!isset(self::$_instance[$group]) ||self::$_instance[$group] === NULL) {
// Create a new instance
self::$_instance[$group] = new self($group);
// Write the logs at shutdown
register_shutdown_function(array(self::$_instance[$group], 'write'));
}
return self::$_instance[$group];
}
\libraries\database.php
// Log levels
private static $log_levels = array (
'error' => 1,
'alert' => 2,
'info' => 3,
'debug' => 4,
);
public static function log($type, $message)
{
// Should we log these to file?
if (self::$log_levels[$type] <= self::$threshold) {//type==debug<====4<=2
self::$log->add($type, $message);
}
}
\libraries\database.php
// Log query
Ko::log('info', str_replace(array("\r\n", "\r", "\n"), " ", $sql));
self::$log->add($type, $message);
}
十:上完静态文件,要改config.php里的version,以防止那个CDN了旧的,没法查看。
<?php
defined('SYS_PATH') or die('No direct access allowed.');
return array(
'version' => '2.005',
————————————————————————————
jquery.Jcrop.js?ver=<?php echo $this->_tpl_vars['version']; ?>
直接在框架初始化时赋值比较方便编写些,多层继承的最上层写上:
class Site_PartinController extends FrontBaseController
class FrontBaseController extends FrontController
class FrontController extends InitController
class InitController extends Controller(init.php里进行模板赋值的)
/application/controllers/init.php
/**
* Overload
*/
public function after ()
{
$this->view->version = KO::config('config.version');
模板里:
<script src="<{$staticUrl}>js/My97TimePicker/WdatePicker_time.js?ver=<{$version}>" type="text/javascript"></script>
flashvars["config"] ="<{$flashXmlUrl}>?ver=<{$version}>";// 如上传组件初始化参数的读取加上版本号
十一:单例模式,一些常规操人作,多用单例模式,这种模式不适合用于写PHP的daemon,容易出现内存不够,如不及时释放:
尽管PHP退出后就释放了,不像Java,但好处还是有的:http://blog.csdn.net/jungsagacity/article/details/7618587
http://blog.sina.com.cn/s/blog_6f49a3c30100qgiy.html
\libraries\session.php
public static function instance ($group = 'default')
protected function __construct ($group = 'default')
register_shutdown_function(array($this , 'write_close'));
public function sessionid ()
public function create ()
public function regenerate ()
public function unsetAll()
public function destroy ()
public function write_close ()
public function delete ($keys)
使用方法:
// Init Session
$this->session = Session::instance();
$this->session->set('findemail',$userInfo['email']);
$findemail = $this->session->get('findemail');
$this->session->unsetAll();
\libraries\config.php
public static function instance ($group = 'default')
final private function __construct ($group = 'default')
public function attach (Config_Driver $reader, $first = TRUE)
public function detach (Config_Driver $reader)
public function load ($group)
public function save ($file, $config = NULL)
public function copy ($group)
final private function __clone ()
使用方法:
$config[$group] = Config::instance('messages')->attach(new Config_File('messages'))->load($group);
Config::instance('lang')->attach(new Config_File('i18n'))->load($lang . DIRECTORY_SEPARATOR . $group);
Config::instance('messages')->attach(new Config_File('messages'))->save($file, $data);
更高级别封装readConfig:$this->_pbConfig = $this->readConfig('pbconfig');
controllers/global.php
/**
* 读取数组配置文件信息
* $this->readConfig('config'); ==> data/messages/config.php
* @param string $group 配置文件名,不带扩展名.php
*/
protected function readConfig($group)
{
\libraries\log.php
public static function instance($group ='default')
final private function __construct()
register_shutdown_function(array(self::$_instance[$group], 'write'));
public function attach(Log_Writer $writer, $types = NULL)
public function detach(Log_Writer $writer)
public function add($type, $message)
public function write ()
private function __clone()
使用方法:
$returnlog = Log::instance('videoreturn')->attach(new Log_File(DATA_PATH . 'logs/' . date('Ymd') . '/', 'setCheckVideoreturn-' . date('Ymd') . '.php'));
$logger = Log::instance(strtoupper($prefix))->attach(new Log_File(DATA_PATH . 'logs/' . date('Ymd') . '/', $prefix . '_' . date('Ymd') . '.php'));
$sharelog = Log::instance('share')->attach(new Log_File(DATA_PATH . 'logs/' . date('Ymd') . '/', 'share-' . date('Ymd') . '.php'));
$sharelog->add('succ:',' from vid:'.$vid.' to uuid :'.$videoData['video']['uuid']);
分析该写日志的实现:
$log = Log::instance(__CLASS__)->attach(new Log_File(DATA_PATH . '/logs/' . date('Ymd') . '/mysql-error-' . date('Ymd') . '.log'));
1)文件及目录都是777:
new Log_File:\libraries\log\file.php, 分析文件函数:
(1)目录777:__construct里直接创建目录: if (! mkdir($directory, 0777, TRUE) || ! is_writable($directory)) {
(2)文件777:public function write(){
\libraries\cookie.php
final private function __construct ()
public static function get ($key, $default = NULL)
public static function set ($name, $value, $expiration = NULL)
public static function delete ($name)
使用方法:
Cookie::set('language1',$language);
$lang = Cookie::get('language1');
十二:关于前端代码的路径等配置:前端Js/css静态域名版本号(反CDNl:version)的配置:
/controllers/site/preview.php
关于版本号及前端,每个controller下都有一个preview.php层层去继承InitController在具体的controller下配置相关前端Js输出及CSS输出的路径:
$this->view->version = KO::config('config.version');
public function after()
{
//$this->getwatch();
$this->view->themePath = $this->themePath;
$this->view->publicPath = 'themes/common';
$this->view->cssPath = 'themes/zh';
$this->view->jsPath = 'themes/zh';
$this->view->lang = (array)Ko::lang('common','zh');
$this->view->staticURL = KO::config('url.static_url');
$this->view->mainURL = KO::config('url.main_url');
$this->view->flashURL = KO::config('url.flash_player_url');
$this->view->basePath = $this->getBaseUrl();
$this->view->version = KO::config('config.version');
$this->view->render();
}
十三:smarty模板输出:
最上层的controller下的before还负责smarty模板的指定:
\libraries\controller.php
public function before ()
{
if ($this->auto_render === TRUE) {
// Load the template
$this->template = $this->request->getController() . '/' . $this->request->getAction();
$this->view = View::factory($this->template);
}
}
继承后再重写:
public function before ()
{
// Auto render , use user defined template.
$this->autoRender(TRUE);
parent::before();
———————上面的before,下面的after都是在Library/request.php里进行按before在前,action在中间,after在后面处理的__________
Before处理如下前期问题,参数获取/模板工厂(smarty)等层层继承,层层parent::before();:
//Overload:英文是重载,国人:同名函数,叫 方法重写
class ActController extends FrontController
{
public function before()
{
parent::before();//访问InitController里的after
}
}
class FrontController extends InitController
{
/**
* Overload
*/
public function before()
{
$lang = '';
parent::before();// 初始化模板等...
class InitController extends Controller
{
public function before()
{
// Init Session
$this->session = Session::instance();//获取参数等全局逻辑准备
$this->varname = trim($this->getRequest()->getParam('varname'));
$this->jsoncallback = trim($this->getRequest()->getParam('jsoncallback'));
}
abstract class Controller
public function before ()
{
if ($this->auto_render === TRUE) {
// Load the template
$this->template = $this->request->getController() . '/' . $this->request->getAction();
$this->view = View::factory($this->template);//模板进入工厂
}
}
十四:关于after:
这块输出头也是放after里的:
\controllers\global.php
public function after()
{
// Send headers: Cache-Control & Expire.
$max_age = $this->getRequest()->getParam('max_age', 0);
if ($max_age > 0) {
$this->getRequest()->addHeader('Cache-Control', 'max-age=' . $max_age);
$this->getRequest()->addHeader('Expires', gmdate('D, d M Y H:i:s', time() + $max_age) . ' GMT');
}
else {
$this->getRequest()->addHeader('Cache-Control', 'no-cache');
$this->getRequest()->addHeader('Expires', '0');
$this->getRequest()->addHeader('Pragma', 'No-cache');
}
parent::after();
}
上面的after函数被:\controllers\front.php重写并parent调用之:
/**
* Overload
*/
public function after()
{
......
$this->view->version = KO::config('config.version');
$this->view->pageCode = $this->getPageCode();
$this->autoRender(TRUE);
parent::after();
}
它也是在后面才输出的,调用在Library/request.php:
上面的日志在后面after写的:
libraries\request.php
$class->getMethod('after')->invoke($controller);
摘录自: \libraries\request.php
try {
// Load the controller using reflection
$class = new ReflectionClass(ucfirst($prefix) . ucfirst($this->controller) . 'Controller');
if ($class->isAbstract()) {
$this->errorPage('Cannot create instances of abstract controller: :error',
array(':error' => $prefix . ucfirst($this->controller) . 'Controller'));
}
// Create a new instance of the controller, Interrupted by dispatcher anytime
$controller = $class->newInstance($this);
if ($this->isDispatched()) {
$class->getMethod('before')->invoke($controller);
}
try {
$action = empty($this->action) ? Route::$default_action : $this->action;
if ($this->isDispatched()) {
$class->getMethod($action)->invokeArgs($controller, $this->params);
}
} catch (ReflectionException $e) {
$class->getMethod('__call')->invokeArgs($controller, array($this->getAction() , $this->params));
}
if ($this->isDispatched()) {
$class->getMethod('after')->invoke($controller);
}
}
十五:关于日志(新加daemon日志实现立即写,不用注册的退出时才写:register_shutdown_function):
日志输出在web时是执行完后再输出,而cli模式是立即输出,(关于错误级别$type定义在第九有备注到,如info级别:Ko::log('info', str_replace(array("\r\n", "\r", "\n"), " ", $sql));),如下:
\libraries\log.php
public function add($type, $message)
{
// Create a new message and timestamp it
$this->_messages[] = array (
'time' => date(self::$timestamp),
'type' => $type,
'body' => is_string($message) ? $message : var_export($message, true),
);
if(Ko::$is_cli === TRUE) {
$this->write();
}
return $this;
}
WEB没有立即写,而是后面才写,并没有在request中的after里,而是注册了shutdown函数write:
public static function instance($group ='default')
{
......
// Write the logs at shutdown
register_shutdown_function(array(self::$_instance[$group], 'write'));//这儿调用写
}
return self::$_instance[$group];
}
调用:\controllers\audit.php
$returnlog->add('check start', '-----------------');
$returnlog->add('data post', $post);
这种实现方法在如果用php做daemon时会遇到从不退出就没有调用写导致里面的数据太大出现php内存不够并意外退出问题:
遇到问题,当守护进程时写日志时往往会出现因为框架是web的,在最后退出才写,而daemon是不退出的,导致内存暴增,解决办法如下:
修改为,如果是cli模式立即就写了得了:
十六:使用modules外挂模块方法:
Ko::modules( array(
'weibo' => 'weibo',
'libspace' => 'libspace',
'face' => 'face',
'nusoap' => 'nusoap',
'sdk' => 'sdk',
));
jackxiang.com/modules/ //jackxiang.com是根目录
face/ libspace/ nusoap/ sdk/ weibo/
ls modules/sdk/ //init.php文件名是必须要有的。
common.php init.php justwinitapi.ex.class.php
调用:
./application/models/app.php: $justwinit = new justwinit();
./application/controllers/front/front.php: $justwinit = new justwinit();
/**
* Overload
*/
public function before()
{
$this->checkToken();
}
public function checkToken()
{
$justwinit = new justwinit();
.......
}
十七:框架扩展:在\libraries\core.php里加一个写日志函数,以给daemon程序写当是cli模式时写:
十八:图片缩放之gd,示例:
关于图片上传及缩小,用到上传类libraries//upload.php,缩小gd类libraries/image.php ,
这儿遇到一个问题是上传后缀是大写的JPG时会报错,最后用strtolower($ext)保存为小写就没这个问题,
我从其它博客试了一下确实存在警告,最好还是小写的jpg后缀为好:http://jackxiang.com/post/3022/
缩小为148.148的等比例缩放,其使用方法如下:
十九:关于单例模式里和观察者模式一块用的问题:
1)写法于传统的PHP写法背离,不像C了,在写得少的人看来很是怪异:
$log = Log::instance(__CLASS__)->attach(new Log_File(DATA_PATH . 'logs/' . date('Ymd'), __CLASS__.'-' . date('Ymd') . '.log'));
2)这种单例与观察模式在代码提示时出现无法跳转,因为再智能的编辑器也没法知道前面这个数组是哪个具体对象了:
$writer['object']->write($messages);//这样当于一个写的类句柄通过观察者数组中取出后:Log_File->write($messages);
3)但为了什么耦合,为了少几个句柄,为了提高效率等,还得用,自己作下分析性参考:http://jackxiang.com/post/6825/ 。
二十:观察者模式:
http://www.cnblogs.com/baochuan/archive/2012/02/22/2362668.html
框架里的观察者模式:
/libraries/log.php
片段如下,这里的writer就是一个进来的类对象数组的一个集合(全是对象),这个:
这个对象去分别调用对象类里的方法,和上面的原始摘录Url里的示例一样的,如下用foreach去一个个的调用write方法,$writer['object']->write($messages);:
二十一:关于memcache或ttserver的缓存实现及配置:
配置文件这样写:
config/cache.php
使用时要这样用:
class TagModel extends Model
{
protected $cachePermanent = 'tt_server';// Permanent Cache KEY,来自配置文件的数组key,及config/cache.php tt_server对应。
return (array)$this->getPermanentCache($userid);
return $this->setPermanentCache($userid, $tags);
为什么可以这样用,因为前面的extends Model这个类的libraries/model.php里有:
[root@iZ25z0ugwgtZ libraries]# grep -r "setPermanentCache" ./
./model.php: protected function setPermanentCache($key, $data)
// Cache KEY
protected $cacheConfig = 'default';
protected function getCache($key)
{
return Cache::instance($this->cacheConfig)->get($this->makeCacheKey($key));//静态调用instance方法获取该类的实例
}
这个cache是有各种cache,这个是ttserver,我们来看它是如何实现这个实例的文件,libraries/cache/ttcache.php :
我都说这个直接传入一个string的tt_server,是怎么从前面配置文件里读取到这个数组的,不仅仅是前面的这个instance函数对这各种cache设备
类进行实例化外,还有对这个下面挂的设备本身的相关配置进行初始化,代码如下:
框架分析到此完结。EOF
AddTime:2016-05-28
加载外部modules方法及入口文件:
// Set the current module list
self::$_modules = $modules;
foreach (self::$_modules as $path) {
$init = $path . DIRECTORY_SEPARATOR . 'init.php';
if (is_file($init)) {
require_once $init;
}
}
也就是说会核心框架会自动加载这个init.php
D:\www\xiyou.cntv.cn[新svn代码]\trunk\codes\modules下的OAuth weibo qiniu 里面都有一个init.php,而这前面三个目录是怎么找的呢?
D:\www\xiyou.cntv.cn[新svn代码]\trunk\codes\application\bootstrap.php 里有让modules相对或绝对路径。 relative or absolute path.:
==================================
概述以上分析,从更高层次来分析其入口的调用层次分析:
model独立,model目录里文件均无互相调用(缓存也在这层,也就是数据层),
service加上业务调用model model/open,service之前也可相互调用给application提供数据。
application可以有多个目录,如多了个openapi的目录,这个目录可通过路由来配置指向即可:
./bootstrap.php:Route::set('openapi_route', 'openapi(/<controller>(/<action>(/<__KO_VARS__>)))', array('__KO_VARS__' => '.+'))
这个对像是传入/libraries/request.php,它又和/libraries/core.php接合,接合autoload和反射类到上面这些model、service、controller里找对应,
找不到时用try..catch出来错即可,而这个bootstrap.php,则被./frontend/index.php ./open/index.php包含进来(它里面有错谴责级别设置)后,进行:
// Bootstrap the application
require_once APP_PATH . 'bootopen.php'; ,于是对外可呈现出多个域名,因为在nginx里直接配置:root /data/htdocs/xiyou_alpha/frontend;就会从
fronted/index.php单入口里穿透并进入到各个层里,运行了,在层次上清晰,在代码上可灵活配置,这就是Ko的优势了,MVC加server层次间的调用规定如下:
一)model目录一个目录:open
二)service目录里调model的open里的类用前面加Open_,调open一级的就不用了。
三)controller里有open openapi两个目录,均调用了service里的open目录:
比如:
controller
./openapi/partner.php: $Open_OpenService = new Open_OpenService();
./open/usercenter.php: $open_OpenService = new Open_OpenService();
_________Model_________
.........//主站的model
/model/open/...开放平台
也就是说model最好是简单一点,service文件间可互相调用:
vi services/open/open.php Open_OpenService 原型可被同级的server其它文件调用如下:
vi services/audit.php: $Open_OpenService = new Open_OpenService();
而这个service呢,也调用了model层里的open目录下的modle:
$tokenModel = new Open_TokensModel();
$info = $tokenModel->getTokenInfoByToken($token);
上面两行的位置在哪儿?能通过类前面加上Open知道是model/open/位置:
/application/models/open/tokens.php class Open_TokensModel extends Model
为何getCache得在services目录里的base.php和library目录下的model.php里有:
protected function getCache($key)
{
return Cache::instance($this->cacheConfig)->get($this->makeCacheKey($key));
}
getCurrentPageVideosFromDB在model里不需要cache,但是getCurrentPageVideos就需要cache了:
public function getVideosByUserId($userId)
{
$userVideos = $this->getCache($userId);
if ($userVideos) {
return $userVideos;
}
$userVideos = $this->getVideosByUserIdFromDB($userId);
if ($userVideos && count($userVideos) > 0) {
$this->setCache($userId, $userVideos);
}
return $userVideos;
}
同样,在service里有些变量也需要cache,如cookie变量从cache里读取,为何需要专门函数实现?因为怕cache重复了,结合函数名,查询参数确保唯一。
/**
* 写入cookie - 您最近看过的
*/
public function setCookieHistoryVideoList($uuid)
{
//设置显示“您最近看过的”视频的个数
$MAX_COUNT = 5;
$cookieId = Cookie::get('xiyou_ck_id');
$cookieData = $this->getCache($cookieId);
if(!$cookieData){
$cookieData = array();
}
$videolist = isset($cookieData['history_list']) ? $cookieData['history_list'] :false;
if(!is_array($videolist)){
$videolist = array();
。。。。。oracle没问题。oracle有一个特别好的功能。。那就是。。你随便挂一块磁盘,在oracle里,都可以认为是一个表分区[当然是需要设置的],现在mysql 5.3好象支持分区表?新的版本才支持的。
10月15日消息,百度技术牛人廖若雪近日就职场成长经历与感悟与腾讯科技对话,揭秘当前正面临人生职业规划的大学毕业生择业的三大要诀。阅读全文
日前在美国俄勒冈州波特兰市举行的首届LinuxCon大会上,Linux操作系统内核的创始人Linus Torvalds语出惊人:Linux已经相当臃肿。没错,虽然开源人士们一直都在以指责Windows系统的肥大臃肿为乐,但随着Linux的使用范围越来越广,越来越的外加组件已经让Linux的规模变得巨大而可怕。
不过,这并非意味着Linux的失败,反而彰显了它的成功。因为Linux现在要面对的是越来越广泛的应用,规模增大是难免的成长的烦恼。
Linus Torvalds在会上也承认,现在的Linux已经不是当年我开始编写时那个极精简的,超高效的核心,功能、应用范围已有了翻天覆地的变化。在此次大会的开幕演讲中,Linux基金会执行总监Jim Zemlin就表示:Linux正在驱动各种各样的设备,从企业数据中心,到当今时间半数的新品智能手机。
经常有人问网站从头到尾是如何完成的,web项目开发的流程是什么呢,网站从需求到完成需要哪些阶段呢等系列问题,自己感觉做起来还较顺手,但是表达起来还确实比较困难,今日看到一文章,感觉写的还不错,摘抄下来,顺便修饰加上自己的一些见解。
--------------------------------------------------------------------------------
web2.0时代web系统的开发应该以用户需求为第一位,UI(用户交互界面),UE(用户体验),变得尤为重要。项目开发流程大致是这样的:
项目开发流程阅读全文
--------------------------------------------------------------------------------
web2.0时代web系统的开发应该以用户需求为第一位,UI(用户交互界面),UE(用户体验),变得尤为重要。项目开发流程大致是这样的:
项目开发流程阅读全文
select year(curdate()),month(curdate()),day(curdate());
select weekofyear(curdate());
有点细微的差别,weekofyear(date)相当于week(date,3).
下面是week(date,Mode),取值的说明,根据自己的实际情况取自己想要的值
Mode 工作日 范围 Week 1 为第一周 ...
0 周日 0-53 本年度中有一个周日
1 周一 0-53 本年度中有3天以上
2 周日 1-53 本年度中有一个周日
3 周一 1-53 本年度中有3天以上
4 周日 0-53 本年度中有3天以上
5 周一 0-53 本年度中有一个周一
6 周日 1-53 本年度中有3天以上
7 周一 1-53 本年度中有一个周一
select date_format(now(),'%Y')
select date_format(now(),'%m')
select date_format(now(),'%e')
select date_format(now(),'%U')
select year(curdate()),month(curdate()),dayofyear(curdate()),weekofyear(curdate());
select weekofyear(curdate());
有点细微的差别,weekofyear(date)相当于week(date,3).
下面是week(date,Mode),取值的说明,根据自己的实际情况取自己想要的值
Mode 工作日 范围 Week 1 为第一周 ...
0 周日 0-53 本年度中有一个周日
1 周一 0-53 本年度中有3天以上
2 周日 1-53 本年度中有一个周日
3 周一 1-53 本年度中有3天以上
4 周日 0-53 本年度中有3天以上
5 周一 0-53 本年度中有一个周一
6 周日 1-53 本年度中有3天以上
7 周一 1-53 本年度中有一个周一
select date_format(now(),'%Y')
select date_format(now(),'%m')
select date_format(now(),'%e')
select date_format(now(),'%U')
select year(curdate()),month(curdate()),dayofyear(curdate()),weekofyear(curdate());
在原创那边写了几个php+ajax的应用例子,今天和新手谈谈smarty+xjax,希望对新手有帮助,xajax是用PHP写的ajax开发框架,可以生成JS代码,这样使用起ajax就比较简单了,今天结合模板引擎smarty,来实现一个检测用户名合法性的小程序,大家有兴趣的话还可以扩展这个程序到自己的应用中,嗯,这里写出核心代码,里面注释很详细,不过建议大家看之前还是看看这个http://blog.csdn.net/fhiesc/archive/2006/07/04/873441.aspx,相信你会很快明白xajax是什么东东,及如何使用,最后依然是效果图和源代码下载。好的,看代码吧:阅读全文
winver---------检查Windows版本
wmimgmt.msc----打开windows管理体系结构(WMI) 阅读全文
wmimgmt.msc----打开windows管理体系结构(WMI) 阅读全文
今天为大家推荐一些经典的PHP开源程序,来满足你架设独立网站的需求。不管是博客、SNS社交、Digg,还是小型的门户网站,PHP都可以在相当程度上满足你。阅读全文
无情地赞美一下Z-blog,我想我有资格,怎么说也是个老伯(博)了。我建博客3年了,使用过(或疯狂测试过)的博客程序有Z-Blog、O-blog(已停止开发)、pjblog、bo-blog、wordpress、movabletype、SaBlog等。我是一个好奇心很强同时又苛求完美的人,这一点在blog程序的选择上表现得很明显。阅读全文
http://b.cnc.qzone.qq.com/cgi-bin/blognew/blog_output_data?uin=40317647&blogid=1228464038&imgdm=imgcache.qq.com&bdm=b.cnc.qzone.qq.com&mode=1&numperpage=15&blogseed=0.05020814931591866&property=GoRE×tamp=1255757003
阅读全文
阅读全文