东邪西毒

你这种年青人我见的多啦,懂一点武功就以为可以横行天下,其实走江湖是一件很痛苦的事。会武功,有很多东西不能做。你不想耕田吧?又不耻去打劫,更不想抛头露面在街头卖艺,你怎么生活?武功高强也得吃饭的。有一种职业很适合你,既可以帮你赚点银两,又可以行侠仗义,你有兴趣吗?你呀,考虑一下,不过要快一点,你知道,肚子很快会饿的。

欧阳峰:为了一个鸡蛋而失去了一只手指,值得吗?
洪七 :不值得!但是我觉得痛快,这才是我自己。本来我应该没事,但是我的刀没以前快。我以前快是因为我直接,认为对就去做,从来不会想什么代价。我以为我这一辈子都不会变,直到那个女孩来求我,我才发觉我完全变了,我竟然没有答应她,因为我知道你一定不会答应。那天,我很失望,我觉得我已经和你混在一起,变成一个人,没有了自己。我不想跟你一样,因为我知道欧阳峰绝对不会为一个鸡蛋去冒险,这是我和你的分别。

 

洪七 :以后我再也不能用刀了。
欧阳峰:不一定要用刀,赤手空拳也能杀人。你不过是少了根手指,这也没什么,好歹还有份差事。怎么,想回家乡?要是为了这个就想回家乡,为什么当初你又你要出来。
洪七 :这个沙漠的后面是什么地方?
欧阳峰:是另外一个沙漠。
每个人都会经历这个阶段,看见一座山,就想知道山后面是什么。我很想告诉他,可能翻过去山后面,你会发觉没有什么特别,回头看会觉得这边更好。但是他不会相信,以他的性格,自己不试试是不会甘心。

欧阳峰:你打算上哪儿?
洪七 :去一个我没去过的地方,希望闯出个名堂。如果你以后在江湖上听说一个九指的英雄,那一定是我。
欧阳峰:她呢?
洪七 :带她一起去吧。像你说的,事在人为,谁说过不准带老婆闯荡江湖,对不对!

 

ssh公钥文件无效

ssh可以通过公钥私钥免密码登录,但是今天在登录一台项目服务器,已经把我自己的机器的pubkey放在了那台登录服务器的/root/.ssh/authorized_keys里面,但是登录的时候还是提示需要密码,搜索找到了类似问题,参照别人提示,开启debug模式找到问题并解决。

原来 .ssh被改了属主,不是属于root用户了。所以还是提示需要密码。把.ssh目录改回root属主,文件解决,附上错误截图。

参考链接:https://yq.aliyun.com/articles/460672

ctime mtime atime

Unix/Linux系统文件有三个时间,ctime, mtime,atime。说说三个之间的区别。

ctime很多人以为是create time,创建时间。实际表示的是change time,改变时间。mtime是modify time,也可以翻译成改变时间。atime就是 access time。

st_atime

           Time when file data was last accessed. Changed by  the
following   functions:   creat(),   mknod(),   pipe(),
utime(2), and read(2).st_mtime
Time when data was last modified. Changed by the  fol-
lowing  functions:  creat(), mknod(), pipe(), utime(),
and write(2).

st_ctime
Time when file status was last changed. Changed by the
following   functions:   chmod(),   chown(),  creat(),
link(2),  mknod(),  pipe(),  unlink(2),  utime(),  and
write().

atime记录访问的时间,像创建读取或者执行的时候。
mtime随文件内容写入而改变。
ctime随文件读写入,改变文件名称,文件归属而改变。
chmod ctime修改,mtime不改。
cat atime修改。
ls -l 显示mtime
ls -lu 显示atime
ls -lc 显示ctime
参考链接:
1.http://www.cnblogs.com/ghgyj/p/3911621.html
2.http://www.cnblogs.com/oray/p/3786440.html

免费ssl证书

上次找到阿里云免费证书,但是只有一年期限。昨天由于公司网站迁移服务器,又找到了一款其他的免费https证书。名字叫letsencrypt,官网:https://letsencrypt.org。

官网推荐使用工具安装,工具网址:https://certbot.eff.org。

选择好服务器软件和服务器系统,以centos6和nginx为例。

1.wget https://dl.eff.org/certbot-auto

chmod a+x certbot-auto

下载certbot-auto文件,并增加可执行权限。

2.sudo ./path/to/certbot-auto –nginx。

运行certbot-auto需要root用户权限,后面跟参数–nginx,自动安装证书并修改nginx配置文件。如果只想安装证书,而手动修改nginx配置文件,–nignx参数后面加 certonly参数。

3 把生成的证书文件加载到nginx 对应的域名配置文件里面去,这个可以生成多个子域名的免费证书,阿里云免费证书只有一个域名。

4,免费的证书只有90天有效期,需要手动更新。更新命令sudo ./path/to/certbot-auto renew。这样就生成好了,如果只想测试下更新命令,后面加参数–dry-run。最后把更新命令加到crontab执行计划任务里面去。安装完毕。

最后,如果在安装证书第一步失败,提示nginx配置文件路径找不到,可以nginx安装路径参数,–nginx-server-root=/usr/local/nginx/conf。

官网网址:https://certbot.eff.org/lets-encrypt/centos6-nginx。

mysql count函数

mysql count函数,返回匹配行数是总数量。原型count(expr),有的人写count(*),也有人写count(column_name),比如count(id)。还是看官方手册怎么写的。

COUNT(*) is somewhat different in that it returns a count of the number of rows retrieved, whether or not they contain NULL values.

For MyISAM tables, COUNT(*) is optimized to return very quickly if the SELECT retrieves from one table, no other columns are retrieved, and there is no WHERE clause. For example:

SELECT COUNT(*) FROM student

 

count(*)有些不同,返回检索的行数,不管是否包含null值。对于MyISAM引擎,count(*)被优化成快速返回,如果select从一张表里接受值,没有其他的行数被检索,而且没有where条件。

意思就是对于MyISAM仅仅查询表的行数,不检索其他列,也没有where条件。count(*)是最快的返回方式。

 

This optimization only applies to MyISAM tables, because an exact row count is stored for this storage engine and can be accessed very quickly. COUNT(1) is only subject to the same optimization if the first column is defined as NOT
NULL
.

这种优化只针对MyISAM么表,因为具体的行数被存储引擎存储而且很容易快速访问。count(1) 仅仅在首列不被定义为不空时和count(*)相同。

COUNT(expr)

Returns a count of the number of non-NULL values of expr in the rows retrieved by a SELECT statement. The result is a BIGINT value.

If there are no matching rows, COUNT() returns 0.

返回表达式的值不为空的查询行数。

结论:count函数要针对不同的存储引擎来看。不带where条件的,用count(*)。带where条件的,受where条件限制。count(1)首列值不为空和count(*)同等效果。

 

session和cookie区别

老问题了,每次面试必问。还是引用wiki上面的解释吧。

session,wiki叫session id。session翻译中文就是会话,ssh登录unix系统,也是一个session。表示一种状态,一个概念。session id是对概念具体的实现。

In computer science, a session identifier, session ID or session token is a piece of data that is used in network communications (often over HTTP) to identify a session, a series of related message exchanges.

session id 是一块数据用在网络通信中,经常是HTTP通信中定义的一个状态,为了一系列的数据交换。https://en.wikipedia.org/wiki/Session_ID

cookie在wiki中定义是http cookie,表示是http协议中的一部分。

An HTTP cookie (also called web cookie, Internet cookie, browser cookie, or simply cookie) is a small piece of data sent from a website and stored on the user’s computer by the user’s web browser while the user is browsing.

http cookie也叫web cookie,互联网cookie ,浏览器cookie。是网站被用户浏览时,从网站发送过来并保存在用户电脑上面的一小段数据。

wiki:https://en.wikipedia.org/wiki/HTTP_cookie

wiki比较客观公正吧。

session是保存在服务器端的,而且持续时间也很短,一般只保存在用户浏览时段,浏览器关掉session也跟着清除掉,跟客户端的通信有两种方式,可以设置一个session cookie,跟着浏览器一起请求过来,也可以把session放在url上当做参数一起请求过来,大多数采用session  cookie这种方式。PHP生成session之后,有个PHPSESSID的cookie。

cookie是保存在客户端上面的一段数据,持续时间也比较长些。

php json_decode返回null

给app写一个接口,返回数据格式为json。自己本地写curl模拟请求,把结果json_decode解析下,本以为会返回数据,结果返回一个null。拼接好参数浏览器里面请求,返回的又是乍看一个正常的json格式字符串。编码也是UTF-8,也把多余的空行删掉了,但是还是返回的null。又回到浏览器那串字符串上面找问题。

乍看是一个json格式字符串,但是复制到bejson网站解析时,确多了个红点。F12查看下网络,字符串前面多了个&#65279。搜索下这个词,出现很多页面,看了下大概明白了什么原因,文件里面多了BOM头。

BOM(Byte order mark),字面意思是字节顺序标记,都知道计算机有大端小端顺序争论,甚至不惜发动战争。wiki原文引用:

The byte order mark (BOM) is a Unicode character, U+FEFF byte order mark (BOM), whose appearance as a magic number at the start of a text stream can signal several things to a program consuming the text

在文本流前面作为一个幻数,可以向使用文本的程序发出几个信号。三个字节16进制表示问0xEF,0xBB,0xBF。

网上也提供了很多方法,用编辑器保存为utf-8无bom的。我也都试过了,也都没改过来。最后写个函数处理掉的。

function removeBom($str){

if(!empty($str)){

$c1 = substr(0,1);

$c1 = substr(1,1);

$c1 = substr(2,1);

if(ord($c1) == 239 && ord($c2) == 187 && ord($c3) == 191){

return substr($str,3);

}

}

}

bom头还有个危害就是session和cookie也都用不了了,在编码的时候编辑器保存文件还是要选择utf8无bom格式。

把带有bom的返回结果用函数清楚下,问题就解决了。虽然很慢,但是没放弃前进。

thinkphp5分析一

thinkphp在十年周的时候,发布了新的版本thinkphp5。官方如下介绍:

ThinkPHP5.0版本是一个颠覆和重构版本,采用全新的架构思想,引入了更多的PHP新特性,优化了核心,减少了依赖,实现了真正的惰性加载,支持composer,并针对API开发做了大量的优化,包括路由、日志、异常、模型、数据库、模板引擎和验证等模块都已经重构,不适合原有3.2项目的升级,请慎重考虑商业项目升级,但绝对是新项目的首选(无论是WEB还是API开发)。

废话不说,一看详细。

下载的版本是5.0.16完整版,先看目录结构。

目录结构和版本3有几个不同的地方,支持composer,多了composer.json文件,composer确实是趋势,yii2版本也加入了composer支持。多了build.php文件,多了extend vendor两个目录。原来的think.php也以unix shell脚本文件的形式写的,下图源码。

但是这样一来,windows服务器怎么识别这个think文件,跟unix服务器不太一样。

extends看名字应该是放扩展,暂时是空的,跳过。

vendor目录包含composer的自动加载文件,文件,命名空间,类,psr4标准。topthink目录是原来版本3的几个类库,单独拿出来了。

下面看主目录。

绿色背景是tp5目录,黑色背景是tp3目录。文件命名分格变了,去掉了.class后缀文件名短了。5的目录多了console.php和console目录,这个是新的主入口文件Loader.php主要是加载类文件,增加了request和response两个类。所有的请求都在request类里面,相当于版本3dispatcher类的作用。定义了get,post方法。

5增加了自定义路由,也是重写了Route类的后果,后面单独写。知道Route类里面包含解析之后的module,controller,action动作。

start.php只有两行代码,包含base.php,然后就执行App:run()->send()。

APP类分析:

可以看到App里面有几个方法,其中有3个invoke开头的反射函数,把版本3里面的exec函数拆分成3个invoke函数。checkRoute方法和route方法还和以前一样,返回module,controller,action,最后通过反射去执行。

Model在版本5这个变化比较大,版本3model直接提供数据库操作方法。initialize函数初始化,没有连接数据库,就是官方说的懒链接,需要数据库才连接数据库。并且拆分为connection builder query三个类,这个也是和laravel框架一样了。

总结,thinkphp版本5支持composer,model也设计了懒连接,需要数据库才连接。都是随着技术趋势在进步,不忘初心。希望tp框架越走越远,越来越好。

 

 

 

 

 

thinkphp3.2分析三

现在说说thinkphp框架里面其他的工具类和实际的项目应用。

软甲开发都遵循MVC分层模式。M对应的是model,模型的意思,提供数据处理的功能,V对应view,视图层,主要提供面向终端用户展示的功能。C对应control,控制层,提供流程控制的功能。所有的业务流程控制都在控制层完成,是连接模型和视图层的枢纽,获取模型层的数据,然后传入视图层去完成展示。

thinkphp框架类里面也看到,controller类,model类,和template类。

controller类除了几个魔术方法,有提供了ajax返回,创建静态页面方法和display方法,display  show fetch都是调用view类里面对应的方法。view就是view类的实例。

model类里面很多方法,用于获取数据库的数据,thinkphp3.2里面的model直接和数据库连接,不支持ORM那种数据对象映射模型。

在model类构造方法时,db方法提供了数据库类实例化,保存数据库连接。

可以看到,数据库实例化是根据不同的配置,不同的配置选项返回一个不同的实例。有的项目需要数据库读写分离,这样就可以有不同的链接实例,只需要改动配置文件就可以,很方便。

model类里面提供了原生的execute和query方法,也有面向对象的new model()->where()->select()这种,用户手册里面有详细介绍。

thinkphp在view层中开启了模板引擎,默认用的smarty模板,view类基本也是对smarty类的重新封装。

还有几个其他的工具类,从文件名能够分别是起什么作用的。

auth是一个提供权限认证类,基于权限表。

behavior是行为基类,只提供一个run方法。

build类是初始化项目构建App下面的目录。

每个目录对应的名字都很清晰明了,里面多了个service目录,service里面包含一些服务类。在业务中碰到一些常用的操作,两个不同的控制器都需要调用,但是不是所有的控制器都需要,写在控制基类让所有控制类继承也不合适。类似php语法里面的trait功能,可以单独拿出来用的。不限制于在控制层,如果模型层有需要也可以调用。

后面还有缓存类,和数据库一样,提供不同的driver type。默认的有memcache,redis,apc,wincache,xcache。加密类提供的driver type有base64,think自带的。图像处理提供的driver type有gd库和imagick。session处理类,提供的driver type有db和memcache。日志类,文件上传类,显示分页类,verify是验证码类。还有跟Think目录同级的目录,也都是第三方或者其他内置的类库。这样所有的文件基本都过一遍了。

总结:thinkphp发展至今已有十几年的时间了。版本5是近两年才出现的,之前一直都是版本3。经过这么多年的发展而没有停止,仍然在国内占据了很多市场。个人感觉最关键的地方就是直观,没有绕的地方。满足大部分中小企业开发的需求,上手快速,学习成本低。缺点就是跟不上技术发展,近几年composer的出现,提供了很多的第三方类库包,但是开发组已经意识到了这个问题,开发出了版本5。声明说和3完全不一样的版本,支持composer,为了和laravel竞争吧应该是。后面了解了再分享。

thinkphp3.2分析二

Thinkphp.class.php::start();运行项目。

Thinkphp类包含两个私有属相,$_instance,$_map,10个公共静态方法。

start方法:

注册自动加载方法:autoload方法。

  1. // 注册AUTOLOAD方法
    spl_autoload_register(‘Think\Think::autoload’);

设定错误和异常处理

// 设定错误和异常处理
register_shutdown_function(‘Think\Think::fatalError’);
set_error_handler(‘Think\Think::appError’);
set_exception_handler(‘Think\Think::appException’);

// 初始化文件存储方式
Storage::connect(STORAGE_TYPE);

$runtimefile = RUNTIME_PATH.APP_MODE.’~runtime.php’;
if(!APP_DEBUG && Storage::has($runtimefile)){
Storage::load($runtimefile);
}else{
if(Storage::has($runtimefile))
Storage::unlink($runtimefile);
$content = ”;
// 读取应用模式
$mode = include is_file(CONF_PATH.’core.php’)?CONF_PATH.’core.php’: MODE_PATH .APP_MODE.’.php’;
// 加载核心文件
foreach ($mode[‘core’] as $file){
if(is_file($file)) {
include $file;
if(!APP_DEBUG) $content .= compile($file);
}
}

// 加载应用模式配置文件
foreach ($mode[‘config’] as $key=>$file){
is_numeric($key)?C(load_config($file)):C($key,load_config($file));
}

// 读取当前应用模式对应的配置文件
if(‘common’ != APP_MODE && is_file(CONF_PATH.’config_’.APP_MODE.CONF_EXT))
C(load_config(CONF_PATH.’config_’.APP_MODE.CONF_EXT));

// 加载模式别名定义
if(isset($mode[‘alias’])){
self::addMap(is_array($mode[‘alias’])?$mode[‘alias’]:include $mode[‘alias’]);
}

// 加载应用别名定义文件
if(is_file(CONF_PATH.’alias.php’))
self::addMap(include CONF_PATH.’alias.php’);

// 加载模式行为定义
if(isset($mode[‘tags’])) {
Hook::import(is_array($mode[‘tags’])?$mode[‘tags’]:include $mode[‘tags’]);
}

// 加载应用行为定义
if(is_file(CONF_PATH.’tags.php’))
// 允许应用增加开发模式配置定义
Hook::import(include CONF_PATH.’tags.php’);

// 加载框架底层语言包
L(include THINK_PATH.’Lang/’.strtolower(C(‘DEFAULT_LANG’)).’.php’);

if(!APP_DEBUG){
$content .= “\nnamespace { Think\\Think::addMap(“.var_export(self::$_map,true).”);”;
$content .= “\nL(“.var_export(L(),true).”);\nC(“.var_export(C(),true).’);Think\Hook::import(‘.var_export(Hook::get(),true).’);}’;
Storage::put($runtimefile,strip_whitespace(‘<?php ‘.$content));
}else{
// 调试模式加载系统默认的配置文件
C(include THINK_PATH.’Conf/debug.php’);
// 读取应用调试配置文件
if(is_file(CONF_PATH.’debug’.CONF_EXT))
C(include CONF_PATH.’debug’.CONF_EXT);
}
}

// 读取当前应用状态对应的配置文件
if(APP_STATUS && is_file(CONF_PATH.APP_STATUS.CONF_EXT))
C(include CONF_PATH.APP_STATUS.CONF_EXT);

// 设置系统时区
date_default_timezone_set(C(‘DEFAULT_TIMEZONE’));

// 检查应用目录结构 如果不存在则自动创建
if(C(‘CHECK_APP_DIR’)) {
$module = defined(‘BIND_MODULE’) ? BIND_MODULE : C(‘DEFAULT_MODULE’);
if(!is_dir(APP_PATH.$module) || !is_dir(LOG_PATH)){
// 检测应用目录结构
Build::checkDir($module);
}
}

// 记录加载文件时间
G(‘loadTime’);
// 运行应用
App::run();

 

App对应的是Thinkphp.class.php同目录下面的App.class.php。

run方法:

可以看到run方法调用了init和exec方法。

 

init方法就是调用Dispatcher::dispatch()方法。

 

Dispatcher类在Dispatcher.class.php里面,有5个静态方法,除了dispatch方法,其他四个都是私有方法,主要是获取url参数,获取moduel,control,action这些参数.

在dispatch方法里面,调用了Route::check()方法。

Route类在Route.class.php中,是一个路由类。

 

除了check方法是公开,其他都是私有方法。

经过这些转换,终于到了App::exec()方法了。

最核心的一句,$method = new \ReflectionMethod($module,$action),如果方法是公共并且不是静态方法,检测是否有_before_方法,有的话并且是公共,执行before方法。

检测方法是否有参数,有参数invokeArgs,没有直接invoke。如果有after方法,调用after方法。如果异常或者方法调用异常,调用__call魔术方法。至此,整个运行流程完毕。