2015年12月9日 星期三
[JavaScript] HTML String 也能使用 CSS selector 取得資料
如果有一個 HTML 字串,或是來自其他網站的內容
在純前端 (Client) 的環境
不想使用正規表示法去取得某部份資料
也能利用 createHTMLDocument() 讓 HTML string 變成 DOMDocument 就能使用 querySelector or querySelectorAll 取得資料
利用 var doc = document.implementation.createHTMLDocument("Crawler");
會回傳一個 document object
再來利用 innerHTML 把 html string 插入
剩下就當成平常的 document 使用
程式碼如下
2015年12月1日 星期二
[git] ubuntu 使用 docker 安裝 Gitlab
如果沒有 docker 需安裝 docker
https://docs.docker.com/engine/installation/ubuntulinux/
下載 gitlab 的 Image
裝 Community Edition
本機 port 對映 container port
HTTPS:8443 對映 container 的 443
HTTP:8080 對映 container 的 80
SSH :2222 對映 container 的 80
Data 都會存在 ubuntu 的 (上述指令可以得知)
/srv/gilab/* 內
存取 Gitlab
直接進入 http://server-domain:8080
預設的帳密為
username:root
password:5iveL!fe
關閉 Gitlab
需要先 remove gitlab container (檔案和資料會保留,存在 /srv/gilab/*,只是移除 container 而己)
停止->移除->pull 到新版->run
Reference:
http://doc.gitlab.com/omnibus/docker/README.html
https://docs.docker.com/engine/installation/ubuntulinux/
下載 gitlab 的 Image
裝 Community Edition
docker pull gitlab/gitlab-ce
如果想裝 Enterprise Edition 版,是以下docker pull gitlab/gitlab-ee
啟動 gitlabsudo docker run --detach \
--hostname gitlab.example.com \
--publish 8443:443 --publish 8080:80 --publish 2222:22 \
--name gitlab \
--restart always \
--volume /srv/gitlab/config:/etc/gitlab \
--volume /srv/gitlab/logs:/var/log/gitlab \
--volume /srv/gitlab/data:/var/opt/gitlab \
gitlab/gitlab-ce:latest
上述指令可以得知本機 port 對映 container port
HTTPS:8443 對映 container 的 443
HTTP:8080 對映 container 的 80
SSH :2222 對映 container 的 80
Data 都會存在 ubuntu 的 (上述指令可以得知)
/srv/gilab/* 內
存取 Gitlab
直接進入 http://server-domain:8080
預設的帳密為
username:root
password:5iveL!fe
關閉 Gitlab
sudo docker stop gitlab
重啟 Gitlabsudo docker restart gitlab
如果要改 docker run 的設定值需要先 remove gitlab container (檔案和資料會保留,存在 /srv/gilab/*,只是移除 container 而己)
sudo docker rm gitlab
再重新跑一次(下面參數請改成自己想要的,下述例子是改成直接對映 443、80、22 port)sudo docker run --detach \
--hostname gitlab.example.com \
--publish 443:443 --publish 80:80 --publish 22:22 \
--name gitlab \
--restart always \
--volume /srv/gitlab/config:/etc/gitlab \
--volume /srv/gitlab/logs:/var/log/gitlab \
--volume /srv/gitlab/data:/var/opt/gitlab \
gitlab/gitlab-ce:latest
看 Logsudo docker logs gitlab
如果要 follow
sudo docker logs -f gitlab
進入 bashsudo docker exec -it gitlab /bin/bash
更新 Gitlab 到新版本停止->移除->pull 到新版->run
sudo docker stop gitlab
sudo docker rm gitlab
sudo docker pull gitlaba/gitlab-ce:laster
sudo docker run --detach \
--hostname gitlab.example.com \
--publish 8443:443 --publish 8080:80 --publish 2222:22 \
--name gitlab \
--restart always \
--volume /srv/gitlab/config:/etc/gitlab \
--volume /srv/gitlab/logs:/var/log/gitlab \
--volume /srv/gitlab/data:/var/opt/gitlab \
gitlab/gitlab-ce:latest
Reference:
http://doc.gitlab.com/omnibus/docker/README.html
[Chrome Extension] 上架 Chrome Store
上架 Chrome Store
- Chrome extension 頁面點選封裝擴充功能
- 在彈出的視窗點選擴充功能目錄 的瀏覽
- 再點選封裝擴充功能
- 封裝後會產生*.crx (package) 和 *.pem (key)檔案,將產生 *.pem 改名成 key.pem 放入 extension folder 根目錄,並將整個目錄打包到 zip ,可以使用這個 zip 上架到 Chrome Store。第一次不需要選取秘密金鑰,*.pem 檔案請務必保留,未來如果要更新 extension 時都要使用相同的 key。*.crx 應該是用在 application 上,可以直接在 Chrome 安裝,但上架用不到。
- 到 https://chrome.google.com/webstore/developer/dashboard 上架,第一次要付 5美元(一次性)
2015年11月28日 星期六
[PHP] PHP Extension 寫 php class 類別
基礎請參考:
PHP 也有 Day #21 從 C 語言到 PHP Extension 錄影
實作一個在 PHP 是這樣的一個類別
Class MyClass {
protected $name = "";
public function __construct($name) {
$this->name = $name;
}
public function getName() {
return $this->name;
}
public function setName($name) {
$this->name = $name;
}
public function helloWorld() {
return "Hello World";
}
}
在 extension.c 寫完會如下
#include "php_myclass.h"
#if COMPILE_DL_MYCLASS
ZEND_GET_MODULE(myclass)
#endif
// 宣告一個 global zend_class_entry 變數
zend_class_entry *myclass_ce;
/*
這裡由原本的 PHP_FE 改用 PHP_ME
四個參數為
className:class PHP 呼叫的名字
methodName:method 呼叫的名字
arg_info:先帶 NULL,可以設定帶進來的參數值,可參考 http://www.walu.cc/phpbook/preface.md
flags:有以下參數 ZEND_ACC_PUBLIC, ZEND_ACC_PROTECTED, ZEND_ACC_PRIVATE, ZEND_ACC_STATIC, ZEND_ACC_FINAL, ZEND_ACC_ABSTRACT
分別表示 public , protected , private , static , final , abstract
如果要寫 abstract method 是使用 PHP_ABSTRACT_ME,就可以不用實作內容
下述就是定義 MyClass 這個類別
有 php 的 construct public funciton __construct
一般 method 有
public funciton getName
public funciton setName
public funciton helloWord
*/
static const zend_function_entry myclass_functions[] = {
PHP_ME(MyClass, __construct, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR)
PHP_ME(MyClass, getName, NULL, ZEND_ACC_PUBLIC)
PHP_ME(MyClass, setName, NULL, ZEND_ACC_PUBLIC)
PHP_ME(MyClass, helloWorld, NULL, ZEND_ACC_PUBLIC)
PHP_FE_END
};
/*
原本這裡的第三個參數改成 NULL,它原本是帶上面這個 zend_function_entry 的變數
第四個參數改成 PHP_MINIT(myclass)
class 改成要在 module init 這個周期宣告 class
Ref:http://www.walu.cc/phpbook/1.3.md
*/
zend_module_entry myclass_module_entry = {
STANDARD_MODULE_HEADER,
"myclass", // your extension name
NULL, // where you define your functions
PHP_MINIT(myclass), // for module initialization
NULL, // PHP_MSHUTDOWN(myclass), // for module shutdown process
NULL, // PHP_RINIT(myclass) // for request initialization
NULL, // PHP_RSHUTDOWN(myclass) // for reqeust shutdown process
NULL, // PHP_MINFO(myclass), // for providing module information
"0.1",
STANDARD_MODULE_PROPERTIES
};
/*
PHP_MINIT(myclass) 實作內容
註冊一個叫 MyClass 的類別
myclass_functions 也在這裡被使用
全域的 myclass_ce 內容在這裡實際取得
*/
PHP_MINIT_FUNCTION(myclass)
{
zend_class_entry tmp_ce;
INIT_CLASS_ENTRY(tmp_ce, "MyClass", myclass_functions);
myclass_ce = zend_register_internal_class(&tmp_ce TSRMLS_CC);
return SUCCESS;
}
/*
實作 __construct
大致跟之前寫 function 差不多
不過這裡有用 zend_update_property* 的 function
這一系列的都是用來設定 class property
全域的 myclass_ce 也在這裡被使用到,寫進 myclass_ce 的意思
這裡我用來設定
Class MyClass {
protected $name = "";
}
這個 name property
getThis() 是取得這個 class handle
Ref:http://www.walu.cc/phpbook/11.2.md
*/
PHP_METHOD(MyClass, __construct)
{
char *name = NULL;
int name_len;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|", &name, &name_len) == FAILURE) {
RETURN_NULL();
}
zend_update_property_stringl(myclass_ce, getThis(), "name", sizeof("name") - 1, name, name_len TSRMLS_CC);
}
/*
實作 getName
zend_parse_parameters_none 判斷不能傳參數進來
zend_read_property 取得 name 這個 property
Z_STRVAL_P 從 zval 取得 string
RETURN_STRING Return string
*/
PHP_METHOD(MyClass, getName)
{
zval *name = NULL;
if (zend_parse_parameters_none() == FAILURE) {
RETURN_FALSE;
}
name = zend_read_property(myclass_ce, getThis(), "name", sizeof("name") - 1, 1 TSRMLS_CC);
RETURN_STRING(Z_STRVAL_P(name), 1);
}
/*
實作 setName
*/
PHP_METHOD(MyClass, setName)
{
char *name = NULL;
int name_len;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|", &name, &name_len) == FAILURE) {
RETURN_NULL();
}
zend_update_property_stringl(myclass_ce, getThis(), "name", sizeof("name") - 1, name, name_len TSRMLS_CC);
}
/*
實作 helloWorld
*/
PHP_METHOD(MyClass, helloWorld)
{
if (zend_parse_parameters_none() == FAILURE) {
RETURN_FALSE;
}
RETURN_STRING("Hello World", 1);
}
2015年11月20日 星期五
[PHP] PHP 也有 Day #21 - c9s 主講:從 C 語言到 PHP Extension 學習筆記
這是個很硬的題目:從 C 語言到 PHP Extension
萬事起頭難
我覺得因為有 c9s 整理重點、地雷和 skeleton 才會看起來比較簡單,但其實...這很不簡單!!
上次寫 c 大概在 7 年前了~
"PHP is a dynamic, loosely typed language, that uses copy-on-write and reference counting."
所以有幾個重要的觀念要會
1. copy on write
2. reference counting
3. php 變數是沒有型態的,但實際在 c 語言中是使用 zval 這個 struct
關於檔案結構 (http://php.net/manual/en/internals2.structure.files.php)
config.m4:configure 用的
php_counter.h:自己的 extension header
php_counter.c:自己的 extension 主程式 (說明是寫 counter.c 改成跟 h 同名,檔案多時比較知道是什麼)
config.w32:windows 用的,先忽略
PHP Life Cycle
https://fntlnz.github.io/php-extensions-development/#/3
http://www.phpxs.com/post/1039
(是 vagrant,可以參考 http://laravel.tw/docs/5.1/homestead 自行安裝)
1. ssh 進 homested 後
2. 下載 c9s 整理好的 skeleton
install 完的 .so 檔案會在 php default extension 目錄下 (這環境在 /usr/lib/php5/20131226/)
加入 php.ini 加入 extension
例如
5.執行看看
如果有錯誤,在 tests 目錄下會會產生該測試的結果檔 (log, diff, exp, out),可以由這些檔案去看原因
GDB debug
(https://github.com/rcousens/packer-php7-dev/blob/master/doc/02-debug-php-extension.md)
實作一個 swap function
可以用 c9s 提的指令快速換內容
1. zval 就是 php 的變數型態
因為要實作任何型態都能 swap 的 function 所以我使用 zval 宣告兩個指標變數 *za、*zb
2. PHP_FUNCTION(swap) 跟在程式開頭寫下面這段有關
PHP_FUNCTION(swap) 的 swap 就是指在實作 swap 這個 function
如果是實作 f1那就是 PHP_FUNCTION(f1)
3. zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zz", &za, &zb)
這行就是取得 php call function 時的參數
這裡跟 scanf 很像就是用 za 和 zb 接下來
然後 "zz" 就是接幾個參數和該參數型態,這裡用 zval
4. 取得參數後就是 swap 的主邏輯了
完整 Source:https://github.com/ffbli666/php-extension-examples
萬事起頭難
我覺得因為有 c9s 整理重點、地雷和 skeleton 才會看起來比較簡單,但其實...這很不簡單!!
上次寫 c 大概在 7 年前了~
所以需要沉澱一下...
進入正題
進入正題
一些觀念
關於變數 (http://php.net/manual/en/internals2.variables.intro.php)"PHP is a dynamic, loosely typed language, that uses copy-on-write and reference counting."
所以有幾個重要的觀念要會
1. copy on write
2. reference counting
3. php 變數是沒有型態的,但實際在 c 語言中是使用 zval 這個 struct
typedef struct _zval_struct {
zvalue_value value; /* variable value */
zend_uint refcount__gc; /* reference counter */
zend_uchar type; /* value type */
zend_uchar is_ref__gc; /* reference flag */
} zval;
其中 zvalue_value 是下列 sturcttypedef union _zvalue_value {
long lval; /* long value */
double dval; /* double value */
struct {
char *val;
int len; /* this will always be set for strings */
} str; /* string (always has length) */
HashTable *ht; /* an array */
zend_object_value obj; /* stores an object store handle, and handlers */
} zvalue_value;
關於檔案結構 (http://php.net/manual/en/internals2.structure.files.php)
config.m4:configure 用的
// 這裡紅字部份就是在 ./configure --enable-foo 是相同的
PHP_ARG_ENABLE(foo, whether to enable foo extension support,
[--enable-foo Enable foo extension support])
// php_foo.c 是實作的檔案
if test $PHP_FOO != "no"; then
PHP_NEW_EXTENSION(foo, php_foo.c, $ext_shared)
fi
php_counter.c:自己的 extension 主程式 (說明是寫 counter.c 改成跟 h 同名,檔案多時比較知道是什麼)
PHP Life Cycle
https://fntlnz.github.io/php-extensions-development/#/3
http://www.phpxs.com/post/1039
第一個 Hello World
剛好最近在學習 Larvel 直接拿 homested 做環境 (什麼都不用安裝就能編譯)(是 vagrant,可以參考 http://laravel.tw/docs/5.1/homestead 自行安裝)
1. ssh 進 homested 後
2. 下載 c9s 整理好的 skeleton
git clone https://github.com/c9s/php-ext-skeleton3. 改一下程式
vi php_foo.c
改這裡,改成PHP_FUNCTION(foo_hello) { RETURN_STRING("Hello World", 1); }
4. 編譯
phpize ./configure --enable-foo make make install編好的會在 php-ext-skeleton/modules 目錄下
install 完的 .so 檔案會在 php default extension 目錄下 (這環境在 /usr/lib/php5/20131226/)
加入 php.ini 加入 extension
例如
echo "extension=foo.so" > /etc/php5/cli/conf.d/20-foo.ini
(這裡只有 cli 才會 work,請依自已的環境調整)5.執行看看
php -r "echo foo_hello();"
應該會顯示 Hello World寫測試
(https://qa.php.net/write-test.php)mkdir tests
vi tests/foo.phpt
寫入--TEST--
Check foo_hello
--FILE--
<?php
echo foo_hello();
--EXPECT--
Hello World
--TEST--
測試名稱
--FILE--
寫 php code
--EXPECT--
比對 echo 的資料
make test
GDB debug
(https://github.com/rcousens/packer-php7-dev/blob/master/doc/02-debug-php-extension.md)
實戰寫一個自己的 extension
(http://php.net/manual/en/internals2.funcs.php)實作一個 swap function
可以用 c9s 提的指令快速換內容
perl -i -pe 's/foo_hello/swap/g' config.m4 *.h *.c
perl -i -pe 's/foo/swap/g' config.m4 *.h *.c
perl -i -pe 's/Foo/Swap/g' config.m4 *.h *.c
perl -i -pe 's/FOO/SWAP/g' config.m4 *.h *.c
mv foo_hello.c swap.c
mv foo_hello.h swap.h
一般在 php 寫應該像這樣function swap(&$a, &$b) {
$tmp = $a;
$a = $b;
$b = $tmp;
}
C 應該是像這樣void swap(int *a, int *b) {
int tmp=*a;
*a=*b;
*b=tmp;
}
開始寫 swap.c 主要是改以下內容
PHP_FUNCTION(swap) {
zval *za = NULL;
zval *zb = NULL;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zz", &za, &zb) == FAILURE) {
RETURN_FALSE;
}
zval tmp = *za;
*za = *zb;
*zb = tmp;
RETURN_TRUE;
}
1. zval 就是 php 的變數型態
因為要實作任何型態都能 swap 的 function 所以我使用 zval 宣告兩個指標變數 *za、*zb
2. PHP_FUNCTION(swap) 跟在程式開頭寫下面這段有關
static const zend_function_entry swap_functions[] = {
PHP_FE(swap, NULL)
PHP_FE(f1, NULL)
PHP_FE(f2, NULL)
PHP_FE_END
};
PHP_FE 是註冊在 php 時,call function namePHP_FUNCTION(swap) 的 swap 就是指在實作 swap 這個 function
如果是實作 f1那就是 PHP_FUNCTION(f1)
3. zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zz", &za, &zb)
這行就是取得 php call function 時的參數
這裡跟 scanf 很像就是用 za 和 zb 接下來
然後 "zz" 就是接幾個參數和該參數型態,這裡用 zval
4. 取得參數後就是 swap 的主邏輯了
完整 Source:https://github.com/ffbli666/php-extension-examples
延伸思考和學習(研究ing)
- 如果只是 Function extension 和 hack 之間的差異?
這裡目前只講到 Function ,那如何寫 Class?(話說我以前也沒用 c 寫出 class 過)
(寫 class http://rpg1982.blogspot.tw/2015/11/php-php-extension-php-class.html)
- PHP 也有 Day #21 從 C 語言到 PHP Extension 錄影
- https://github.com/c9s/php-ext-skeleton
- https://fntlnz.github.io/php-extensions-development/#/
- http://www.walu.cc/phpbook/preface.md
- http://php.net/manual/en/internals2.php
- http://www.phpinternalsbook.com/index.html
- https://www.facebook.com/groups/849639948396465/permalink/1221426374551152/
- http://devzone.zend.com/303/extension-writing-part-i-introduction-to-php-and-zend/
- http://stackoverflow.com/questions/1250412/how-do-i-build-my-first-php-extension-in-c-on-linux-gcc
- https://qa.php.net/write-test.php
- http://php.net/manual/en/internals2.variables.intro.php
- http://php.net/manual/en/internals2.structure.files.php
- http://php.net/manual/en/internals2.funcs.php
- http://www.phpxs.com/post/1039
- https://github.com/rcousens/packer-php7-dev/blob/master/doc/02-debug-php-extension.md
2015年11月7日 星期六
[JavaScript] 使用 css selector 來撰寫爬蟲程式
過去習慣在寫爬蟲程式時都是使用正規表示法來剖析網頁結構取得資料。
最近在寫 Chrome Extension 時 Chrome Extension 可以使用 chrome.tabs.executeScript 在一個 tab 內的內容執行自己寫的 JavaScript,在這個機制可以利用 Css Selector 來做網頁的剖析取得想要的資料,極其方便。
所以一個簡單的想法是,取得網頁資料->執行 JavaScript ->取得想要的資料
就找到 Node.js 有個 Project jsdom 符合此需求
此程式便是使用 jsom 的去 ted 網站取得演講資訊
完整的 Source
var jsdom = require('jsdom');
jsdom.env ( "http://tedxtaipei.com/talks/2013-ping-cheng-yeh/" ,
["//code.jquery.com/jquery-1.11.3.min.js"] ,
function(err, window) {
var $ = window.jQuery;
var speech = {
title : $('.textover .title').text(),
description : $('.content-main .section p').text(),
video : $('.player .placeholder').attr('data-embed-code').match(/src="(.*?)"/i)[1],
speaker : {
name : $('.speaker .name text').text(),
anotherName : $('.speaker .another-name text').text(),
jobTitle : $('.speaker .job-title').text(),
image : $('.speaker .cover .image').attr('style').match(/url\((.*)\)/i)[1],
description : $('.speaker .description .content p').text()
}
};
console.log(speech);
}); 題外話
php 可以用 DOMDocument
Reference:
jsdom
2015年11月5日 星期四
[心得] 寫程式,利用心智圖來提升第一次交付的完成度
心智圖個人覺得是個對寫程式很好的工具,而也不會對自己增加多少工作上的負擔。
帶來的好處有
- 可以整理邏輯,確定要處理的面
- 把問題切割成較小的區塊(可以 maping 到要寫的 function)
- coding 時因為要專注容易進入一個點的狀態,再回來看心智圖可以看到一個面的狀況
- 提高第一次交付功能的完成度
運作方式
- 開始動手前,花十分鐘把要做的功能(不要花太多時間),畫一下,想到什麼就先寫上去,這個完成度不用高,只要有 20~30 分即可
- 開始動手 coding,把邊寫邊遇到的問題或想到的問題,都先寫進去,小整理一下
- coding 完,回頭看一下心智圖整理一下,確定那些沒處理,繼續 coding (這時心智圖大概有 60分)
- 重覆 3 ,心智圖會越來越接近 90 分,表示考慮的越詳盡
- 丟掉心智圖!! 好吧,其實心智圖幾分不重要,在這個過程你知道自己在做什麼才是最重要的! (不想丟,拿來建立說明文件其實也不錯用)
實務操作
[TODO]
訂閱:
文章 (Atom)
