WordPress 再次初体验

最近,利用些许业余时间帮助朋友的创业网站搭建网站。在评估基于 Laravel 从零搭建、基于 WordPress、Drupal 等 CMS 进行搭建等多种方案后,最终选择 WordPress 这个平台。

虽然此前自己做过一段时间 Drupal 定制开发,但是我一直认为其主题开发方面比较坑,D8 在这方面有没有改进,也没有进行关注,即使改进了,也需要自己学习很多新东西。而基于 Laravel 从零搭建,管理后台的搭建就需要耗费太多的精力,如果想要实现较好的交互体验,还需要强力的前端支持,开发成本过高。鉴于朋友的网站是媒体性质的网站,WordPress 比较适合该类型的站点,其具有功能强大的管理后台、丰富的功能插件、够用内容发布流程和较为华丽的主题,最终决定选型 WordPress。

为了能够按时保质保量完成任务,这一次真心投入精力去学习去了解这个系统,阅读 Codex 文档[1],阅读 WordPress 英文电子书,编写实验代码了解插件、主题开发。也许是伴随着快速的版本迭代演进,WordPress 变得越来越强大了,底层的结构也变得比较优雅(也许是因为我以前就没有用心去看懂的缘故 -_-!!!)。

另外,伴随着对 WordPress 的一些概念的理解和实战,更正了自己此前的一些偏见,重新认识了该平台。

  • 观念一:自己写不写东西,其实和自己使用的是 WordPress、Ghost 亦或者 Jekyll 等等完全没有关系,关键在于自己想不想写,愿不愿意写。
  • 观念二:不是所有的代码都需要自己手动完成,适量投入一些金钱,购买一些优秀的插件,能够帮你节省更多的时间和金钱。
  • 观念三:不要建立太复杂的分类结构,简单的组织一些,写下去就好。以前那些花里胡哨的分类我现在都不知道是为什么创建的了,囧。

最终决定写下这篇文章,并希望以后能够经常记录一些内容,比如学习笔记、生活感悟、WordPress 新认知等。


[1]: Dash 大赞

开启daemon模式:nohup vs. screen

通常我们的服务器上一些任务,如邮件发送服务,需要以daemon模式运行。使用nohup和screen都可以实现这个目的,但它们的实现机制是不一样的。

nohup通过让子进程忽略SIGHUP信号实现。screen通过开启一个新会话实现。因为screen的父进程是初始化进程init(pid=1),所以当我们断开服务器连接时,screen中开启的任务不会收到HUP信号,还会继续运行。

下面,我们通过一个具体的例子进行演示:首先,写一个死循环脚本(loop.sh);然后,分别使用nohup和screen实现daemon模式运行;最后,使用命令:ps xafo pid,ppid,command,查看进程的实际运行情况。

loop.sh代码如下:

#!/bin/bash
while true
do
  echo `date`
  sleep 1
done

nohup

启用命令:

$ nohup bash loop.sh &

进程信息:

 6458   678  \_ sshd: vagrant [priv]
 6475  6458  |   \_ sshd: vagrant@pts/1
 6476  6475  |       \_ -bash
11185  6476  |           \_ bash loop.sh

screen

启用命令:

$ screen # <— 开启会话
$ bash loop.sh # <— 运行进程

使用 Ctrl+a d 退出会话。

进程信息:

 9051     1 SCREEN
 9052  9051  \_ /bin/bash
 9110  9052      \_ bash loop.sh
10835  9110          \_ sleep 1

参考信息

记一次磁盘清理的过程

缘起

我们测试环境中的一台Job机器磁盘空间使用率一直很高(VM 40G),最近经常遇到磁盘空间已满,导致一些任务无法正常运行,比如新项目的代码无法部署,影响正常的开发流程。几天前为赶项目进度,我们采用临时清理一些日志文件的方式,仅仅空出几G的空间,只能缓解一时,隔天又满了。总是这样就也太坑了,我决定花些时间彻底解决一下,不管采用什么办法。早晨起床后,我在Doit.im中创建了项目,并进行一些规划,列出一些需要进行的任务。

我对这台机器的职责并不熟悉,我猜测会否是该机器上部署了太多的服务导致,如果是这样,就将它们迁移出去;如果是因为机器配置太低的原因,则切换到配置更高的机器;迁移的话就需要对该机器上运行的任务进行梳理……

实战

到公司后,又听到该机器磁盘已满,又是临时的清理了些空间。我跑去测试环境的维护人员哪里,督促他解决这个问题,之后就待在那里和他一起解决。

1. 首先,确认了这台机器只是作为Job机器,没有其他的服务。那么问题在哪里呢?

2. 使用`du -sh /path`命令逐级缩小查找范围,期间也删除了一些小的日志文件,最终发现了一个16G+的`nohup.out`日志文件。

3. 终于找到罪魁祸首了,果断`rm nohup.out`删除掉它。咔,发现磁盘空间并没有被释放掉。这是什么情况?

4. 分析后发现,因为写入这个日志文件的进程还在执行导致的。那么,怎样定位这个进程呢?

5. 我首先尝试使用`ps aux | grep cmd`命令,发现没有与该目录下文件名匹配的进程;接着我尝试遍历`/proc`目录下所有进程的`fd`目录,命令:`find /proc -name fd -type d | grep nohup.out`,方法可行。

6. 这时瞄一眼同事的屏幕,神器出厂,lsof,`lsof | grep nohup.out`即可,还可以显示文件的状态,被我删除的文件后缀有`(deleted)`。

7. 定位到进程pid后,在使用`ps -p pid`查看进程的命令;`kill`关闭进程,`nohup`重启命令,日志文件重定向到`/dev/null`,磁盘空间瞬间释放了 ;-)

8. `lsof`命令的结果显示,还有很多其他的`nohup.out`文件存在,它们也将被逐个清理掉。

反思

虽然上面的过程可以在解决问题,但是这是一个折腾的过程,我们需要如何避免类似的问题?

1. 使用`nohup`启用命令时,将日志重定向到指定的目录下或者`/dev/null`;

2. 对于Job的日志,最好是在代码中调用日志接口,写入到指定目录,同时按照日期进行分割,这样方便以后的清理。

遗留问题

1. 使用`ls -al`查看`/proc`下的文件描述符(fd),发现它们是软连接(lr-x——),为什么删除掉文件后,磁盘空间没有被释放呢?(SegmentFault

PHP is_array()

同事维护的站点遇到一个SQL注入漏洞,最后发现是由于对in_array()的理解错误(一直以为是绝对匹配才会返回true),同时拼接SQL语句时也没有使用Prepared Statement导致。代码中使用in_array($var, $white_set)判断从Query String获得的变量是否符合要求,参见下面示例代码($var为一整型变量,取用时没有使用intval($var)或者(int)$var强制转换为整型)。

$white_set = array(11, 13, 16);
$var = $_GET['var'];
if (in_array($var, $white_set)) {
$query = ‘ … column = ‘ . $var . ‘ … ‘;

}

访问http://www.example.com/path/to?var=16′ and ‘x’='时,$var=16′ and ‘x’=',使用in_array($var, $white_set)判断会得到true,因为in_array的第三个参数默认为false,此时在依次比较$var和$white_set中的元素会进行类型转换,相当于使用==进行比较;如果第三个参数为true,则相当于使用===进行比较,两个变量只有完全一致才会返回true。

总结:in_array()第三个参数决定变量和数组中元素如何进行比较。值为false时(默认),相当于==;值为true时,相当于===。

ZF :: 自动加载自定义的Action Helper

项目中需要自定义Action Helper。虽然可以将Action Helper放置在自定义的类库目录下解决Autoloading问题,但是由于该类只在当前项目中使用到,因此更希望实现如下路径:

application/
  controllers/
    helpers/
      Acl.php
    IndexController.php
  Bootstrap.php

最终在StackOverflow上找到了解决办法,移除不必要的代码,具体实现如下:

类名:Application_Controller_Action_Helper_Acl

只要在Bootstrap.php中添加如下方法即可:

protected function _initActionHelpers()
{
    Zend_Controller_Action_HelperBroker::addPath(
        APPLICATION_PATH . '/controllers/helpers',
        'Application_Controller_Action_Helper_'
    );
}

如果该Action Helper可以在多个项目中使用,那么将其放置在自定义类库目录下的路径是个不错的选择,如下:

library/
  My/
    Controller/
      Action/
        Helper/
          Acl.php

类名如下:My_Controller_Action_Helper_Acl

首先,在application/configs/application.ini中添加如下配置:

autoloaderNamespaces.my = "My_"

或者

autoloaderNamespaces[] = "My_"

然后,在Bootstrap.php中添加如下方法来加载Helper类:

protected function _initActionHelpers()
{
  Zend_Controller_Action_HelperBroker::addHelper(
    new My_Controller_Action_Helper_Acl()
  );
}

以上就是两种不同的实现Action Helper自动加载的方法。起初由于对ZF的Resource Autoloading不熟悉,被StackOverflow上的解决方法迷惑了,最后发现其实实质内容都是一样的。

PS:其实Action Helper的用处我还没有搞清楚,目前只是照葫芦画瓢的说,囧!

使用PHP SimpleXML生成XML文件

使用SimpleXML生成XML文件很简单,示例代码如下:

$xml = new SimpleXMLElement('<books />');
$book = $xml->addChild('book');
$book->addChild('title', '拖延心理学');
$book->addChild('isbn', '9787300113906');
$rating = $book->addChild('rating', 5);
$rating->addAttribute('type', 'stars');
// 添加其他节点
echo $xml->asXML();

输入结果如下:

<?xml version="1.0"?>
<books><book><title>&#x62D6;&#x5EF6;&#x5FC3;&#x7406;&#x5B66;</title><isbn>9787300113906</isbn><rating type="stars">5</rating></book></books>

请注意输出的XML内容,声明中没有encoding="UTF-8",汉字也被转换为对应的UTF-8编码。

如何解决上面遇到的问题呢?

虽然SimpleXML没有提供设置encoding的接口,但是可以通过以下途径解决:

使用如下代码初始化SimpleXMLElement对象:

$xml = new SimpleXMLElement('<?xml version="1.0" encoding="utf-8"?><books />');

PS:网上有建议说在最后生成字符串时使用$xml = str_replace('<?xml version="1.0"?>', '<?xml version="1.0" encoding="UTF-8"?>', $xml);命令进行替换实现添加encoding的目的,但是这种方法无法解决汉字转为UTF-8编码的问题。