公益项目志愿者招募(PHP,UI)

我们是谁?

中国•支教联盟(CNAEF),创办于2006年4月。由自愿支持农村中小学教育的社会各界爱心人士自发组织的全国性民间公益机构。以联系和提供支教为主题,主要发布支教信息,传播贫困地区教育现状,共同关注孩子成长。自成立以来,长期致力于为发达地区爱心咨询寻找资助对象,为欠发达地区教育引入社会各界力量。

我们要做什么?

我们想继续完善一下我们的网站,如果有可能会有自己的博客和手机应用,我们都是业余的志愿者,每个人都不能在这个事情上花上很多时间,所以,我们需要你的加入。

你最好有以下一种或多种技能:

  • 能做一些简单web页面
  • 能写一些PHP代码,最好用过一些框架
  • 能写一些JavaScript脚本
  • 会用git或者想学习如何使用git
  • 能够开发简单的手机app(android/ios不限)

如果你是个大牛当然最好,如果你是个菜鸟,我们会提供全方位的技术指导。需要强调的是:我们不能提供报酬,不过欢迎各位来北京约饭。

我的联系方式

  • QQ 35650697
  • makerwang#gmail.com
  • 也可以加入我们的QQ群:249822315

相关链接

中国支教联盟官方网站:http://www.go9999.com

中国支教联盟代码仓库:https://github.com/CNAEF

关于我的支教经历请移步:https://blog.foolbird.net/4503.html

说说PHP在Web上的运行模式

在各种环境上配置过无数次运行环境之后,我们好像还是搞不清PHP到底是怎么被执行的以及在不同的环境中有什么区别,曾经还和一个架构师因为PHP到底有几种运行模式这个事儿理论过,所以今天花一点时间研究一下。

一. 常见的运行模式

在Apache中,PHP通常有以下3种运行模式:

  1. CGI
  2. apache2handler(mod_php)
  3. FastCGI

在IIS中,PHP通常有以下3种运行模式:

  1. CGI
  2. ISAPI
  3. FastCGI

在Nginx中,默认支持的模式只有 FastCGI。

二. 那么问题来了,到底什么是CGI,什么是FastCGI还有ISAPI

CGI

CGI 全称 Common Gateway Interface, 中文是“通用网关接口”,在维基百科上的说明是这样的:

CGI描述了客户端和服务器程序之间传输数据的一种标准。
但实际上这个说明是有明显歧义的,不利于我们理解CGI这个概念,百度百科上的说明是这样的:
CGI是外部应用程序(CGI程序)与Web服务器之间的接口标准,是在CGI程序和Web服务器之间传递信息的规程。
简单的说,CGI规定了Web服务器程序如何调用PHP(或者其他程序),以及PHP该如何响应服务器程序的调用。

FastCGI

解释FastCGI之前必须要了解CGI的运行原理,Web服务器每次接收到CGI请求都要去创建(fork)一个PHP进程,然后执行程序,返回结果,再销毁进程,网上把这种执行模式叫做fork-and-execute。

而这种执行模式的问题在于,每次都要消耗大量的系统资源去创建和销毁PHP进程。所以就出现了FastCGI标准,FastCGI通常是一个进程管理器,他会初始化很多PHP进程,如果服务器发来CGI请求,进程管理器会把这个请求分配到其中一个PHP进程,返回结果后并不销毁进程,而是等待下一次分配请求。

我们常用的PHP-FPM实际上是一个实现了FastCGI标准的进程管理器,另外还有PHP-CGI也是。

Apache2handler

这是我们在Linux下部署环境常用的一种模式,也是Apache默认的模式,Apache通过mod_php5模块来调用PHP。

Apache中还有一个模式的概念,一般指的是prefork,worker,event,不过这和我们这里要研究的PHP运行模式没有太大关系,其实PHP-FPM也有几种不同的工作模式,这样说下去天要黑了。

ISAPI

好吧,其实我从来没接触过ISAPI,这是微软设计的一套CGI标准,只适用于Windows平台。

在PHP源码中的sapi目录还有cliphpdbgembed,litespeed等执行模式,不过都是web之外的东西。关于这些不同的运行模式的区别与优劣,其实是个很深奥的学问,待我研究明白再和大家娓娓道来。

参考:http://segmentfault.com/q/1010000000256516

mkdir的前世今生

前几日有同事询问我Yii2中是否有封装递归创建目录的方法,我告知了他CFileHelper的用法,对这个事情我从未有过怀疑,任何成熟的框架必然会封装各种文件操作的方法,但过后我寻思良久,mkdir明明有$recursive参数,为何所有的框架都要封装创建文件的方法呢?

用过PHP4的人可能还记得,PHP4中的mkdir定义是这样的:

1
bool mkdir ( string $pathname [,int $mode = 0777 ] )

而PHP5中的定义如下:

1
bool mkdir ( string $pathname [,int $mode = 0777 [, bool$recursive = false [, resource$context ]]] )

这两个参数最早出现在2003年的php源码中,并在php5.0.0中发布,但是一直到PHP4的最后一个版本4.4.9这两个参数也没有被添加。

那么我们有了第一个答案,使用FileHelper是有兼容性方面考虑的,如果你的程序有可能在低版本的PHP中运行,需要使用外部的文件创建方法。

那么下一个问题,众所周知,Yii对PHP的版本要求是5.1.0,Yii2的版本要求是5.4.0,大多新版框架和系统都已经放弃了对PHP4的支持,那么在高版本PHP上运行的为什么要用FileHelper?

第二个答案是这样的,如果你仅仅是用来创建文件夹,那么mkdir的确更方便,但是mkdir有着几个猪一样的队友,比如rmdir和copy,他们都不支持递归,所以FileHelper更方便,他提供了内置函数外的各种强大功能,还能兼顾兼容性。

Yii2\helpers\CFileHelper 定义如下:

1
2
3
4
5
6
7
8
9
10
public static function normalizePath($path, $ds = DIRECTORY_SEPARATOR)
public static function localize($file, $language = null, $sourceLanguage = null)
public static function getMimeType($file, $magicFile = null, $checkExtension = true)
public static function getMimeTypeByExtension($file, $magicFile = null)
public static function getExtensionsByMimeType($mimeType, $magicFile = null)
public static function copyDirectory($src, $dst, $options = [])
public static function removeDirectory($dir, $options = [])
public static function findFiles($dir, $options = [])
public static function filterPath($path, $options)
public static function createDirectory($path, $mode = 0775, $recursive = true)

什么?没看出来强大?好,请看copyDirectory方法关于$option参数的说明:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
* @param array $options options for directory copy. Valid options are:
*
* - dirMode: integer, the permission to be set for newly copied directories. Defaults to 0775.
* - fileMode: integer, the permission to be set for newly copied files. Defaults to the current environment setting.
* - filter: callback, a PHP callback that is called for each directory or file.
* The signature of the callback should be: `function ($path)`, where `$path` refers the full path to be filtered.
* The callback can return one of the following values:
*
* * true: the directory or file will be copied (the "only" and "except" options will be ignored)
* * false: the directory or file will NOT be copied (the "only" and "except" options will be ignored)
* * null: the "only" and "except" options will determine whether the directory or file should be copied
*
* - only: array, list of patterns that the file paths should match if they want to be copied.
* A path matches a pattern if it contains the pattern string at its end.
* For example, '.php' matches all file paths ending with '.php'.
* Note, the '/' characters in a pattern matches both '/' and '\' in the paths.
* If a file path matches a pattern in both "only" and "except", it will NOT be copied.
* - except: array, list of patterns that the files or directories should match if they want to be excluded from being copied.
* A path matches a pattern if it contains the pattern string at its end.
* Patterns ending with '/' apply to directory paths only, and patterns not ending with '/'
* apply to file paths only. For example, '/a/b' matches all file paths ending with '/a/b';
* and '.svn/' matches directory paths ending with '.svn'. Note, the '/' characters in a pattern matches
* both '/' and '\' in the paths.
* - caseSensitive: boolean, whether patterns specified at "only" or "except" should be case sensitive. Defaults to true.
* - recursive: boolean, whether the files under the subdirectories should also be copied. Defaults to true.
* - beforeCopy: callback, a PHP callback that is called before copying each sub-directory or file.
* If the callback returns false, the copy operation for the sub-directory or file will be cancelled.
* The signature of the callback should be: `function ($from, $to)`, where `$from` is the sub-directory or
* file to be copied from, while `$to` is the copy target.
* - afterCopy: callback, a PHP callback that is called after each sub-directory or file is successfully copied.
* The signature of the callback should be: `function ($from, $to)`, where `$from` is the sub-directory or
* file copied from, while `$to` is the copy target.

@bobhero 通过另一角度分析了这个事情,他认为mkdir的这些队友如此设计是有原因的,但最终我没有找到答案。因为Python3关于文件操作的方法是这样的:

1
2
3
4
5
6
7
os.mkdir(path, mode=0o777, *, dir_fd=None)
os.mkdirs(name, mode=0o777, exist_ok=False)
os.unlink(path, *, dir_fd=None)
os.remove(path, *, dir_fd=None)
os.removedirs(name)
os.rmdir(path, *, dir_fd=None)
shutil.rmtree(path[, ignore_errors[, onerror]])

而Ruby2.2.2的文件操作是这样的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
Dir.mkdir(string [, integer])
Dir.rmdir(string)
Dir.unlink(string)
Dir.delete(string)
FileUtils.cd(dir, options)
FileUtils.cd(dir, options) {|dir| .... }
FileUtils.pwd()
FileUtils.mkdir(dir, options)
FileUtils.mkdir(list, options)
FileUtils.mkdir_p(dir, options)
FileUtils.mkdir_p(list, options)
FileUtils.rmdir(dir, options)
FileUtils.rmdir(list, options)
FileUtils.ln(old, new, options)
FileUtils.ln(list, destdir, options)
FileUtils.ln_s(old, new, options)
FileUtils.ln_s(list, destdir, options)
FileUtils.ln_sf(src, dest, options)
FileUtils.cp(src, dest, options)
FileUtils.cp(list, dir, options)
FileUtils.cp_r(src, dest, options)
FileUtils.cp_r(list, dir, options)
FileUtils.mv(src, dest, options)
FileUtils.mv(list, dir, options)
FileUtils.rm(list, options)
FileUtils.rm_r(list, options)
FileUtils.rm_rf(list, options)
FileUtils.install(src, dest, mode = <src's>, options)
FileUtils.chmod(mode, list, options)
FileUtils.chmod_R(mode, list, options)
FileUtils.chown(user, group, list, options)
FileUtils.chown_R(user, group, list, options)
FileUtils.touch(list, options)

出于什么样的考虑每个语言对此设计有这样的差异我们不得而知,但这的确引发了我不少的思考,你呢?

注:文中提到的FileHelper不仅仅指Yii的CFileHelper,泛指所有第三方文件操作库。

循环中的unset和splice

昨日为了满足运营姑凉们的排序需求,写了一段js代码进行数据筛选,却因为循环出现问题耽误了很久,事情大概是这样的:

在javascript中,没有和php中一样的unset方法,如果要释放掉数组中指定元素要使用.splice()方法。由于循环语句的机制导致如果在循环内splice掉其中的元素,循环指针可能出现蹿位,最终就是导致某些元素没有被遍历到。javascript中for in与jquery中的.each都有这个问题,php中也有splice方法,并且php的循环在使用unset和splice的时候都会出现“意外”,而foreach由于他的特殊性幸免遇难。还是看代码吧,这个很有趣的低级错误常常被我们忽略。

1
2
3
4
5
6
7
8
9
10
11
<script>
window.onload = function () {
var a = ['a','b','c','d','e','f','g'];
for(k in a){
alert(a[k]);
if (a[k] == 'c' || a[k] == 'd')
a.splice(k, 1);
}
// output is abcefg
}
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
<?php
$a = array( 'a','b','c','d','e','f','g');
foreach($a as $k=&gt;$v) {
echo $v;
if ($v == 'c' || $v == 'd') {
unset($a[$k]);
}
}
// output is abcdefg
echo "\n";
$a = array( 'a','b','c','d','e','f','g');
foreach($a as $k=&gt;$v) {
echo $v;
if ($v == 'c' || $v == 'd') {
array_splice($a, $v, 1);
}
}
// output is abcdefg
echo "\n";
$a = array( 'a','b','c','d','e','f','g');
for($i=0;$i&lt;count($a);$i++) {
echo $a[$i];
if ($a[$i] == 'c' || $a[$i] == 'd') {
unset($a[$i]);
}
}
// output is abcde
echo "\n";
$a = array( 'a','b','c','d','e','f','g');
for($i=0;$i&lt;count($a);$i++) {
echo $a[$i];
if ($a[$i] == 'c' || $a[$i] == 'd') {
unset($a[$i]);
}
}
// output is abcde
echo "\n";
$a = array( 'a','b','c','d','e','f','g');
for($i=0;$i&lt;count($a);$i++) {
echo $a[$i];
if ($a[$i] == 'c' || $a[$i] == 'd') {
array_splice($a, $i, 1);
}
}
// output is abcefg

在那遥远的地方

这是一篇迟到了很久的支教总结,刚刚结束了在广西边境一学期的支教生活,这段日子看到了太多,思考了太多,有太多的话想说,却总是不知道从何说起(王老师:这句话表现了作者写作时复杂的心理状态-_-|||)

在旅途中听同行的姑娘讲述了支教的故事,所以萌生了去支教的想法。经过一个月的联系,准备,8月末终于踏上行程,从大东北一路跑到广西边境,成为了一名支教志愿者。

武联村隶属于广西省崇左市龙州县金龙镇,几公里之外便是中越交界,武联完小有六十多个孩子,一共有五个班,一二年级合成了一个班,三年级一个班,四年级一个班,五年级一个班还有学前班,没有六年级,五年级读完便要去镇上读六年级。五个班级只有校长和主任两个人讲课,最近几年这里来了不少志愿者,这个学期只有我和肖老师两个人。

这里多数孩子都是留守儿童,家长都在外地打工,剩下孩子跟着老人或者亲属一起生活。除了留守儿童,还有一些单亲家庭和困难户,甚至还有被镇中心小学淘汰掉的顽皮学生。这些特殊的背景,也造就了这群特殊的孩子。

学校每天上三节或四节课,再加上二十分钟的早读,因为没有足够的老师,所以只开设了数学课和语文课,每周一、三、五下午还有一节“体育课”。有时候他们吵着要上音乐课,但是音乐课上很少有孩子开口跟着我唱歌。有时候他们还吵着要上美术课,然后不管我在上面讲什么,都自顾自的在自己的本子上乱涂乱画。我甚至还给他们上了两节英语课,但是除了ABCDEFG,其他的内容对他们来说显得如此的“难”。

学校每天为孩子们提供三餐,早餐是免费的,其余的要交伙食费,周一、三、五的早餐是粥,周二和周四是粉,午餐和晚餐是豆腐或者豆芽和一些肉丁,这几个月从没有见他们吃过其他的东西,也有很多孩子选择不吃,而是在校长开的超市里买一些零食。

出于各种原因,很多孩子晚上都住在学校,夏天多的时候有十几个,冬天的时候也会有几个孩子。他们在学校里吃晚餐,洗衣服,洗凉水澡,偷偷翻墙出去玩,或者跑到我们的房间来捣乱,有的时候我会给他们放一个电影,这样一整个晚上他们都会很安静。

学校的伙食不太合我和肖老师的口味,所以每周我们都要准备很多的肉和菜,每天晚上炒上两个菜,我们吃过之后再叫上孩子们来吃,孩子们总是很兴奋。如果零花钱足够多,他们晚上还会花一块钱买上一包泡面,然后跑到我的房间来要热水。

村小里的孩子成绩都很差,期中考试,四年级17个孩子,数学10个人及格,语文只有4个人及格。平时的教学管理也非常的困难,这里不像城市里的学校,有着严格的纪律,孩子们上课乱跑乱跳,吵吵闹闹,很少有孩子在认真听讲,所以总要不停的强调纪律,经常提问,以便把他们的注意力吸引过来。

写作业是一个很大的难题,通常连半数都收不上来,而且答案全是出自三四个成绩好的孩子之手。抄袭在他们看来是家常便饭,写作文的时候经常会发现好几个孩子写了一样的作文。他们在家或者在寝室几乎从来不写作业,家里人对此不闻不问,所以几乎每天我都要给他们留一节课的时间来写作业。

惩罚对这些不守规矩的小鬼们的作用很有限,他们甚至更愿意站着上课,或者更愿意站在班级后面上课,这样就能趁老师不注意跳上几下,或者在后面玩他们的小游戏。我是很反感体罚或者打骂孩子的,很生气的时候会拿书,尺子或者笤帚教训他们,但对于他们来说这显得微不足道,因为我很难真的下狠手,他们和我说他们不怕我,和我说以前的老师拿尺子打到手肿起来。

除了学习之外,女孩子对老师的态度也是一个难题,青春期的叛逆再加上她们顽皮的本性,使得老师在他们眼中变成了随时可以打,可以骂,可以侮辱,可以嘲笑,可以捉弄的敌人。男孩子还好一点,有时候他们会怕你,有时候他们愿意把你当成好朋友,有时候甚至会关心你。

这几个月我们犯下了很多的错误,而我至今觉得最大的错误,就是来这里的第一天就失去了一个老师的威严,这本是好事,却给日后的管理增加了不少的难度。另一个很严重的错误可能是自己很难做到对他们严格,总是心慈手软,当然我们都希望自己成为一个和蔼可亲的教师,但当了解了这些的孩子之后,我渐渐的变成了自己最讨厌的样子,我也开始理解为什么多数老师都是这样一幅模样。

读书的时候没能成为一个“好学生”,去教书又没能当一个好的老师,在这里我没能教会孩子们太多的东西,也没办法真正和他们成为朋友,仿佛我在他们身上看到和学到的东西远多于他们在我这学到的。他们让我深刻的体会到了家庭对于孩子成长的重要性,也让我意识到了教育的重要性。

有很多问题一直困扰着我,为什么教育部门不给这里分配足够多的老师?为什么很多村小都关闭了这里还仍然在?为什么家长们要把孩子送到这里来接受如此不正规的教育?仅仅在三公里之外,就是金龙镇中心小学,那里有足够多的老师,有宽阔的操场,有完善的教学设备,优秀的教学质量,而对于很多骑摩托接送孩子的家长,每天只要多花上几分钟的时间,对于那些住校的孩子,一周接送一次就足够了,那么这是为什么?到底谁来为孩子们的未来负责?

除了这些孩子们的顽皮和教育部门的不作为,让我感受到更多的是冷漠,教育工作者的冷漠,家长的冷漠,社会的冷漠,亲人的冷漠,正是他们的冷漠和他们的不关心造成了如今这样的景像。而我们以一己之力很难改变这一切,我们能做的,只有在黎明到来之前陪在他们的身边。

有想去支教的,请移步 http://www.go9999.com/