變形金剛:KO電鍍嘴炮OP

| Comments

嘴炮OP這個模具應該是電影系列十年來最好的模具,經典到一再複刻和改模,和原色雙刀、DA28一起都屬同模或改模版本。相比之下,這款模具在塗裝上勝過同模其它版本,電鍍件和銀柒使人形、車形在色彩上對比鮮明,和銀色飛翼搭配很自然。

人形正面比例協調,L級的大小更突顯霸氣。

受G1動畫的影響,我覺得樸實無華的平頭卡車更能顯現OP的威嚴,電影版的狗頭卡車多少有點浮誇。不過從還原電影的角度來說,這款模具的車形還是很不錯的。尤其是兩部分電鍍件,遮光闆和進氣面闆,非常閃亮。白色描邊的火焰紋的刻畫也很到位。

這款模具之所以叫嘴炮OP,就是因為有嘴有炮。一般OP的模具的頭雕都是帶口罩的,而這款是露嘴的。其實我更喜歡帶口罩的形象,機器人要嘴沒什麼用,尤其像大黃蜂那種嗪奶頭的嘴,見一次吐一次。

L級的頭雕還是很精細的,細節的刻畫和塗裝都很到位,隻是這長了腮的OP是要配合天火馬甲搞個海陸空三棲麼。

炮是由兩個油箱組合變形而成,雖然炮形比較奇葩,但是構思確實很新穎。

背包不算大,不過還是過於突兀,尤其是這交叉的造型個人覺得很難看。

模具本身的缺陷很少,除了背包問題,吹毛求疵地有以下幾點:

  • 腰部的兩個輪子耷拉着太難看,完全可以做成翻折上去。
  • 車形狀态下的進氣面闆,作爲最大的電鍍件,變成人形後居然成了鞋底,很容易刮傷。
  • 手臂上的兩塊折闆太礙事。
  • 手是不可動手。
  • KO版的普遍問題:左遮光闆卡不緊,胸甲滑動不流暢。

L級模具的噱頭一般是聲光效果。這款模具有三個,一是人形狀态下,扳下腹部機關,胸甲聳起的同時,胸部和眼睛會亮,OP喊:“I am Optimus Prime !”(其實不如喊那句最經典的:“Autobots, transform and roll out !”,向G1緻敬會讓很多人喪失抵抗力……);第二個是人形變車形時,上半身和下半身分離時,發出叽叽咔叽的變形音效;第三個是在車形狀态下,扳動車頭背部的開關,發出機槍掃射的聲音。平心而論,這款模具的聲光效果設計得還是不錯的,人形、變形、車形三種狀态都有照顧,不像變3的L級大黃蜂,所有聲光效果都在那個巨大無比的雞肋背包上。

總之,這款KO雙刀電鍍嘴炮OP在模具、塗裝、做工、用料等各方面的表現都不錯,非神物而神物,壓箱底還是可以勝任的。

PHP擴展框架的創建

| Comments

創建項目

在PHP源碼目錄下的ext目錄下執行:

1
./ext_skel --extname=foobar

修改foobar/config.m4,移除以下三行前的dnl:

dnl PHP_ARG_WITH(foobar, for foobar support,
dnl Make sure that the comment is aligned:
dnl [ –with-foobar Include foobar support])

編譯與安裝

在foobar目錄下執行以下命令,生成configure腳本:

1
/usr/bin/phpize

執行configure:

1
./configure --with-php-config=/usr/bin/php-config

編譯安裝:

1
sudo make install

修改php.ini,啟用擴展:

1
extension=foobar.so

PHP流的上下文和過濾器的實現

| Comments

上下文的實現和應用

上下文包含流的選項和流的參數兩部分內容。

1
php_stream_context *php_stream_context_alloc(void);

流的選項是一系列鍵值對。

1
2
3
int php_stream_context_set_option(php_stream_context *context, const char *wrappername, const char *optionname, zval *optionvalue);

int php_stream_context_get_option(php_stream_context *context, const char *wrappername, const char *optionname, zval ***optionvalue);

流的參數目前只實現對流的事件的回調,php_stream_context->notifier存儲如下結構:

1
2
3
4
5
6
7
typedef struct {
    php_stream_notification_func func;
    void (*dtor)(php_stream_notifier *notifier);
    void *ptr;
    int mask;
    size_t progress, progress_max;
} php_stream_notifier;

回調函數的原型為:

1
2
3
4
5
typedef void (*php_stream_notification_func)(php_stream_context *context,
      int notifycode, int severity,
      char *xmsg, int xcode,
      size_t bytes_sofar, size_t bytes_max,
      void * ptr TSRMLS_DC);

notifycode包含如下:

  • PHP_STREAM_NOTIFY_RESOLVE:主機名解析完成
  • PHP_STREAM_NOTIFY_CONNECT:socket連接建立
  • PHP_STREAM_NOTIFY_AUTH_REQUIRED:需要驗證
  • PHP_STREAM_NOTIFY_MIME_TYPE_IS:遠程資源的MIME-Type可用
  • PHP_STREAM_NOTIFY_FILE_SIZE_IS:遠程資源的大小可用
  • PHP_STREAM_NOTIFY_REDIRECTED:原始地址被跳轉
  • PHP_STREAM_NOTIFY_PROGRESS:php_stream_notifier->progress和progress_max(可能的)已更新
  • PHP_STREAM_NOTIFY_COMPLETED:流中已無可用數據
  • PHP_STREAM_NOTIFY_FAILURE:請求失敗
  • PHP_STREAM_NOTIFY_AUTH_RESULT:遠程驗證已完成,並且可能是成功的

severity包含如下:

  • PHP_STREAM_NOTIFY_SEVERITY_INFO
  • PHP_STREAM_NOTIFY_SEVERITY_WARN
  • PHP_STREAM_NOTIFY_SEVERITY_ERR

php_stream_notifier->ptr用於存儲附加數據,如果該數據需要手工回收,需指定php_stream_notifier->dtor。

php_stream_notifier->mask如果被賦值severity,其它severity的事件將不會觸發回調函數。

過濾器的實現和應用

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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
#include "ext/standard/php_string.h"

typedef struct {
  char is_persistent;
  char *from_chars;
  char *to_chars;
  int tr_len;
} php_donie_filter_data;

static php_stream_filter_status_t php_donie_stream_filter(
  php_stream *stream, php_stream_filter *thisfilter,
  php_stream_bucket_brigade *buckets_in,
  php_stream_bucket_brigade *buckets_out,
  size_t *bytes_consumed, int flags TSRMLS_DC
) {
  php_donie_filter_data *data = thisfilter->abstract;
  php_stream_bucket *bucket;
  size_t consumed = 0;

  while(buckets_in->head) {
      bucket = php_stream_bucket_make_writeable(buckets_in->head TSRMLS_CC);
      php_strtr(bucket->buf, bucket->buflen, data->from_chars, data->to_chars, data->tr_len);
      consumed += bucket->buflen;
      php_stream_bucket_append(buckets_out, bucket TSRMLS_CC);
  }

  if (bytes_consumed) {
      *bytes_consumed = consumed;
  }

  return PSFS_PASS_ON;
}

static void php_donie_stream_filter_dtor(
  php_stream_filter *thisfilter TSRMLS_DC
) {
  php_donie_filter_data *data = thisfilter->abstract;
  pefree(data, data->is_persistent);
}

static php_stream_filter_ops php_donie_stream_filter_ops = {
  php_donie_stream_filter,
  php_donie_stream_filter_dtor,
  "donie.to_upper_case"
};

static php_stream_filter *php_donie_stream_filter_create(
  const char *name, zval *param, int persistent TSRMLS_DC
) {
  php_donie_filter_data *data;

  data = pemalloc(sizeof(php_donie_filter_data), persistent);
  if (!data) {
      return NULL;
  }
  data->is_persistent = persistent;
  data->from_chars = "abcdefghijklmnopqrstuvwxyz";
  data->to_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
  data->tr_len = strlen(data->from_chars);

  return php_stream_filter_alloc(&php_donie_stream_filter_ops, data, persistent);
}

static php_stream_filter_factory php_donie_stream_uppercase_factory = {
  php_donie_stream_filter_create
};

PHP_MINIT_FUNCTION(donie)
{
  /* register a filter */
  php_stream_filter_register_factory("donie.to_upper_case", &php_donie_stream_uppercase_factory TSRMLS_CC);

  return SUCCESS;
}

PHP_MSHUTDOWN_FUNCTION(donie)
{
  /* unregister the filter */
  php_stream_filter_unregister_factory("donie.to_upper_case" TSRMLS_CC);

  return SUCCESS;
}

註冊和註銷

分別在MINIT和MSHUTDOWN函數中調用php_stream_filter_register_factory()和php_stream_filter_unregister_factory()註冊和註銷過濾器。

過濾器的執行過程

當過濾器被調用時,調用方將使用php_donie_stream_filter_create()函數創建過濾器的實例。該函數在被執行時初始化過濾器的相關數據,並指定過濾器的相關操作。

調用方然後將過濾器實例添加到對應的流的讀過濾器鏈或寫過濾器鏈中,當流中發生讀或寫的操作時,過濾器鏈將數據放到一或多個php_stream_bucket結構中,並傳遞給過濾器處理。

業務邏輯

結構體php_donie_stream_filter_ops指定了過濾器的名稱和相關業務邏輯。php_donie_stream_filter_ops->php_donie_stream_filter_dtor是過濾器的析構函數。php_donie_stream_filter_ops->php_donie_stream_filter是過濾器的主要業務邏輯。

在php_donie_stream_filter()中,函數接收一個php_stream_bucket鏈表buckets_in,並將處理後的php_stream_bucket追加到鏈表buckets_out中輸出。

php_stream_bucket_make_writeable()將一個bucket從鏈表中移除,如果必要,複製其內部緩衝數據,以獲得對內容的寫權限。此外,對bucket的相關操作還有:

1
2
3
4
5
6
7
8
9
10
11
php_stream_bucket *php_stream_bucket_new(php_stream *stream, char *buf, size_t buflen, int own_buf, int buf_persistent TSRMLS_DC);

int php_stream_bucket_split(php_stream_bucket *in, php_stream_bucket **left, php_stream_bucket **right, size_t length TSRMLS_DC);

void php_stream_bucket_delref(php_stream_bucket *bucket TSRMLS_DC);
void php_stream_bucket_addref(php_stream_bucket *bucket);

void php_stream_bucket_prepend(php_stream_bucket_brigade *brigade, php_stream_bucket *bucket TSRMLS_DC);
void php_stream_bucket_append(php_stream_bucket_brigade *brigade, php_stream_bucket *bucket TSRMLS_DC);

void php_stream_bucket_unlink(php_stream_bucket *bucket TSRMLS_DC);

若過濾器已準備好輸出的數據,返回PSFS_PASS_ON;若還需要更多數據才能結束過濾操作,返回PSFS_FEED_ME;若遇到嚴重問題,返回PSFS_ERR_FATAL。

使用gn操作增強Vim的搜索

| Comments

gn是Vim 7.4新增的一個操作(motion),作用是跳到並選中下一個搜索匹配項。

具體說,Vim裡執行搜索後,執行n操作只會跳轉到下一個匹配項,而不選中它。但是我們往往需要對匹配項執行一些修改操作,例如替換、刪除或修改大小寫等,如果先跳轉再執行對應操作,會比較繁瑣。gn可以和相應的操作結合,簡化這些過程。

舉個栗子。如果要把所有的win、linux替換成大寫,可以先用正則搜索“\(win\|linux\)\C”,然後執行“gUgn”,此時下一個匹配的結果就會被替換成大寫,然後直接執行“.”重複上次操作即可。

此外,常用的組合有:

  • cgn: 刪除下一個匹配項,並進入插入模式。
  • dgn: 刪除下一個匹配項,並保持常規模式。

和操作“N”相同,執行“gN”是逆向操作。

在i3wm狀態欄顯示股票信息

| Comments

效果如圖:

創建腳本,並賦可執行權限:

~/.i3/myi3status.sh
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#!/bin/sh
# shell script to prepend i3status with more stuff

STOCK_SCRIPT=`realpath "$0"|xargs dirname`/stock.php

i3status | while :
do
    stock_info=""
    if [[ -x "$STOCK_SCRIPT" ]]; then
        stock_info=`$STOCK_SCRIPT`
    fi

    read line

    # if output_format = i3bar in i3status.conf
    stock_info="[{ \"full_text\": \"${stock_info}\" },"
    echo "${line/[/$stock_info}" || exit 1

    # # if not output_format = i3bar in i3status.conf
    # echo "$stock_info | $line" || exit 1
done

如果i3status.conf中啟用了JSON格式輸出(支持顏色),應啟用上面腳本中第一塊的代碼,否則使用後面的。啟動JSON格式輸出的內容具體如下:

~/.i3status.conf
1
2
3
4
general {
    colors = true
    output_format = i3bar
}

在i3wm的配置文件中用以上腳本替換i3status:

~/.i3/config
1
2
3
4
5
6
7
8
9
10
bar {

    # ...

    # status_command i3status
    status_command ~/.i3/myi3status.sh

    # ...

}

在和上面腳本同路徑下創建腳本:

~/.i3/stock.php
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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
#!/bin/env php
<?php
class PinYin
{
    public static function utf8To($ss, $isfirst = false)
    {
        return self::convert(self::utf8ToGB2312($ss), $isfirst);
    }

    public static function utf8ToGB2312($ss)
    {
        return iconv('UTF-8', 'GB2312//IGNORE', $ss);
    }

    // 字符串必须为GB2312编码
    public static function convert($ss, $isfirst = false)
    {
        $res = '';
        $len = strlen($ss);
        $pinyinArr = self::get_pinyin_array();
        for ($i=0; $i<$len; $i++) {
            $ascii = ord($ss[$i]);
            if ($ascii > 0x80) {
                $asciiB = ord($ss[++$i]);
                $ascii = $ascii * 256 + $asciiB - 65536;
            }

            if ($ascii < 255 && $ascii > 0) {
                if (($ascii >= 48 && $ascii <= 57) || ($ascii >= 97 && $ascii <= 122)) {
                    $res .= $ss[$i]; // 0-9 a-z
                } elseif ($ascii >= 65 && $ascii <= 90) {
                    $res .= strtolower($ss[$i]); // A-Z
                } else {
                    $res .= '_';
                }
            } elseif ($ascii < -20319 || $ascii > -10247) {
                $res .= '_';
            } else {
                foreach ($pinyinArr as $py=>$asc) {
                    if ($asc <= $ascii) {
                        $res .= $isfirst ? $py[0] : $py;
                        break;
                    }
                }
            }
        }
        return $res;
    }

    public static function toFirst($ss)
    {
        $ascii = ord($ss[0]);
        if ($ascii > 0xE0) {
            $ss = self::utf8ToGB2312($ss[0].$ss[1].$ss[2]);
        } elseif ($ascii < 0x80) {
            if ($ascii >= 65 && $ascii <= 90) {
                return strtolower($ss[0]);
            } elseif ($ascii >= 97 && $ascii <= 122) {
                return $ss[0];
            } else {
                return false;
            }
        }

        if (strlen($ss) < 2) {
            return false;
        }

        $asc = ord($ss[0]) * 256 + ord($ss[1]) - 65536;

        if($asc>=-20319 && $asc<=-20284) return 'a';
        if($asc>=-20283 && $asc<=-19776) return 'b';
        if($asc>=-19775 && $asc<=-19219) return 'c';
        if($asc>=-19218 && $asc<=-18711) return 'd';
        if($asc>=-18710 && $asc<=-18527) return 'e';
        if($asc>=-18526 && $asc<=-18240) return 'f';
        if($asc>=-18239 && $asc<=-17923) return 'g';
        if($asc>=-17922 && $asc<=-17418) return 'h';
        if($asc>=-17417 && $asc<=-16475) return 'j';
        if($asc>=-16474 && $asc<=-16213) return 'k';
        if($asc>=-16212 && $asc<=-15641) return 'l';
        if($asc>=-15640 && $asc<=-15166) return 'm';
        if($asc>=-15165 && $asc<=-14923) return 'n';
        if($asc>=-14922 && $asc<=-14915) return 'o';
        if($asc>=-14914 && $asc<=-14631) return 'p';
        if($asc>=-14630 && $asc<=-14150) return 'q';
        if($asc>=-14149 && $asc<=-14091) return 'r';
        if($asc>=-14090 && $asc<=-13319) return 's';
        if($asc>=-13318 && $asc<=-12839) return 't';
        if($asc>=-12838 && $asc<=-12557) return 'w';
        if($asc>=-12556 && $asc<=-11848) return 'x';
        if($asc>=-11847 && $asc<=-11056) return 'y';
        if($asc>=-11055 && $asc<=-10247) return 'z';
        return false;
    }

    public static function get_pinyin_array()
    {
        static $pyArr;
        if(isset($pyArr)) return $pyArr;

        $kk = 'a|ai|an|ang|ao|ba|bai|ban|bang|bao|bei|ben|beng|bi|bian|biao|bie|bin|bing|bo|bu|ca|cai|can|cang|cao|ce|ceng|cha|chai|chan|chang|chao|che|chen|cheng|chi|chong|chou|chu|chuai|chuan|chuang|chui|chun|chuo|ci|cong|cou|cu|cuan|cui|cun|cuo|da|dai|dan|dang|dao|de|deng|di|dian|diao|die|ding|diu|dong|dou|du|duan|dui|dun|duo|e|en|er|fa|fan|fang|fei|fen|feng|fo|fou|fu|ga|gai|gan|gang|gao|ge|gei|gen|geng|gong|gou|gu|gua|guai|guan|guang|gui|gun|guo|ha|hai|han|hang|hao|he|hei|hen|heng|hong|hou|hu|hua|huai|huan|huang|hui|hun|huo|ji|jia|jian|jiang|jiao|jie|jin|jing|jiong|jiu|ju|juan|jue|jun|ka|kai|kan|kang|kao|ke|ken|keng|kong|kou|ku|kua|kuai|kuan|kuang|kui|kun|kuo|la|lai|lan|lang|lao|le|lei|leng|li|lia|lian|liang|liao|lie|lin|ling|liu|long|lou|lu|lv|luan|lue|lun|luo|ma|mai|man|mang|mao|me|mei|men|meng|mi|mian|miao|mie|min|ming|miu|mo|mou|mu|na|nai|nan|nang|nao|ne|nei|nen|neng|ni|nian|niang|niao|nie|nin|ning|niu|nong|nu|nv|nuan|nue|nuo|o|ou|pa|pai|pan|pang|pao|pei|pen|peng|pi|pian|piao|pie|pin|ping|po|pu|qi|qia|qian|qiang|qiao|qie|qin|qing|qiong|qiu|qu|quan|que|qun|ran|rang|rao|re|ren|reng|ri|rong|rou|ru|ruan|rui|run|ruo|sa|sai|san|sang|sao|se|sen|seng|sha|shai|shan|shang|shao|she|shen|sheng|shi|shou|shu|shua|shuai|shuan|shuang|shui|shun|shuo|si|song|sou|su|suan|sui|sun|suo|ta|tai|tan|tang|tao|te|teng|ti|tian|tiao|tie|ting|tong|tou|tu|tuan|tui|tun|tuo|wa|wai|wan|wang|wei|wen|weng|wo|wu|xi|xia|xian|xiang|xiao|xie|xin|xing|xiong|xiu|xu|xuan|xue|xun|ya|yan|yang|yao|ye|yi|yin|ying|yo|yong|you|yu|yuan|yue|yun|za|zai|zan|zang|zao|ze|zei|zen|zeng|zha|zhai|zhan|zhang|zhao|zhe|zhen|zheng|zhi|zhong|zhou|zhu|zhua|zhuai|zhuan|zhuang|zhui|zhun|zhuo|zi|zong|zou|zu|zuan|zui|zun|zuo';
        $vv = '-20319|-20317|-20304|-20295|-20292|-20283|-20265|-20257|-20242|-20230|-20051|-20036|-20032|-20026|-20002|-19990|-19986|-19982|-19976|-19805|-19784|-19775|-19774|-19763|-19756|-19751|-19746|-19741|-19739|-19728|-19725|-19715|-19540|-19531|-19525|-19515|-19500|-19484|-19479|-19467|-19289|-19288|-19281|-19275|-19270|-19263|-19261|-19249|-19243|-19242|-19238|-19235|-19227|-19224|-19218|-19212|-19038|-19023|-19018|-19006|-19003|-18996|-18977|-18961|-18952|-18783|-18774|-18773|-18763|-18756|-18741|-18735|-18731|-18722|-18710|-18697|-18696|-18526|-18518|-18501|-18490|-18478|-18463|-18448|-18447|-18446|-18239|-18237|-18231|-18220|-18211|-18201|-18184|-18183|-18181|-18012|-17997|-17988|-17970|-17964|-17961|-17950|-17947|-17931|-17928|-17922|-17759|-17752|-17733|-17730|-17721|-17703|-17701|-17697|-17692|-17683|-17676|-17496|-17487|-17482|-17468|-17454|-17433|-17427|-17417|-17202|-17185|-16983|-16970|-16942|-16915|-16733|-16708|-16706|-16689|-16664|-16657|-16647|-16474|-16470|-16465|-16459|-16452|-16448|-16433|-16429|-16427|-16423|-16419|-16412|-16407|-16403|-16401|-16393|-16220|-16216|-16212|-16205|-16202|-16187|-16180|-16171|-16169|-16158|-16155|-15959|-15958|-15944|-15933|-15920|-15915|-15903|-15889|-15878|-15707|-15701|-15681|-15667|-15661|-15659|-15652|-15640|-15631|-15625|-15454|-15448|-15436|-15435|-15419|-15416|-15408|-15394|-15385|-15377|-15375|-15369|-15363|-15362|-15183|-15180|-15165|-15158|-15153|-15150|-15149|-15144|-15143|-15141|-15140|-15139|-15128|-15121|-15119|-15117|-15110|-15109|-14941|-14937|-14933|-14930|-14929|-14928|-14926|-14922|-14921|-14914|-14908|-14902|-14894|-14889|-14882|-14873|-14871|-14857|-14678|-14674|-14670|-14668|-14663|-14654|-14645|-14630|-14594|-14429|-14407|-14399|-14384|-14379|-14368|-14355|-14353|-14345|-14170|-14159|-14151|-14149|-14145|-14140|-14137|-14135|-14125|-14123|-14122|-14112|-14109|-14099|-14097|-14094|-14092|-14090|-14087|-14083|-13917|-13914|-13910|-13907|-13906|-13905|-13896|-13894|-13878|-13870|-13859|-13847|-13831|-13658|-13611|-13601|-13406|-13404|-13400|-13398|-13395|-13391|-13387|-13383|-13367|-13359|-13356|-13343|-13340|-13329|-13326|-13318|-13147|-13138|-13120|-13107|-13096|-13095|-13091|-13076|-13068|-13063|-13060|-12888|-12875|-12871|-12860|-12858|-12852|-12849|-12838|-12831|-12829|-12812|-12802|-12607|-12597|-12594|-12585|-12556|-12359|-12346|-12320|-12300|-12120|-12099|-12089|-12074|-12067|-12058|-12039|-11867|-11861|-11847|-11831|-11798|-11781|-11604|-11589|-11536|-11358|-11340|-11339|-11324|-11303|-11097|-11077|-11067|-11055|-11052|-11045|-11041|-11038|-11024|-11020|-11019|-11018|-11014|-10838|-10832|-10815|-10800|-10790|-10780|-10764|-10587|-10544|-10533|-10519|-10331|-10329|-10328|-10322|-10315|-10309|-10307|-10296|-10281|-10274|-10270|-10262|-10260|-10256|-10254';
        $key = explode('|', $kk);
        $val = explode('|', $vv);
        $pyArr = array_combine($key, $val);
        arsort($pyArr);

        return $pyArr;
    }
}

define('STOCKS_FILE', $_SERVER['HOME'].'/.stocks');

if (!file_exists(STOCKS_FILE)) {
    exit('File not found: '.STOCKS_FILE);
}
$stockCodeArr = explode("\n", trim(file_get_contents(STOCKS_FILE)));
if (empty($stockCodeArr)) {
    exit('No stock code found.');
}

$context = stream_context_create(
    [
        'http'=>[
            'method'=>"GET",
            'timeout' => 3
        ]
    ]
);
$response = file_get_contents("http://hq.sinajs.cn/list=".implode(',', $stockCodeArr), false, $context);
if (empty($response)) {
    exit('Failed fetching stock info with API.');
}

$result = [];
$lines = explode("\n", trim(iconv('gbk', 'utf-8', $response)));
foreach ($lines as $line) {
    $matches = [];
    preg_match('/".*"/', $line, $matches);
    if (!empty($matches)) {
        $stock = explode(',', trim($matches[0], '"'));
        if (!empty($stock)) {
            $result[] = strtoupper(PinYin::utf8To($stock[0], true)).": {$stock[3]}, {$stock[4]}, {$stock[5]}";
        }
    }
}
echo implode('; ', $result);

腳本從~/.stocks中讀取股票代碼:

~/.stocks
1
2
sh601985
sz002024

PHP流的操作

| Comments

實現

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/* reimplement fopen using stream */
ZEND_FUNCTION(donie_stream_fopen)
{
  php_stream *stream;
  char *path, *mode;
  int path_len, mode_len;
  int options = ENFORCE_SAFE_MODE|REPORT_ERRORS;

  if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &path, &path_len, &mode, &mode_len) == FAILURE)
  {
      return;
  }

  stream = php_stream_open_wrapper(path, mode, options, NULL);
  if (!stream)
  {
      RETURN_FALSE;
  }

  php_stream_to_zval(stream, return_value);
}

php_stream_open_wrapper()是對文件類型資源創建流的方法,此外還有基於socket的流、目錄流和特殊流三種。php_stream_to_zval()用於把流實例轉換成zval結構。

創建文件類型的流

1
2
#define php_stream_open_wrapper(path, mode, options, opened)   _php_stream_open_wrapper_ex((path), (mode), (options), (opened), NULL STREAMS_CC TSRMLS_CC)
#define php_stream_open_wrapper_ex(path, mode, options, opened, context) _php_stream_open_wrapper_ex((path), (mode), (options), (opened), (context) STREAMS_CC TSRMLS_CC)

參數path是文件名或URL,mode是模式字符串,options是選項組合。php_stream_open_wrapper_ex()允許指定一個流的上下文。

options包含以下選項:

  • USE_PATH:應用ini中的include_path到相對路徑。內建的fopen()的第三個參數置True時使用此選項。
  • STREAM_USE_URL:只有遠程URL才允許打開,%%file://, php://, compress.zlib://%%這樣的本地URL會報錯。
  • ENFORCE_SAFE_MODE:只有設置了此選項且ini中的safe_mode開啟時,才會使safe_mode生效,不設置此選項,則不論ini中是否開啟都不會生效。
  • REPORT_ERRORS:若開啟流出錯,生成錯誤信息。
  • STREAM_MUST_SEEK:不是所有流都允許seek,若置此選項,且流不允許seek,則包裝器不會開啟流。
  • STREAM_WILL_CAST:置此參數將要求流可被轉換成posix或stdio類型的文件描述符,若流不可轉換,可在IO開始前失敗。
  • STREAM_ONLY_GET_HEADERS:http包裝器使用此參數,只獲取資源的元數據,不獲取內容。
  • STREAM_DISABLE_OPEN_BASEDIR:當ini中的open_basedir開啟時,置此參數跳過open_basedir檢查。
  • STREAM_OPEN_PERSISTENT:要求流和相關資源都創建為持久數據。
  • IGNORE_PATH:不從include_path中搜索。
  • IGNORE_URL:只有本地文件才可以被打開。

創建傳輸類型的流

1
php_stream *_php_stream_xport_create(const char *name, size_t namelen, int options, int flags, const char *persistent_id, struct timeval *timeout, php_stream_context *context, char **error_string, int *error_code)

參數:

  • name:URL。
  • options:參數,與php_stream_open_wrapper()的相同。
  • flags:STREAM_XPORT_CLIENT或STREAM_XPORT_SERVER與其它STREAM_XPORT_*常量的組合。
  • persistent_id:鍵值,置此參數將使流在多次請求間持久存在。
  • timeout:置NULL將使用ini中設置的值。
  • errstr:用於向外傳遞錯誤信息,初始應置為NULL,若有錯誤信息傳出,調用方有責任釋放錯誤信息佔用的內存。
  • errcode:錯誤碼。

flags:

  • STREAM_XPORT_CLIENT:工作為客戶端,向遠程發起連接。
  • STREAM_XPORT_SERVER:工作為服務器,接受連接。
  • STREAM_XPORT_CONNECT:傳輸建立的同時發起對遠程的連接,否則,需手動調用php_stream_xport_connect()。
  • STREAM_XPORT_CONNECT_ASYNC:發起異步遠程連接。
  • STREAM_XPORT_BIND:将传输流绑定到本地资源. 用在服务端传输流时,这将使得accept连接的传输流准备端口, 路径或特定的端点标识符等信息。
  • STREAM_XPORT_LISTEN:%%Listen for inbound connections on the bound transport endpoint. This is typically used with stream-based transports such as tcp://, ssl://, and unix://%%.

創建目錄類型的流

1
php_stream php_stream_opendir(const char *path, int options, php_stream_context *context)

創建特殊類型的流

1
2
3
4
5
php_stream *php_stream_fopen_tmpfile(void);
php_stream *php_stream_fopen_temporary_file(const char *dir, const char *pfx, char **opened_path);
php_stream *php_stream_fopen_from_fd(int fd, const char *mode, const char *persistent_id);
php_stream *php_stream_fopen_from_file(FILE *file, const char *mode);
php_stream *php_stream_fopen_from_pipe(FILE *file, const char *mode);

讀流

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 讀一個字符
int php_stream_getc(php_stream *stream);

// 讀取指定數量的字符
size_t php_stream_read(php_stream *stream, char *buf, size_t count);

// 讀取直到行末、或流末、或最多maxlen個字符
char *php_stream_get_line(php_stream *stream, char *buf, size_t maxlen, size_t *returned_len);
char *php_stream_gets(php_stream *stream, char *buf, size_t maxlen);

// 與php_stream_get_line相同,可指定截止標記
char *php_stream_get_record(php_stream *stream, size_t maxlen, size_t *returned_len, char *delim, size_t delim_len TSRMLS_DC);

// 讀取一個目錄項
php_stream_dirent *php_stream_readdir(php_stream *dirstream, php_stream_dirent *entry);

寫流

1
2
3
4
5
6
7
8
9
// 寫非阻塞流可能寫入的數據比傳入的短;_string要求傳入的字符串以NULL結尾
size_t php_stream_write(php_stream *stream, char *buf, size_t count);
size_t php_stream_write_string(php_stream *stream, char *stf);

int php_stream_putc(php_stream *stream, int c);
// 與_string不同的是會自動追加一個換行符到字符串末尾
int php_stream_puts(php_string *stream, char *buf);

size_t php_stream_printf(php_stream *stream TSRMLS_DC, const char *format, ...);
1
int php_stream_flush(php_stream *stream);

在關閉流的時候,flush會被自動調用,並且大部分無過濾的流因無內部緩沖而不需flush,所以單獨flush一般是不需要的。

尋址

1
2
3
4
int php_stream_seek(php_stream *stream, off_t offset, int whence);
int php_stream_rewind(php_stream *stream);
int php_stream_rewinddir(php_stream *dirstream);
off_t php_stream_tell(php_stream *stream);

offset是相對於whence的位移量,whence包含:

  • SEEK_SET:文件開頭。置offet為負值被認為是個錯誤並導致不可預料的行為。offset超出文件範圍會導致一個錯誤,或文件被增大。
  • SEEK_CUR:當前位置。
  • SEEK_END:文件末尾。offset一般為負,正值的行為因流的實現而異。

獲取額外信息

1
int php_stream_stat(php_stream *stream, php_stream_statbuf *ssb);

關閉流

1
2
#define php_stream_close(stream) php_stream_free((stream), PHP_STREAM_FREE_CLOSE)
#define php_stream_pclose(stream) php_stream_free((stream), PHP_STREAM_FREE_CLOSE_PERSISTENT)

包含以下選項:

  • PHP_STREAM_FREE_CALL_DTOR:銷毀流時調用php_stream->ops->close
  • PHP_STREAM_FREE_RELEASE_STREAM:銷毀流時調用php_stream_wrapper->ops->stream_close
  • PHP_STREAM_FREE_PRESERVE_HANDLE:php_stream->ops->close不銷毀句柄
  • PHP_STREAM_FREE_RSRC_DTOR:用於流內部資源列表垃圾回收
  • PHP_STREAM_FREE_PERSISTENT:用於持久流,所有操作的結果在多次請求間持久有效
  • PHP_STREAM_FREE_CLOSE:CALL_DTOR和RELEASE_STREAM的組合,用於非持久流的常規選項
  • PHP_STREAM_FREE_CLOSE_CASTED:CLOSE和PRESERVE_HANDLE的組合
  • PHP_STREAM_FREE_CLOSE_PERSISTENT:CLOSE和PERSISTENT的組合,用於持久流的常規選項

變形金剛:祖國合金放大版探長

| Comments

探長可以和平頭OP並列變四最好的模具,人形和車形俱佳。但是官版的V級神物實在雞肋,所以祖國版准L級模具實在是拯救巨大控的好物。除了放大,探長的整個車頭都是合金件,既有質感又有份量。涂裝較官版也有改進,胸甲和輪軲上了銀柒,槍管上了金柒,車形後部增加了火焰紋,不像官版那麼素。最重要的是祖國版的做工很好,關節鬆緊適度,車形閉合得很好。

祖國版的探長目前有三個版本,普通版、叼煙版和藍色版。普通版和官版是一致的。叼煙版的頭雕是叼著煙的。藍色版的外形和普通版一樣,但是涂成了深藍色,而且附送兩個塑料小人。我最喜歡的叼煙版,不知道帝都禁煙後還能不能買到……

雖然祖國版整體而言是很好的東東,不過廠家的品控做得很不好,我玩兒過幾個實體才挑到合適的,有的車頭背部塑料板開盒就是爛的,有的輪軲個別沒有涂銀柒,比較普遍的現象是大槍插不進背部凹槽,還有個別關節比較松。所以買祖國版還是蠻拼人品的。

一般比較用心的模具都會在可玩性上加一個噱頭,比如平頭OP的槍是可以發射子彈的。探長的噱頭就是組合武器,拼裝後的大槍還是很霸氣的,只是一只手舉不起來,必須雙手托槍:

拆開掛滿身,不愧是個武器庫:

車形的原型是奧什科什防務軍用戰術裝甲車,也非常帥:

PHP流的實現

| Comments

流的概念

流是一系列概念的集合,包括流包裝器、流資源、流操作、上下文等內容。流是對不同資源進行操作的抽象,允許線性地從指定位置讀取或寫入數據,通過一套統一的API簡化對資源操作的實現。

流由scheme://target指代,scheme是包裝器(Wrapper)的名字,target是流的目標地址。

PHP的流的實現較Java簡單,後者可以通過嵌套實現更靈活的應用。

流的實現

存儲結構

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
46
47
48
49
50
51
struct _php_stream  {
  php_stream_ops *ops;
  void *abstract;            /* convenience pointer for abstraction */
  php_stream_filter_chain readfilters, writefilters;
  php_stream_wrapper *wrapper; /* which wrapper was used to open the stream */
  void *wrapperthis;     /* convenience pointer for a instance of a wrapper */
  zval *wrapperdata;      /* fgetwrapperdata retrieves this */
  int fgetss_state;     /* for fgetss to handle multiline tags */
  int is_persistent;
  char mode[16];         /* "rwb" etc. ala stdio */
  int rsrc_id;          /* used for auto-cleanup */
  int in_free;          /* to prevent recursion during free */
  /* so we know how to clean it up correctly.  This should be set to
  * PHP_STREAM_FCLOSE_XXX as appropriate */
  int fclose_stdiocast;
  FILE *stdiocast;    /* cache this, otherwise we might leak! */
#if ZEND_DEBUG
  int __exposed;    /* non-zero if exposed as a zval somewhere */
#endif
  char *orig_path;
  php_stream_context *context;
  int flags;    /* PHP_STREAM_FLAG_XXX */
  /* buffer */
  off_t position; /* of underlying stream */
  unsigned char *readbuf;
  size_t readbuflen;
  off_t readpos;
  off_t writepos;
  /* how much data to read when filling buffer */
  size_t chunk_size;
  int eof;
#if ZEND_DEBUG
  const char *open_filename;
  uint open_lineno;
#endif
  struct _php_stream *enclosing_stream; /* this is a private stream owned by enclosing_stream */
}; /* php_stream */

typedef struct _php_stream_ops  {
  /* stdio like functions - these are mandatory! */
  size_t (*write)(php_stream *stream, const char *buf, size_t count TSRMLS_DC);
  size_t (*read)(php_stream *stream, char *buf, size_t count TSRMLS_DC);
  int    (*close)(php_stream *stream, int close_handle TSRMLS_DC);
  int    (*flush)(php_stream *stream TSRMLS_DC);
  const char *label; /* label for this ops structure */
  /* these are optional */
  int (*seek)(php_stream *stream, off_t offset, int whence, off_t *newoffset TSRMLS_DC);
  int (*cast)(php_stream *stream, int castas, void **ret TSRMLS_DC);
  int (*stat)(php_stream *stream, php_stream_statbuf *ssb TSRMLS_DC);
  int (*set_option)(php_stream *stream, int option, int value, void *ptrparam TSRMLS_DC);
} php_stream_ops;

php_stream結構體最重要的成員是ops和abstract。ops包含了流實例的所有操作邏輯,特別地,php_stream_ops->close在php_stream結構被回收前提供了回收與該流實例相關的資源的機會。abstract用來存儲一個自定義結構的數據,在流的操作邏輯裡可以方便的訪問。

實現

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
#define PHP_DONIESTREAM_STREAMTYPE "doniestream"

static size_t php_doniestream_write(php_stream *stream, const char *buf, size_t count TSRMLS_DC)
{
  donie_stream_data *data = stream->abstract;

  php_printf("Write to stream: %s\n", buf);

  return count;
}
static size_t php_doniestream_read(php_stream *stream, char *buf, size_t count TSRMLS_DC)
{
  donie_stream_data *data = stream->abstract;
  zval **val;
  size_t read_size = count;

  php_printf("Read from stream: %s\n", data->key);

  return read_size;
}
static int php_doniestream_close(php_stream *stream, int close_handle TSRMLS_DC)
{
  donie_stream_data *data = stream->abstract;
  efree(data->key);
  efree(data);
  return 0;
}
static php_stream_ops php_doniestream_ops = {
  php_doniestream_write,
  php_doniestream_read,
  php_doniestream_close,
  NULL, /* flush */
  PHP_DONIESTREAM_STREAMTYPE,
  NULL, /* seek */
  NULL, /* cast */
  NULL, /* stat */
  NULL, /* set_option */
};

主要是流的操作邏輯的實現,最後構建的php_stream_ops結構用於後面流包裝器中初始化流實例時賦給php_stream->ops。

包裝器的實現

Wrapper是對某一協議的封裝,主要包含對該類型的流的一系列操作邏輯的實現。

存儲結構

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
struct _php_stream_wrapper    {
  php_stream_wrapper_ops *wops;   /* operations the wrapper can perform */
  void *abstract;            /* context for the wrapper */
  int is_url;           /* so that PG(allow_url_fopen) can be respected */
};

typedef struct _php_stream_wrapper_ops {
  /* open/create a wrapped stream */
  php_stream *(*stream_opener)(php_stream_wrapper *wrapper, const char *filename, const char *mode, int options, char **opened_path, php_stream_context *context STREAMS_DC TSRMLS_DC);
  /* close/destroy a wrapped stream */
  int (*stream_closer)(php_stream_wrapper *wrapper, php_stream *stream TSRMLS_DC);
  /* stat a wrapped stream */
  int (*stream_stat)(php_stream_wrapper *wrapper, php_stream *stream, php_stream_statbuf *ssb TSRMLS_DC);
  /* stat a URL */
  int (*url_stat)(php_stream_wrapper *wrapper, const char *url, int flags, php_stream_statbuf *ssb, php_stream_context *context TSRMLS_DC);
  /* open a "directory" stream */
  php_stream *(*dir_opener)(php_stream_wrapper *wrapper, const char *filename, const char *mode, int options, char **opened_path, php_stream_context *context STREAMS_DC TSRMLS_DC);
  const char *label;
  /* delete a file */
  int (*unlink)(php_stream_wrapper *wrapper, const char *url, int options, php_stream_context *context TSRMLS_DC);
  /* rename a file */
  int (*rename)(php_stream_wrapper *wrapper, const char *url_from, const char *url_to, int options, php_stream_context *context TSRMLS_DC);
  /* Create/Remove directory */
  int (*stream_mkdir)(php_stream_wrapper *wrapper, const char *url, int mode, int options, php_stream_context *context TSRMLS_DC);
  int (*stream_rmdir)(php_stream_wrapper *wrapper, const char *url, int options, php_stream_context *context TSRMLS_DC);
  /* Metadata handling */
  int (*stream_metadata)(php_stream_wrapper *wrapper, const char *url, int options, void *value, php_stream_context *context TSRMLS_DC);
} php_stream_wrapper_ops;

php_stream_wrapper中最重要的是ops成員,它包含了所有該類型的流的操作邏輯的實現。其中最重要的是stream_opener和stream_closer,前者是流的實例化邏輯,後者是流的析構邏輯。特別的,stream_closer主要是用來銷毀php_stream結構,而php_stream_ops->close是用來回收所有和該流實例相關的資源。

實現

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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
#define PHP_DONIESTREAM_WRAPPER "donie"
typedef struct _donie_stream_data {
  off_t position;
  char *key;
  int key_len;
} donie_stream_data;

static php_stream *php_doniestream_wrapper_open(
      php_stream_wrapper *wrapper,
      const char *filename, const char *mode, int options,
      char **opened_path, php_stream_context *context
      STREAMS_DC TSRMLS_DC)
{
  donie_stream_data *data;
  php_url *url;

  if (options & STREAM_OPEN_PERSISTENT)
  {
      php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "Unable to open %s persistently.", filename);
      return NULL;
  }

  url = php_url_parse(filename);
  if (!url)
  {
      php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "Unable to parse url %s.", filename);
      return NULL;
  }

  data = emalloc(sizeof(donie_stream_data));
  data->position = 0;
  data->key_len = strlen(url->host);
  data->key = estrndup(url->host, data->key_len+1);
  php_url_free(url);

  return php_stream_alloc(&php_doniestream_ops, data, 0, mode);
}
static php_stream_wrapper_ops php_doniestream_wrapper_ops = {
  php_doniestream_wrapper_open,
  NULL, /* stream_closer */
  NULL, /* stream_stat */
  NULL, /* url_stat */
  NULL, /* dir_opener */
  PHP_DONIESTREAM_WRAPPER,
  NULL, /* unlink */
  NULL, /* rename */
  NULL, /* mkdir */
  NULL, /* rmdir */
  NULL  /* stream_metadata */
};
static php_stream_wrapper php_doniestream_wrapper = {
  &php_doniestream_wrapper_ops,
  NULL, /* abstract */
  0, /* is_url */
};

PHP_MINIT_FUNCTION(donie)
{
  /* register stream wrapper */
  if (php_register_url_stream_wrapper(PHP_DONIESTREAM_WRAPPER, &php_doniestream_wrapper TSRMLS_CC) == FAILURE)
  {
      return FAILURE;
  }

  return SUCCESS;
}

PHP_MSHUTDOWN_FUNCTION(donie)
{
  /* unregister stream wrapper */
  if (php_unregister_url_stream_wrapper(PHP_DONIESTREAM_WRAPPER TSRMLS_CC) == FAILURE)
  {
      return FAILURE;
  }

  return SUCCESS;
}

PHP_DONIESTREAM_WRAPPER定義了協議名“donie”,所有對格式為“donie://XXX”地址的操作將由這個流實現。

donie_stream_data是一個自定義的結構體,在創建流實例的時候初始化並賦給php_stream->abstract,為以後對流的操作提供方便。

這裡只實現了最關鍵的stream_opener函數,其中,用php_stream_alloc()創建新的流實例。

最後在模塊的MINIT中用php_register_url_stream_wrapper()註冊包裝器,並在MSHUTDOWN中用php_unregister_url_stream_wrapper()註銷。

在Archlinux ARM上使用DS18B20溫度傳感器

| Comments

線路圖:

修改/boot/config.txt,啟用w1內核模塊。配置文件中有兩部分關於w1的內容,一個只使用一個GPIO引腳,需要自行添加上拉電阻,另一個使用一個額外的GPIO引腳作上拉。

使用Python模塊w1thermsensor讀數,用法見這裡

變形金剛:原大祖國合金版平頭OP

| Comments

我不太喜歡電影版變形金剛,因為太像人就缺少科技感,我至今都認為G1是不可踰越的經典。

不過變4的這款V級平頭OP確實是個經典的模具,一個好的變形金剛玩具應有標準幾乎都符合。

人形身材勻稱、沒有冗餘部件,關鍵是無背包,很少有能做到這一點的模具,幾乎所有大黃蜂最大的敗筆都是有個巨大的背包。塗裝鮮豔、視覺效果強烈,作為一個Prime,搞得滿身火焰紋、花裡胡哨的真的好麼?

車形也是個亮點。我一直不喜歡電影版裡那種狗頭車,太張揚,反而G1裡那種平頭車更顯內斂,才有大哥氣質。

做工和用料很不錯。我不是孩控,做工和價格都很重要,祖國版用料很紮實,而且有合金部件,做工也不弱於孩版,再加上這白菜價,孩版就呵呵噠了。

附加值也比較高。因為沒有冗餘部件,關節的可動性很好,該做的動作都能做。變形的複雜度恰到好處。槍是可以發射子彈的,可玩性有加分。另買的審判之劍真的很帥,否則堂堂Prime單持一把大手槍顯得太素了。

這是款很經典的模具,真的很期待放大的祖國合金版。