Becomin' Charles

算法 | LNMP | Flutter | Mac

Becomin' Charles

周日闲来无事,打开Yii的源代码学习一下,今天主要看的代码都是base包里面的,也就是框架根目录下的base目录里的代码。这个目录里的代码不多,但是类很多,而且这些类对整个框架来说至关重要,都是非常基本的组件。老实说,这里面的代码,我已经看了无数次了,但是总觉得常看常新,转念一想,这也实属正常,毕竟这是原作者数年框架开发经验的结晶,岂是我这等工作一两年的菜鸟能随便吃透的?

最近又玩了两次Visual Paradigm这个软件,觉得自己操作水平又有进步,甚感欣喜,所以今天既然分析源代码,就正好再练练手,于是乎,我开始用VP UML开始画base包里的类,得图如下。(说明:这个图是基于Yii的1.1.8版本,另外这里面少了两个类,因为这两个类的功能比较特殊,我没有细看,所以也就没有画在图里,看后面是不是有兴致再来专门画一张,缺失的两个类是CSecurityManager和CStatePersister。)

阅读全文 »

想学学怎么写jQuery的插件,结果发现举步维艰,于是想通过研究插件代码的案例,来积累经验,结果看到第一行,我就郁闷了,天下间最郁闷的事情莫过于你出门去约会,结果刚打开门,就摔个狗吃屎,现在我就那个感觉。看代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

//第一种“包装盒”
(function($){
// to do things here, I like use this way
})(jQuery);

//第二种“包装盒”
(function(window, undefined){
// this is the wrapper of jquery
})(window);

//第三种“包装盒”
(function($, undefined){
// this is the wrapper of jquery ui components
}(jQuery));

闭包是个好东西啊,自打初窥门径后,我干什么都喜欢在外面套个闭包,我把这个称为是打包盒,而且这个盒子很神奇,里面看得到外面,但是外面看不到里面。好处很多,比如可以放心大胆地命名变量啊,不用担心污染全局空间,也不用担心被全局空间的其他变量污染,还有像上面片段里,第一种那种写法,可以非常安全地去使用$符号代表的jQuery对象。因为在全局空间里,如果一个页面上引入多个类库的话,$符号很可能代表的不是jQuery对象,而用了第一种写法后,$符号变得很安全了。

翻开jQuery和其各类插件的源代码,你会发现,它们都被安放在一些“打包盒”里面,不禁有种英雄所见略同的感觉,但是不要感觉太好,为什么人家用的,跟我用的不太一样呢?看第二种,是jQuery用的“打包盒”。乍看跟第一种很像,但是那两个形参,简直匪夷所思。第一个是window,这明显是地球人都知道的全局变量啊,第二个是undefined,这更加神了@#$%^&。淘宝UED有篇文章解释了这个问题。文章观点大体是,将window由全局变量变为形参,可以在后期代码最小化时候,通过对局部变量名的替换,来大幅度减小代码体积,这算是一个优化吧,undefined那个同理。另一点文中提到的是,undefined可以被重新定义成自定义的值,也即可能被污染,使用这种方式,会得到真正的一个undefined。当然,我的眼光和专业程度也止于此了,但是我还是觉得,还有可能有一些额外的好处,如果看官童鞋你知道,请不吝赐教。

第三种的话,看过了两种,基本上已经有点差不多全懂了,但是仔细一看,还差那么一点的。$形参,和undefined形参不多解释了。请大家关注那个不同点,就是最后一个闭合小括号的位置,发现了吧,最后闭合小括号,为什么是放在最后面了呢?由此我也发现了一个特性,就是在小括号里面的匿名函数,如果在函数结束的大括号后面,直接跟一对小括号的话,会使那个匿名函数立刻执行。如果离开了外面那对小括号,匿名函数是不能生存的,如果是具名函数呢,在闭合大括号后,跟一对小括号,也无法实现对函数的立即调用,反倒会有语法错误。但是还是来看最关键的那个不同,如下:

1
2
3
4
5
6
7
8
9
10
11

//写法A
(function($, undefined){
// this is the wrapper of jquery ui components
}(jQuery));

//写法B
(function($, undefined){
// this is the wrapper of jquery ui components
})(jQuery); //不用找茬了,这行不一样,看“)”的位置

这两种“包装盒”到底有何不同?第一种比第二种有什么特别的好处么?

开始学习写Python,写了一两个小脚本后,就产生了非常奇怪的感觉,我怎么觉得Python看着这么混乱呢?

一个是没有大括号了,代码那种段落感没了,第二个是没有分号了,觉得一行总是没完似的。靠缩进来流程控制,觉得非常难受,总是习惯性地搞不清楚一个if和一个for的结束在什么地方,为啥不像pascal和VB那样,加个关闭之类的东西,也让人看着舒服点。代码说完,再说说文档,去官网查文档,感觉那文档视觉效果跟代码一样,没有括号,分号这样分块,断句的地方。那文档就是秘密麻麻一片,从上到下,页面长长的一串,而且还太宽,连个索引也没有,还得不停地ctrl+F来找函数。排版也忒鹾了吧?

目前都还只是视觉上的别扭吧。估计Python的粉要来嘲我了。再往后学学吧~

要给一个朋友的服务器上架设ftp,一看,这哥们的服务器装的是RHEL AS4,看到这个发行版的名字,我就发怵了,果不其然,么有包管理器,我简直寸步难行,什么软件都没法装,各种依赖,太难解决了!!

看了一下已经安装的软件列表,发现已经装了vsftpd,就用这个吧,man了半天,最后配好了,但是一连,发现死活连不上,百思不得其解,各种google,各种百度,也不知道过了多少天,才发现问题的症结在iptables,原来,要使用passive mode登陆ftp,必须开一个范围的端口,而系统默认的iptables规则,不允许对这些端口的访问,导致ftp连不上。怎么开放iptables的端口呢?又是各种google,各种百度,搜到一个iptables的tutorial,竟然有290页之厚,太难用了。

几经周折又找到了一个图形化系统界面可以配置防火墙规则的,system-config-securitylevel-tui,通过这个东西,很容易就可以开放一个端口,但是这个东西也有问题,我要开10000号段的端口100个,就麻烦了,虽然网上说,用格式形如10000-10100:tcp这样的写法可以开放一个系列的端口,但是实际上,这么写是不管事的,也不知道是不是软件的版本的问题。

最后,知道这个ui界面生成的规则被写到了/etc/sysconfig/iptables文件里面,然后又看了一下man,研究了一下如果手动配置规则的话,端口范围的写法(–dport 10000:10100),先用ui工具生成一条一个端口的规则,然后手动改文件,将端口改成范围,一重启,发现终于一切都按照预期的搞定了,真是累死我了。

在RHEL下面,启动,重启,关闭,查询iptables状态的方法:

Usage: /etc/init.d/iptables {startstoprestartcondrestartstatuspanicsave}

本文要讲的,就是如何简化从Mac登录Linux主机的操作步骤,提高效率。所谓的“免登陆”其实是不存在的,只是说,从验证密码的登录方式,改为公私钥对的登录验证方式。使用后者的方式,每次会由ssh客户端自动发送验证信息,所以就免去了人工输入密码,看起来好像“免登陆”一样。

关于这么做的原理,大家可以自己去Google,关键词是“非对称加密”,“RSA算法”,“基于ssh密钥对的自动登录”,等等,我就不多解释了。我直接说操作步骤吧:

  1. 生成密钥对
  2. 用密码登录远程主机,将公钥拷贝过去
  3. done

怎么样,无敌简单吧?

生成密钥对

执行命令 ssh-keygen -t rsa
执行结果如下:

charles@mac:~ > ssh-keygen -t rsa
Generating public/private rsa key pair.
Enter file in which to save the key (/Users/charles/.ssh/id_rsa):
Created directory ‘/Users/charles/.ssh’.
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /Users/charles/.ssh/id_rsa.
Your public key has been saved in /Users/charles/.ssh/id_rsa.pub.
The key fingerprint is:
c8:4b:85:87:90:7c:1a:67:b6:71:f5:51:0c:9d:a2:89 charles@TCMBP.local
The key’s randomart image is:
+–[ RSA 2048]—-+
… .. o=..
+.*o. …+
Bo+o. o..
…+E o

  • S
    . .
    .

+—————–+

注意:提示enter passphrase的时候,不要输入,因为你本来就想少打一次密码的,这里如果设置了用密码保护私钥,那登录的时候还是要输密码,就白做了。

做完这个步骤后,cd ~/.ssh,你就可以看到你刚才生成的密钥对,id_rsa是私钥,id_rsa.pub是公钥。下一步,就是把公钥拷贝到目标主机上。

将公钥拷贝到目标主机

用ssh登录到目标主机,然后cd /.ssh目录,如果目录不存在,那么要自己创建mkdir -p ~/.ssh。你今后要用哪个帐户登录主机,就在哪个帐户的home目录下操作,如果要免登陆root,就要去/root下操作。使用比较好,不用多想了。

有了.ssh目录后,进去,然后把id_rsa.pub传过去,可以用scp命令,这里要做的一个主要操作,就是将id_rsa.pub,的文件内容,写到一个叫authorized_keys的文件中去,如果目标主机的相应用户名下已经有了.ssh目录和authorized_keys文件,那你操作要小心一点,可能别人也做过免登陆的设置,这个时候你要小心不要把别人的设置给覆盖了。如果没有的话,就创建文件touch ~/.ssh/authorized_keys,然后执行cat id_rsa.pub >> authorized_keys,将你的公钥写入到authorized_keys中,公钥文件.pub里面只有一行信息,上面的命令相当于把那一行信息追加到authorized_keys文件最后一行。

如果.ssh目录是你主机刚刚创建的,那么可能还需要改变一下这个目录的权限,将权限放低,chmod -R 0600 ~/.ssh,到此,所有设置就算做完了,你可以退出登录,在自己的主机上试一下了,现在再敲入ssh命令后,不用密码就可以登录主机了。

PHP是支持引用的,但是关于一个变量什么时候是引用,什么时候是值,我脑子里总是迷迷糊糊的,所以,想用引用的时候,就会不自觉地在所有的地方加上&符号,真的很累赘。为了缓解这个问题,我特意写了下面这个代码片段来增进自己的理解。起初,我对这个东西的理解是,对于对象来说,PHP5就是按照引用传递的。但是我现在想,可能事情不是这么一回事情。下面的代码片段中,$obj其实是A的对象的一个引用,可以理解成一个指针,函数参数$o,其实还是对$obj的拷贝,只是拷贝的是引用本身,也即,$o其实是$obj的拷贝,只是由于其拷贝了$obj,所以,$o也指向A的对象。两个指针指向一个地方,这也是为什么函数changeObj可以改变A的对象的原因。我想,这样大概从技术来说,会更加合理一点。

1
2
3
4
5
6
7

a = 3;
$o->b = 5;
}
changeObj($obj);
var_dump($obj);

执行结果如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14

object(A)#1 (2) {
["a"]=>
int(1)
["b"]=>
int(1)
}
object(A)#1 (2) {
["a"]=>
int(3)
["b"]=>
int(5)
}

然后再看一个普通变量的引用传递的例子,这个例子里面使用的是原生的整型变量,然后函数changeInt的函数声明里,也用一般方法声明参数,普通的调用,我们发现函数不能改变$x和$y的值,这符合我们的预期,但是第二个形式的调用,就是传参数时候,带上引用标志&,结果我们发现,$x和$y的值也改变了,这是为什么呢,跟上面的例子是一个原因,函数的形式参数a和b其实是$x和$y的引用的拷贝,所以a和b也指向x和y,所以,a和b可以改变x和y的值。再看最下面的一种形式,这种是我认为的真正的按照引用传递参数。非常的绕乎对吧?我也觉得如此,确实太过灵活了。确实不利于学习和理解,代码也不容易维护,如果真的想用引用传递参数,推荐用最底下一种方式,明确告诉调用的人,这里其实是引用,千万别用changeInt(&$x, &$y);这种形式,这个代码散步在各处,将极难定位bug。

ExtJS 是我听说了很久的一个 js 框架了,但是从来就没有用过,读研期间寝室里有个小哥在用,不过那时候醉心于 WordPress 开发,也懒得理会了。现在公司里,有个上线系统用到了这个框架中的一个 TreePanel 组件,整个界面上,就正中间扔了一个 tree panel,而且不知道什么原因,CSS 还有 bug,把按钮都搞破了,弄得丑陋不堪,我于是自告奋勇,仔细调试,终于修复了那个显示上的 bug,从此算是初次结识了 ExtJS。它真正吸引我的原因是,它能够把富客户端应用的开发,从美工和基础交互中解放出来,只需要专注于业务逻辑开发即可,从而让后台工程师也能够有能力快速开发外观专业的网站应用系统。我觉得这真是功德无量的一个事情。(如果我直觉没错,Flex 框架也是这一类东西,这也是我对其有兴趣的原因,当然还没有时间尝试)

阅读全文 »

Vim 应该是现在世界上最流行的编辑器,没有之一。就算你千般百般地不喜欢它,掌握它也成了一件必须的事情了。因为日后你如果做程序员,在非 Win 系列的服务器上搞开发,Vim 绝对是无法避免的。你日常能接触到的 server,emacs 可能没装,但是 Vi 不可能没装,就这样。

特意开辟这个文章,用于收藏一些工作中常用的操作,主要有这么几个原则:

  1. 非常有用
  2. 不常用
  3. 每次用都想不起来应该怎么用

不知道大家是不是经常跟我一样有这种感受呢?我会把我遇到的这类操作,都详细写在这里。

简单的列编辑

比如:将配置文件中的指定列前面加上注释符号。

1
2
3
4
5
6
7
8
9
10
11
12
#fastcgi.conf
fastcgi_param GATEWAY_INTERFACE CGI/1.1;
fastcgi_param SERVER_SOFTWARE nginx;
fastcgi_param QUERY_STRING $query_string;
fastcgi_param REQUEST_METHOD $request_method;
fastcgi_param CONTENT_TYPE $content_type;
fastcgi_param CONTENT_LENGTH $content_length;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param SCRIPT_NAME $fastcgi_script_name;
fastcgi_param REQUEST_URI $request_uri;
fastcgi_param DOCUMENT_URI $document_uri;
fastcgi_param DOCUMENT_ROOT $document_root;

如上代码是一个很常见的配置文件,我现在要给第 2-12 行前面都加上一个 # 号,注释掉他们,在 EmEditor 里面这个事情无敌简单的,但是到了 Vi 里面,你是不是总也想不起来应该怎么弄?

1
:2,12s/^/#/g

解释一下,【2,12】在 Vi 中叫 range,看帮助的时候,如果看到 range,这就是一种写法,表明第 2 行到 12 行。
还有一种写法:

1
:s/^/#/g 11

将光标移动到第二行,然后执行上面的命令,后面的【11】代表,执行这个命令,重复11次。这里一直没解释里面那个至关重要的乱码串,那个就是替换的命令了。下次再说。

删除的时候使用 f 和 t

删除是 Vim 里面非常常用的功能,一个一个字幕删除,用 x,可以代替 del 用。整行删除,dd,应该都会了。删除一个单词,用 dw。这些都是简单提一下,我主要讲两个很好用的功能,就是 f 和 t。

f 的本意是行查找,fa,就是从光标当前位置开始,在本行中,找到第一个字母 a,和 d 结合起来以后,就非常方便了,比如:

在这个代码里,你想删掉第一个参数,可以把光标放在(后面的第一个$上,然后按”df,”,就可以把$a连同后面的逗号都删掉,非常方便。当然了,你可以发挥一下,”d2f,”这个按键序列就是把$a和$b都删掉了,但是呢,用数字这种东西,在实际操作中,我感觉大脑经常反应不过来,需要思考,不实用。

在来说一下 t,t 这个东西,跟f非常像,区别就是t把找光标放到找到的那个字符前面一个位子,比如上面那个例子,我把光标放在第一个 $ 上,然后按下 “dt)” 这个序列,可以把函数的 3 个参数都删掉,但是却不会删掉括号,非常舒服,在括号里删东西,我经常用这个功能。

惹人烦恼的^M

久用 Vim 的人,可能会遇到这样的情况,打开一个从别的环境拷贝过来的文件,发现每一行的末尾都有一个^M,非常恼人。

这个问题产生的原因,是因为三种系统的换行符定义不一样造成的。在Dos系统下,行结束符为\r\n,在linux下,行结束符为\n,在Mac下,行结束符为\r,当一个文件在一个系统上编辑,然后拷贝到另一个系统打开的时候,就会出现^M,事实上,还会出现别的恼人情况,比如一打开,发现没有任何断行,这在理论上完全可行,但是估计在Google搜索,这会是另一个问题,其实都是同一个起因。

Vim下面有两个变量 fileformat 和 fileformats,简写为 ff 和 ffs。当你发现文件没有断行或者有 ^M 的时候,你有几种选择。

在 Linux 上,看到 ^M 可以执行 dos2unix,然后将 Windows 文件的 \r\n 转换成\n,然后发现 ^M 消失了。

还可以,:set ff=win,让 linux 下的 Vim 按照 Windows 格式来解析文件。当然也可以查找替换把 ^M 给替换掉。

除此之外,还可以用ffs添加到配置文件来告诉 Vim 按照怎样的顺序来尝试适配。

比如我在 Mac 系统下,set ffs=mac,unix,dos,这样,一般情况下我打开任何系统过来的文件,都能按照正确的格式显示。

最好看的配色方案

常用Vim的人,经常会为了配色方案而纠结,仿佛每一种都不是特别好看。其实我也这么觉得的,我分析下来,原因可能是,为Vim贡献配色方案的,大都是西方人,他们的眼睛是蓝色的,感受到的对比度,饱和度,亮度,和东方人的黑眼睛看起来是不一样的。所以他们配出来的配色方案,我们东方人,怎么着都觉得不太好看。

当然,还是能找出来一些能够勉强一看的。我个人比较熟比较习惯的是自带的evening和desert,感觉多不错,而且对我用到的好几种语言,都有不错的表现。

最近,还发现了一套非常强大,经过精心设计的配色方案,叫 solarized,在圈子里影响也很大,http://ethanschoonover.com/solarized 特此推荐给大家,因为,我觉得它配色还算是相当舒服的,对比略低,但是不影响美观。

编码相关Encoding

使用:set fileencoding 可以显示文件的编码格式,简写形式是 :set fenc,使用 :set fenc=utf-8 可以转换文件的编码格式为 utf-8

使用 :set encoding 可以显示编辑器当前使用什么编码方案来展示文档,简写为enc,如果utf-8文档使用非utf-8显示,汉字会出现乱码,使用:set enc=utf-8可以将vim使用的编码方案切换的utf-8

Tab view

用一个vi编辑器,打开多个代码文件,vim从7.0版本开始,支持文件标签页,使用方法也非常简单,使用:tabnew filename来打开一个新文件,就会自动出现在新tab里面,并且在打开文件超过两个的时候,顶部出现标签控制行。

#在vim中,用新tab打开文件 :tabnew filename

#在命令行中,用标签页一次打开多个文件
$ vim -p filename1 filename2 filename3

#在vim中,各个标签页的切换
:tabn #下一个tab
或者 gt
:tabp #上一个tab
或者 gT

#搜索已经打开的tab #tabf keyword

手动加载代码高亮

比如,你使用 Vim 从头开始编辑一个 Shell 下的脚本,这个脚本是个命令,所以,一开始你没有给这个脚本设置扩展名,你会在脚本的开头使用 shabang 来指定解析器,后面用 chmod +x 后就可以执行了。

这时候,你发现写了几行代码后,代码高亮并没有生效,是因为你刚开始编辑文件的时候,文件名没有扩展,而且文件的内容全空。所以无法匹配正确的语法高亮,这个时候,你可以保存,然后重新打开文件,那也可以激活代码高亮。Vim 可以通过 shabang 来识别语法类型。

如果你不想这么麻烦,先保存关闭再打开,你可以执行命令:

1
:filetype detect

当然,如果你没有写扩展名,也不写 shabang 的话,那是没用的。

最常见的SVN客户端其实是TortoiseSVN,我一直叫它作乌龟SVN。上班以后,我在办公室使用SVN连接非办公网络的服务器,发现无法连接,原来,是因为大多数办公网络都设置了防火墙,要连接外网的服务器,必须配置代理。如果使用的是TortoiseSVN,那么“右键”-》“Settings”-》“Networking”,就可以找到设置代理的地方:

set proxy in tortoise svn

我自己经常使用的是另一款SVN客户端,不知道国内有多少用户,总之我也提一下吧,下载的网站是这里:http://www.collab.net/,这家公司是SVN的幕后支持公司,他们开发基于命令行的各种操作系统的客户端,基于命令行,所以就非常的轻巧,最关键是装好以后,可以和NetBeans无缝集成,也不会弄一堆花花绿绿的图标在资源管理器里面,让你看了红色惊叹号凭空焦虑。

给这个命令行客户端设置代理有点纠结,看不明白的童鞋,自动忽略好了。首先打开CMD,然后键入命令echo %APPDATA%,得到的结果,就是你的配置所在的根目录,进入那个目录,然后进入Subversion子目录,你会看到两个配置文件,一个叫config,一个叫servers,用写字板编辑那个servers的配置文件:

1
2
3
4
5
6
7
8

[global]
# http-proxy-exceptions = *.exception.com, www.internal-site.org
http-proxy-host = defaultproxy.whatever.com
http-proxy-port = 7000
http-proxy-username = defaultusername
http-proxy-password = defaultpassword

配置好后,就可以实现给SVN客户端挂上了代理,如果只想给特定的域挂代理,就使用另一个section来配置代理:

1
2
3
4
5
6
7
8
9
10
11
12
13
14

[groups]
group1 = *.googlecode.com
# othergroup = repository.blarggitywhoomph.com
# thirdgroup = *.example.com

### Information for the first group:
[group1]
http-proxy-host = proxy.myoffice.com
http-proxy-port = 8080
# http-proxy-username = blah
# http-proxy-password = doubleblah
# http-timeout = 60

如上只是我的配置的一个节选,是我给googlecode这个域配置了一个代理。

Yii框架自带验证器这个包,提供了一组常见各类属性的验证器。如果使用Gii代码生成工具创建model,会根据数据库字段的属性默认生成一些验证器的配置。在配置验证器的时候,很多验证器都有以后属性叫做allowEmpty,这个属性的真正逻辑却不是看上去的那个意思。

阅读全文 »