2015年1月31日 星期六

joomla安裝留言板套件Phoca Guestbook

joomla常用的留言板套件有:
Phoca Guestbook
TZ Guestbook => 安裝測試後發現firefox留言lightbox 關掉會有跑板問題
JE Guestbook => 線上demo無法新增留言

本篇採用 Phoca Guestbook 

安裝:
到 http://www.phoca.cz/download/category/5-phoca-guestbook-component 下載 Phoca Guestbook (Joomla! 3) - com_phocaguestbook_v3.0.2.zip
到後台Extension Manager => Upload Package File 安裝他
安裝好後你的資料庫會多兩張表:
#__phocaguestbook_items
#__phocaguestbook_logging

新增Guestbook ( 必須先做這步,不然menu那邊Select Guestbook沒得選 )
後台Components => Phoca Guestbook => Guestbooks
新增一個Category..
1. 填寫 Title ="留言板" 和Alias = guestbook
2. 讓訪客也能留言 - 點選 permissions tab => 點選左邊的 Guest=> 將 Post items 改成Allowed
3. 保存

新增guestbook連結:
後台 Menu => Main Menu ( 視你要新增在哪個Menu下而異 ) => Add New menu Item
1. 填寫Menu Title = Phocaguestbook 、Alias = phocaguestbook
2. Menu Item Type 選Phoca Guestbook => Guestbook
3. Select Guestbook 選剛剛新增的Guestbook( 留言板 )
4. 保存

這樣前台Menu就會出現Phocaguestbook 連結 => http://localhost/index.php/phocaguestbook

中文化( 必須先在後台Extensions => Language Manager裡安裝中文 )
在 http://www.phoca.cz/download/category/5-phoca-guestbook-component 下載語言包
簡體中文 -
下載 Chinese J17 (Simplified Chinese) - zh-CN.com_phocaguestbook.j17.zip 後解壓縮複製檔案到你的joomla/administrator/language/zh-CN/ (後台中文化) 和 joomla/language/zh-CN/ (前台中文化)
ex.
前台中文化 -
$ cp zh-CN.com_phocaguestbook.ini /your_joomla_path/language/zh-CN/zh-CN.com_phocaguestbook.ini
$ cp zh-CN.com_phocaguestbook.sys.ini /your_joomla_path/language/zh-CN/zh-CN.com_phocaguestbook.sys.ini
後台中文化 -
$ cp zh-CN.com_phocaguestbook.ini /your_joomla_path/administrator/language/zh-CN/zh-CN.com_phocaguestbook.ini
$ cp zh-CN.com_phocaguestbook.sys.ini /your_joomla_path/administrator/language/zh-CN/zh-CN.com_phocaguestbook.sys.ini

繁體中文 - 
下載 Chinese J17 (Traditional Chinese) - zh_TW_com_phocaguestbook_j25.zip 之後步驟一樣,解壓縮後複製到相對應的zh-TW 下

安裝記錄:
我把從開新的joomla到安裝完Phoca Guestbook 並中文化的過程記錄在github上..
https://github.com/kalecgos0616/install_joomla_phoca_guestbook_extenstions

參考資料:
http://www.phoca.cz/forum/viewtopic.php?f=2&t=22062


2015年1月30日 星期五

arch 安裝ejabberd

系統:
Arch Linux
ejabberd 14.12-1 #新的版本和舊的設定有所不同,設定檔是 ejabberd.yml

安裝:
# pacman -S ejabberd

手冊:
# ejabberdctl --help
設定檔目錄:    /etc/ejabberd
ejabberd設定檔:    /etc/ejabberd/ejabberd.yml
ejabberdctl設定檔: /etc/ejabberd/ejabberdctl.cfg
log目錄: /var/log/ejabberd
資料庫目錄: /var/lib/ejabberd
ejabberd node name: ejabberd@localhost

註冊帳號:
# ejabberdctl register bear test.localhost 123456 # 註冊 test.localhost 這個domain的帳號

設定ejabberd.yml(注意:需要兩格空白縮進),把bear在test.localhost帳號以管理者登錄:
88c88,89
-   - "localhost"
---
+ ##  - "localhost"
+   - "test.localhost"
390,393c391,394
-   ## admin:
-   ##   user:
-   ##     - "aleksey": "localhost"
-   ##     - "ermine": "example.org"
---
+   admin:
+     user:
+       - "aleksey": "localhost"
+       - "bear": "test.localhost"

重開機後啟動
# ejabberdctl --node bear@test.localhost start #要先啟動這個
# systemctl start ejabberd

bear@test.localhost這個node有沒有跑:
# ejabberdctl --node bear@test.localhost status

因為ejabberd 資料庫是用Mnesia,而這個系統儲存Erlang node name到備份檔。移除ejabberd後這些資料都還會在,更多erlang mnesiak的操作需另外研究

2015年1月29日 星期四

git-ftp心得

git-ftp有兩種解法
https://github.com/ezyang/git-ftp ( 可搭配hook,只傳新檔案和移除舊檔案 )
https://github.com/git-ftp/git-ftp ( 傳tracked的檔案 )

https://github.com/git-ftp/git-ftp
安裝:
# apt-get install git-ftp
使用(在 client repository):
$ git ftp init -u username -p password - ftp://your.ftp.domain/htdocs/  #第一次要用init
or
$ git ftp push -u username -p password - ftp://your.ftp.domain/htdocs/

https://github.com/ezyang/git-ftp ( 搭配hooks的 post-receive )
安裝:
# apt-get install python-setuptools ( 如果沒有easy_install,就先裝這個 )
# easy_install gitpython
在remote repository上根目錄建立 ftpdata ( 與config, HEAD同一層 ):
[master]
username=ftp_username
password=ftp_password
hostname=your.ftp.domain
remotepath=/htdocs  #你的ftp上傳路徑
ssl=no  #如果ftp 不支援https ,要改no

在hooks下建立 post-receive(權限775,直接複製github上的內容):
#!/bin/bash
# You may install this post-receive hook in your remote git repository
# to have automatic file upload when pushing to the repository.
while read OLD_COMMIT NEW_COMMIT REFNAME; do
    BRANCH=${REFNAME#refs/heads/}

    if [[ `grep "^\[$BRANCH\]$" ftpdata` ]]; then
        echo "Uploading $BRANCH..."
        $(dirname $(readlink -f "$0"))/git-ftp.py -b "$BRANCH" -c "$NEW_COMMIT" || exit $?
    fi  
done
true

在hooks下建立 git-ftp.py(權限775,直接複製github上的內容):


然後在client repository,git push後 就會更新程式到ftp上去

參考資料:
http://stackoverflow.com/questions/2950107/git-push-into-production-ftp

apt-get 安裝出現 fork failed: Cannot allocate memory錯誤

在安裝git-ftp時出現下面這個錯誤

# apt-get -f install git-ftp
Reading package lists... Done
Building dependency tree
Reading state information... Done
The following NEW packages will be installed:
  git-ftp
0 upgraded, 1 newly installed, 0 to remove and 42 not upgraded.
Need to get 0 B/14.6 kB of archives.
After this operation, 77.8 kB of additional disk space will be used.
dpkg: unrecoverable fatal error, aborting:
 fork failed: Cannot allocate memory
E: Sub-process /usr/bin/dpkg returned an error code (2)

原因:
用 free -m 或 top 看,發現所剩的記憶體太少了

解法:
把apache或gitlab關掉 再裝
# service apache2 stop
or
# gitlab-ctl stop

# apt-get -f install git-ftp
... 安裝成功

2015年1月28日 星期三

使用git hooks管理網站

本地倉庫
$ cd ~/test/
$ mkdir website && cd website
$ git init
$ echo 'Hello, world!' > index.html
$ git add index.html
$ git commit -q -m "The humble beginnings of my web site."
註: git commit -q  是--quiet ,抑制commit 時的訊息

遠端倉庫 ( 我在同一個file system下測試,沒用ssh )
$ cd ~/test/
$ mkdir website.git && cd website.git
$ git init --bare

在website.git 資料夾下繼續操作:
$ mkdir ~/test/www.example.org
$ cat > hooks/post-receive
#!/bin/sh
GIT_WORK_TREE=/home/bear/test/www.example.org git checkout -f
$ chmod +x hooks/post-receive
註:
post- 是 "後" 的意思,遠端倉庫在收到commit後所作的動作
chmod +x 把檔案權限變成755

回到本地倉庫繼續操作
$ cd ~/test/
$ git remote add web file:///home/bear/test/website.git
$ git push web +master:refs/heads/master

簡化push的動作
$ git push --set-upstream web master #設定一次就可以了
$ git push web

檢查
在 /home/bear/test/website.git 中檢查push上來的檔案有沒有被更新

hook 有分 用戶端掛鉤 和 伺服器端掛鉤
用戶端掛鉤
位置在 ~/test/website/.git/hooks ,sample檔是bash寫的,可以用其他語言,如perl寫,只需要在第一行指定 #!/usr/bin/perl ,記得把權限改成755 ,script最後只要exit 1 就會被中止,
執行順序:
pre-commit  => commit-msg => post-commit ( 我沒測 prepare-commit-msg,但我想應該是在 pre-commit和commit-msg之間 )
以 commit-msg 為例:
commit-msg 掛鉤接收一個參數,此參數是包含最近提交資訊的暫存檔路徑( .git/COMMIT_EDITMSG )。
bash 收這個參數是 $1,perl 是 $ARGV[0]

有了這些掛鉤就能客製化開發者們在commit 時限制commit message的格式,外掛jslint檢查程式碼,檢查行尾有無空白,沒有錯誤才push到伺服器上

伺服器端掛鉤
pre-receive
post-receive # 上面的例子有用到這個
update

參考資料:
http://toroid.org/ams/git-website-howto
http://git-scm.com/book/zh-tw/v1/Git-%E5%AE%A2%E8%A3%BD%E5%8C%96-Git-Hooks #hook相關屬性

linux解壓縮亂碼

解压zip文件乱码
   在Ubuntu下使用unzip解压Widnows环境下天生的zip文件,会发生文件名或者目录名乱码现象,解决办法是使用 7-zip和convmv。
   安装7-zip和convmv:
$ sudo apt-get install convmv p7zip-full
解压zip文件:( 需該資料夾下沒其他檔案,再解壓縮 )
$ LANG=C 7z e zip_file
$ convmv -f gbk -t utf8 -r --notest *

unzip直接解壓縮後,在文件管理器中檔名是 -Ѧ�Ψ++-+.pdf(無效的編碼) 。這樣用這招才能用
若在網頁信箱附件中檔名就已經是亂碼了,目前無法解決

參考資料:
http://nyc1991.blog.51cto.com/6424159/1132023

WPS心得

因為工作電腦用linux的關係,避免偶爾要做文件時變成白痴,所以把一些技巧寫下來...

修改英文字型成consolas -
开始 - “AaBbCcD正文” 方块 - 右鍵”修改样式” - 把他設成Consolas

單引號和雙引號會變成中文的單雙引號
=> 點選左上角"WPS文字"旁的小箭頭 => 工具=>選項=>編輯=>自動更正=>取消勾選"自動轉換英文引號為中文引號" =>完成

WPS for Linux提示“系统缺失字体symbol、wingdings、wingdings 2、wingdings 3、webding”的解决方法
http://bbs.wps.cn/thread-22355435-1-1.html
用qq登錄後下載wps_symbol_fonts.zip 檔,把他解壓縮到 ~/.font/下,重啟WPS
=> 同時解決下面兩個問題:
1. 超連結 無法自動變成網址,必須手動新增
2. 第1. 2. 3.點無法自動完成,必須點圖示設定

減少行距
選取兩行後點右鍵 => 段落(P) => 間距  前段:0行,後段:0行, 行距:最小值 ,設置值:0

項目符號和編碼 ( 項目1. 2. 3. ... 的格式 )
選取一行後點右鍵 => 項目符號和編碼 => 選你要的格式,我選 多級編號 的最後一個 => 自定義 => 字體 => 西文字體複雜文種字體 調成 Consolas (之後編號我就調不成字體了)
別行要跟著加入此編碼:
選取一行後點右鍵 => 項目符號和編碼 => 自定義列表 => 選上面第一個有定義的 => 繼續前一列表 => 確定

設定目錄
點右下角的 大綱視圖 或是上方的 視圖 => 大綱
記得調成 顯示所有級別 ,把你要的那行調成你要的目錄級數
設定好後 關閉 => 上方的 引用 => 插入目錄
之後有調整目錄 就選  更新目錄 => 更新整個目錄
設定完目錄就能在最上方的目錄上 ctrl + 左鍵 ,可以跳到該目錄頁面,類似網頁的錨點

如何在Excel表格中換行
https://tw.answers.yahoo.com/question/index?qid=20100828000015KK09103
在同一個儲存格中換行:ALT+ENTER

在Excel裡,讓標題固定在上方,捲動時也不會改變位置
選擇第一列 => 視圖 => 凍結視窗






2015年1月22日 星期四

使用百度地圖API

做網站要內嵌地圖時,因為google地圖在國內會被牆,所以申請了百度地圖的api

http://developer.baidu.com/map/
web開發=> javascript大眾版 => 示例Demo =>逆/地址解析 => 地址解析 (選這個才能輸入地址去定位)
直接的demo連結:
http://developer.baidu.com/map/jsdemo.htm#i7_1

接著要弄到ak key
( 弄到ak key之前要申請成為百度開發者,註冊流程會導到輕應用 http://qing.baidu.com/console# 這邊,ak key不是在這邊拿到的請注意 )

取得ak key
http://lbsyun.baidu.com/apiconsole/key
創建應用 => 應用類型選瀏覽器 => IP白名单打 "*"

然後就能複製代碼在本機測試了
代碼:
<script src="http://api.map.baidu.com/api?v=2.0&ak=你的ak_key" type="text/javascript"></script>
<div id="allmap">
</div>
<script type="text/javascript">
 // 百度地图API功能
 var map = new BMap.Map("allmap");
 var point = new BMap.Point(116.331398,39.897445);
 map.centerAndZoom(point,12);
 // 创建地址解析器实例
 var myGeo = new BMap.Geocoder();
 // 将地址解析结果显示在地图上,并调整地图视野
 myGeo.getPoint("你的地址", function(point){
  if (point) {
   map.centerAndZoom(point, 16);
   map.addOverlay(new BMap.Marker(point));
  }
 }, "你的城市");
</script>

自己寫css調整地圖大小:
#allmap { width: 100px; height: 100px; }

实现多点标注及显示提示信息 (全國經銷商圖)
使用百度地圖工具產生
http://api.map.baidu.com/lbsapi/creatmap/
如果要看全中國地圖,定位中心点 => 当前城市 =>搜尋"全國"
添加标注 => 可以選擇標注的樣式、地點、說明(甚至你做出來後可以添加圖片, 如 http://blog.csdn.net/u013310075/article/details/24435869 上修改一些js可以辦到 )

如果要放在google blogger上,因為會被blogger的css影響到,要調一下CSS:
#dituContent #platform img { padding: 0; background: transparent; } /* #dituContent 是 百度地图容器 的div id*/
padding: 0; => 取消地圖上的白邊
background: transparent; => 讓標駐點陰影透明

實例:

同一個網頁有兩張地圖
百度地圖API不支援同一頁有兩個地圖,另一張可使用騰訊地圖 ( http://api.map.soso.com/doc/ )

參考資料:
http://blog.csdn.net/u013310075/article/details/24435869 百度地图之调用javaScript api接口实现多点标注及显示提示信息 (較舊)
http://api.map.baidu.com/lbsapi/creatmap/ 百度地圖API工具

php使用curl上傳檔案

起因:
在設計API時有上傳檔案的需求,上傳檔案必須用POST送,不能用GET... 怎麼辦呢?

Server 假設用php收資料,get_request.php:
echo "\$_REQUEST:";
print_r($_REQUEST);
echo "\$_FILES:";
print_r($_FILES);
echo "\$_POST:";
print_r($_POST);
echo "\$_GET:";
print_r($_GET);

使用Linux的curl:
GET:
$ curl "http://localhost/get_request.php?b=1"
結果:
$_REQUEST:Array
(
    [b] => 1
)
$_FILES:Array
(
)
$_POST:Array
(
)
$_GET:Array
(
    [b] => 1
)

POST:
$ curl --data "a=2&b=3" "http://localhost/get_request.php"
結果:
$_REQUEST:Array
(
    [a] => 2
    [b] => 3
)
$_FILES:Array
(
)
$_POST:Array
(
    [a] => 2
    [b] => 3
)
$_GET:Array
(
)

https連結
http://stackoverflow.com/questions/10079707/https-connection-using-curl-from-command-line
$ curl -k "https://whatever.com/script.php"

上傳檔案:
curl -F "file=@chrome.jpg;filename=nameinpost" -F "a=2" http://localhost/get_request.php?b=1
結果:
$_REQUEST:Array
(
    [b] => 1
    [a] => 2
)
$_FILES:Array
(
    [file] => Array
        (
            [name] => nameinpost
            [type] => image/jpeg
            [tmp_name] => /tmp/phpNKmLsv
            [error] => 0
            [size] => 4164
        )

)
$_POST:Array
(
    [a] => 2
)
$_GET:Array
(
    [b] => 1
)
註:
-F 指定檔案, 之後要post資料一樣要-F ,"file=@chrome.jpg;filename=nameinpost",filename= 可以指定新的上傳檔案名稱

ps. 如果遇到server有做 HTTP authentication ,用curl如何認證?
$ curl http://user:password@example.org/

$ curl -u user:password http://example.org/

使用php的curl函數,curl.php:
$toURL = "http://localhost/get_request.php?b=1";
$post = array(
  "a"=>"2",
  "userfile"=>"@chrome.jpg",
  //檔案若和程式在同一目錄或相對目錄, 可以用getcwd(), 如:
  // "userfile"=>"@".getcwd()."/oxox.doc",
  // 另外還可以在檔名後面加上分號指定mimetype(較新版的PHP才能使用)
  // (預設的 mimetype 為application/octet-stream)
  // "userfile"=>"@".getcwd()."\\somePic.png;type=image/png"
);
$ch = curl_init();
$options = array(
  CURLOPT_URL=>$toURL,
  CURLOPT_POST=>true,
  CURLOPT_POSTFIELDS=>$post, // 直接給array
);
curl_setopt_array($ch, $options);
curl_exec($ch);
curl_close($ch);
$ php curl.php
結果:
$_REQUEST:Array
(
    [bear] => 2
    [a] => 123
)
$_FILES:Array
(
    [userfile] => Array
        (
            [name] => chrome.jpg
            [type] => application/octet-stream
            [tmp_name] => /tmp/phpilTGG5
            [error] => 0
            [size] => 4164
        )

)
$_POST:Array
(
    [a] => 123
)
$_GET:Array
(
    [bear] => 2
)



參考資料:
http://stackoverflow.com/questions/14978411/http-post-and-get-using-curl-in-linux
http://evelynnote.blogspot.sg/2011/03/curl.html 更多的curl 指令用法
http://blog.roodo.com/esabear/archives/16358749.html




2015年1月21日 星期三

MySQL REPLACE INTO和INSERT INTO ... ON DUPLICATE KEY UPDATE區別


這兩種方式的作用是如果資料庫中存在記錄就更新,否則就插入新記錄,但是在使用上也是有一點區別的
  總結如下:
1. 如果表中不存在主鍵記錄,REPLACE和INSERT*UPDATE都與INSERT是一樣的特點。
2. 如果表中存在主鍵記錄,REPLACE相當於執行DELETEINSERT兩條操作,而INSERT*UPDATE的相當於執行if exist do update else do insert操作。因此,如果REPLACE填充的字段不全,則會導致未被更新的字段都會修改為默認值,並且如果有自增ID的話,自增ID會變化為最新的值(這樣如果是以自增ID為標誌的話可能導致記錄丟失);而INSERT*UPDATE只是更新部分字段,對於未被更新的字段不會變化(不會強制修改為默認值)。

測試:
建一張表:
-- 資料表結構 `user`
CREATE TABLE IF NOT EXISTS `user` (
`id` int(11) NOT NULL,
  `name` varchar(255) NOT NULL,
  `phone` varchar(255) NOT NULL,
  `update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;

-- 初始資料 `user`

INSERT INTO `user` (`id`, `name`, `phone`, `update_time`) VALUES
(1, 'bear', '12345', NOW()),
(2, 'ted', '54321', NOW());

-- 資料表索引 `user`
ALTER TABLE `user`
 ADD PRIMARY KEY (`id`), ADD UNIQUE KEY `phone` (`phone`);

使用replace into更新
mysql > replace into user(id, name) select 1, 'bear new_name';
結果:
+----+---------------+-------------+---------------------+
| id | name          | phone       | update_time         |
+----+---------------+-------------+---------------------+
|  1 | bear new_name |             | 2015-01-21 15:20:08 |
|  2 | ted           | 54321       | 2015-01-21 14:36:33 |
+----+---------------+-------------+---------------------+
沒填的欄位phone會變成預設值,會先執行delect再insert

使用INSERT INTO ... ON DUPLICATE KEY UPDATE更新
mysql > insert into user(id) select '2' on duplicate key update name='ted new_name';
結果:
+----+---------------+-------+---------------------+
| id | name          | phone | update_time         |
+----+---------------+-------+---------------------+
|  1 | bear new_name |       | 2015-01-21 15:20:08 |
|  2 | ted new_name  | 54321 | 2015-01-21 15:25:54 |
+----+---------------+-------+---------------------+
沒填的欄位不會被取代成預設值

使用INSERT INTO ... ON DUPLICATE KEY UPDATE新增資料
mysql > insert into user(id, name, phone) select NULL, 'wes', '56789' on duplicate key update name='wes new_name', phone='98765';
結果:
+----+---------------+-------+---------------------+
| id | name          | phone | update_time         |
+----+---------------+-------+---------------------+
|  1 | bear new_name |       | 2015-01-21 15:20:08 |
|  2 | ted new_name  | 54321 | 2015-01-21 15:25:54 |
|  3 | wes           | 56789 | 2015-01-21 15:28:55 |
+----+---------------+-------+---------------------+
新增的資料會是select裡面的資料而不是update後面的資料

參考資料:
http://www.blogjava.net/xylz/archive/2010/06/23/324242.html

2015年1月20日 星期二

gitlab 心得

使用Omnibus package embed安装的gitlab
https://about.gitlab.com/downloads/
路徑放在:
/var/opt/gitlab/
而非
/home/git/gitlab/ 下
重啟gitlab:
gitlab-ctl restart  #重啟動後刷頁要等一段時間,而非像apache馬上生效
而非
/etc/init.d/gitlab restart

設定檔在:
/var/opt/gitlab/gitlab-shell/config.yml

log檔在:
tail -f /var/log/gitlab/nginx/gitlab_error.log
tail -f /var/log/gitlab/nginx/gitlab_access.log

查gitlab版本
http://yourdomain/help

git push錯誤:(這是突發事件,之前用正常)
$ git push -u origin master
Agent admitted failure to sign using the key.
Access denied.
fatal: Could not read from remote repository.

Please make sure you have the correct access rights
and the repository exists.
修改config.yml的
gitlab_url: "http://domain" # "http://domain:8080(/)", "http://wlan_ip(/)", "http://wlan_ip:8080(/)", "http://localhost:8080(/)", "http://localhost(/)"
self_signed_cert: false => true
都無解

暴力解法:
 /var/opt/gitlab/git-data/repositories/username
因為這邊在gitlab GUI介面沒開project-new.git資料夾出來,所以推上去找不到地方存
/var/opt/gitlab/git-data/repositories/bear# ls
project-old.git    project-old.wiki.git    project-new.wiki.git   ( 沒開project-new.git )

隨便cp一個舊的project,然後把使用者改git:git,權限改777(不改777可以拉下來但推上去gitlab會沒紀錄)
# cp -r project-old.git project-new.git
# chown git:git -R project-new.git
# chmod 777 -R project-new.git
這樣在http://domain/username/project-new/commits/master 下就可以看到log了,只是會帶舊的log在裡面,我試過開新的project-new然後git init --bare,ln -s hooks /opt/gitlab/embedded/service/gitlab-shell/hooks ,但無效,gitlab看不到專案

....過了幾個小時候,又可以正常用了,看來是偶發事件....不確定跟我中間reboot重啟機契友沒有關係

更改gitlab後台repo的網址
將gitlab後台網址
git@li8xx-xxx.members.linode.com:root/test.git
改成
git@your.domain:root/test.git
修改 /opt/gitlab/embedded/service/gitlab-rails/config/gitlab.yml
host: li8xx-xxx.members.linode.com => your.domain
重啟gitlab
# gitlab-ctl restart



2015年1月19日 星期一

git合併兩個現有repos的紀錄到同一個repos

假設要把mp3合併到mp2(主要)
下載兩個repos並進入其中主要的branch
$ git clone git@your.domain:bear/mp2.git
$ git clone git@your.domain:bear/mp3.git
$ cd mp2
把mp3加入mp2的remote
$ git remote add mp3 ../mp3
$ git fetch mp3
$ git branch mp3 mp3/master
$ git merge mp3
合併後的git log圖如下:
*   0f54c78 2015-01-19 | fix conflict merge [bear]
|\
| * dc09a26 2015-01-19 | add mp3.txt (mp3/master, mp3) [bear]
| * d6b0993 2015-01-19 | mp3 test commit 2 [bear]
| * 9d89949 2015-01-19 | mp3 first commit [bear]
*   4fea708 2014-11-23 | Merge branch 'master' of your.domain:bear/mp2 (origin/master, origin/HEAD) [b
|\
| * fdc6ced 2014-11-23 | gitlab [bear]
* 719d8ef 2014-11-23 | gitlab [bear]
* a66d115 2014-11-23 | Initial commit [kalecgos0616]
( ~/.bashrc:設定 alias glog='git log --pretty=format:"%h %ad | %s%d [%an]" --graph --date=short')
要重新回到mp2 被merge前的 commit( 4fea708 ) 並保留log,因為 0f54c78 中混合了4fea708 和 dc09a26 的code,有衝突的檔案要手動解決conflict,要回到最原本的4fea708
方法1. ( 會多3個revert的commit )
$ git revert dc09a26 d6b0993 9d89949
方法2. ( 只會多一個commit,並回到 4fea708 的狀態)
$ git revert -m 1 0f54c78

下面這方法試不出來,可能是我的0f54c78 是兩個repos merge的node
$ git revert HEAD~2..HEAD

ps. 把本地端某個branch推到gitlab上
$ git checkout -b branch_name remote_name/branch_name
$ git push -u origin branch_name

參考資料:
http://stackoverflow.com/questions/4114095/revert-to-a-previous-git-commit
http://blog.caplin.com/2013/09/18/merging-two-git-repositories/
http://stackoverflow.com/questions/2765421/push-a-new-local-branch-to-a-remote-git-repo-and-track-it-too

2015年1月16日 星期五

git 搬移log到svn下

需求:
git下的所有log資料搬到svn 某新資料夾下(svn其他資料夾已有log)

過程照參考資料做基本上就可以了...
1. Create Subversion-aware Git clone ( 首先你要安裝git-sv )
 $ git svn clone http://svn.localhost/svn/trunk/company
 $ cd trunk
 $ git fetch git@your.domain:username/project.git # 我是抓我gitlab上的git

 $ git branch tmp $(cut -b-40 .git/FETCH_HEAD)
$(cut -b-40 .git/FETCH_HEAD) 是原本gitlab最後一個commit id
 $ git tag -a -m "Last fetch" last tmp

2. Apply initial commit
 $ INIT_COMMIT=$(git log tmp --pretty=format:%H | tail -1)
$(git log tmp --pretty=format:%H | tail -1)是tmp branch log的最後一個 commit id
 $ git checkout $INIT_COMMIT .  #注意最後要有點(.)
 $ git commit -C $INIT_COMMIT

3. Rebase and submit
 $ git rebase master tmp
 $ git branch -M tmp master

 $ git svn dcommit

4. Update Google Code
 ...
 $ mv .git/refs/tags/newlast .git/refs/tags/last #這步我沒做



參考資料:
http://code.google.com/p/support/wiki/ImportingFromGit

2015年1月13日 星期二

diff 兩個資料夾的某些內容並上色

目標:
使用diff像git diff比較兩資料夾下的所有檔案,排除某些資料夾,並上色

1. 使用git diff
http://stackoverflow.com/questions/5751567/git-diff-branch-name-status-ignore
$ git diff -w --name-only app/ ../cakephp-84/app/ | grep -v 'app/tmp/' | xargs git diff app/
fatal: ../cakephp-84/app/Controller/UsersController.php: '../cakephp-84/app/Controller/UsersController.php' is outside repository
原因:cakephp-84不在同一個git repos中,stackoverflow的解法似乎是比同個git下不同branch的東西

注意:git diff 必須在有.git的資料夾下才能使用,但如果只是簡單的比較不限定在該git下的檔案

2. 使用colordiff
安裝colordiff:
$ sudo pacman -S colordiff
使用:
$ diff -rw --exclude="tmp" --exclude="webroot" cakephp/app cakephp-84/app | colordiff | less -R
--exclude => 可以連續使用,不比較某些資料夾下的檔案
-r => 遞迴,連子資料夾一起比
-w => 不比較空白
colordiff => 也可以把最前面的diff直接換成colordiff,這樣後面就不用pipe line 去加colordiff
less -R => 直接用less配合colordiff會有亂碼,用這招移除ANSI編碼並轉成顏色

參考資料:



2015年1月12日 星期一

arch 安裝memcached和php-memcached、php-memcache

安裝系統memcached
$ sudo pacman -S memcached
開機時啟用memcached
# systemctl enable memcached.service
啟動memcached
# systemctl start memcached.service
檢查memcached有沒有跑
# ps aux | grep memcached

# netstat -tap | grep memcached (如果用netstat -ant的話就看預設的port 11211 有沒有開,但是port可以改)
檢查執行中的設定值
$ echo "stats settings" | nc localhost 11211
STAT maxbytes 67108864 #預設最小的64MB
STAT maxconns 1024
STAT tcpport 11211
STAT udpport 11211
STAT inter 127.0.0.1
STAT verbosity 0
STAT oldest 0
STAT evictions on
STAT domain_socket NULL
STAT umask 700
STAT growth_factor 1.25
STAT chunk_size 48
STAT num_threads 4 #預設4個threads
STAT num_threads_per_udp 4
STAT stat_key_prefix :
STAT detail_enabled no
STAT reqs_per_event 20
STAT cas_enabled yes
STAT tcp_backlog 1024
STAT binding_protocol auto-negotiate
STAT auth_enabled_sasl no
STAT item_size_max 1048576
STAT maxconns_fast no
STAT hashpower_init 0
STAT slab_reassign no
STAT slab_automove 0
END
更改memcache設定值:
# vim /etc/conf.d/memcached (注意:2013/5後,裝好memcache,arch不再有這個檔案,要自己建)
# user to run memcached as; also used for pid file ownership
MEMCACHED_USER="memcached"
# see 'memcached -h' for available options
MEMCACHED_ARGS="-m 1024 -l 127.0.0.1 -t 1"
-t 1 =>number of threads to use
-l 127.0.0.1 =>  listen to localhost
預設的port是11211( -p 11211 是禁止的)
-m 1024 => 1024MB RAM分給Memcached(-m 64是禁止的),64MB是最小值
編輯/usr/lib/systemd/system/memcached.service:
 [Unit]
 Description=Memcached Daemon
 After=network.target
 [Service]
-EnvironmentFile=/etc/conf.d/memcached
-ExecStart=/usr/bin/memcached -u $MEMCACHED_USER $MEMCACHED_ARGS
+User=memcached
+# Remove '-l 127.0.0.1' to listen on all addresses
+ExecStart=/usr/bin/memcached -l 127.0.0.1
+Restart=always

 [Install]
 WantedBy=multi-user.target
重啟memcached
# systemctl restart memcached
然後再度檢查執行中的設定值:
STAT maxbytes 1073741824 #更改為1024MB
STAT maxconns 1024
STAT tcpport 11211
STAT udpport 11211
STAT inter 127.0.0.1
STAT verbosity 0
STAT oldest 0
STAT evictions on
STAT domain_socket NULL
STAT umask 700
STAT growth_factor 1.25
STAT chunk_size 48
STAT num_threads 1 #更改為1個threads
STAT num_threads_per_udp 1
STAT stat_key_prefix :
STAT detail_enabled no
STAT reqs_per_event 20
STAT cas_enabled yes
STAT tcp_backlog 1024
STAT binding_protocol auto-negotiate
STAT auth_enabled_sasl no
STAT item_size_max 1048576
STAT maxconns_fast no
STAT hashpower_init 0
STAT slab_reassign no
STAT slab_automove 0
END

安裝php memcached 擴展
我曾想用 pacman -S php-memcache 和 pacman -S php-memcached,來安裝,但是就算更改memcache.ini、memcached.ini後在phpinfo()裡面都沒看到memcached載入。

最後使用pecl來安裝
# pecl install memcache
# pecl install memcached
然後把/etc/php/conf.d 下的memcache.ini 、 memcached.ini 註解拿掉,
memcache.ini:
extension=memcache.so
memcached.ini:
extension=memcached.so

如果ubuntu使用apt-get安裝php5-memcache、php5-memcached
# apt-get install php5-memcache php5-memcached
且web server跑的是nginx
先去 /etc/php5/fpm/conf.d 檢查 memcached.ini  memcache.ini
重啟fpm(如果沒有重啟php5-fpm,phpinfo()是不會新增memcache和memcached的擴展的)
# service php5-fpm restart
重啟nginx
# service nginx restart

寫一段程式測試:
$mc = new Memcached();
$mc->addServer("127.0.0.1", 11211);
 
$result = $mc->get("test_key");
 
if($result) {
  echo $result;
} else {
  echo "No data on Cache. Please refresh page pressing F5";
  $mc->set("test_key", "test data pulled from Cache!") or die ("Failed to save data at Memcached server");
}
# php memcache.php
No data on Cache. Please refresh page pressing F5
# php memcache.php
test data pulled from Cache!

批量刪除key特定開頭的緩存

https://stackoverflow.com/a/25168042  memcache and wildcards
新增一個deleteKeysByIndex.php檔案

function deleteKeysByIndex($search) {
    $m = new Memcached();
    $m->addServer('127.0.0.1', 11211); // 127.0.0.1 緩存server IP
    $keys = $m->getAllKeys();
    foreach ($keys as $index => $key) {
        if (strpos($key,$search) !== false) {
            $m->delete($key);
        } else {
            unset($keys[$index]);
        }
    }

    // returns an array of keys which were deleted
    return $keys;
}

$keys = deleteKeysByIndex("CacheKey-User-"); // 刪除 CacheKey-User-開頭的緩存。如:CacheKey-User-1、CacheKey-User-2 ...
print_r($keys);  // 輸出被刪除的key
用法:
$ php deleteKeysByIndex.php

apache錯誤:Request exceeded the limit of 10 internal redirects due to probable configuration error.

主機:CentOS

讀取網址 http://localhost
頁面出現錯誤:
500 Internal Server Error
apache error log:
[Mon Jan 12 12:04:40 2015] [error] [client x.x.x.x] Request exceeded the limit of 10 internal redirects due to probable configuration error. Use 'LimitInternalRecursion' to increase the limit if necessary. Use 'LogLevel debug' to get a backtrace.

但是讀取
http://localhost/index.php、http://localhost/index.html、http://localhost/test.php
正常

原因:
httpd.conf 開了Rewrite( LoadModule rewrite_module modules/mod_rewrite.so
 )

<Directory "/var/www/html">裡面的AllowOverride 被改成All
解法:
把<Directory "/var/www/html">裡面的 AllowOverride 改成None

2015年1月9日 星期五

PHP常見問題

取某段字串裡的數字
preg_match_all('!\d+!', $str, $matches);
取某段字串裡的日期 (我們假設日期格式是 YYYY-MM-DD HH-ii-ss)
preg_match('/^\d{4}-\d{2}-\d{2}\s\d{2}:\d{2}:\d{2}/', $str, $matches);
去除前後的空白
$ php -r '$str = "  hello world  "; echo trim($str);'
hello world
參考資料:
http://stackoverflow.com/questions/6278296/extract-numbers-from-a-string

去除陣列所有字串前後的空白
http://stackoverflow.com/questions/6769464/how-can-i-trim-all-strings-in-an-array
使用array_map()
$source_array = array(" hey ", "bla ", " test");
$result = array_map('trim', $source_array); // $result = array("hey", "bla", "test");


開發時在網頁上顯示錯誤
編輯/etc/php5/apache2/php.ini 修改以下設定
error_reporting = E_ALL
display_errors = On
( nginx+fpm )
If you have /etc/php5/fpm/php.ini (used in Debian, Ubuntu style config) then changes to this file should take effect, and that configuration may be further overridden per pool by making changes to specific /etc/php5/fpm/pool.d/*.conf files.
如果你有 /etc/php5/fpm/php.ini (Debian, ubuntu) ,則編輯修改以下設定即可(採用)
; enable display of errors
display_errors = On
display_startup_errors = On
而該設定會被 /etc/php5/fpm/pool.d/*.conf 的設定覆蓋,例 ( /etc/php5/fpm/pool.d/www.conf ):
; enable display of errors
php_flag[display_errors] = on
php_flag[display_startup_errors] = on


註:
; Production Value: E_ALL & ~E_DEPRECATED & ~E_STRICT =>產品上線用
; Development Value: E_ALL => 開發用
參考資料:
http://stackoverflow.com/questions/5680831/php-does-not-display-error-messages
http://stackoverflow.com/questions/13929314/display-errors-for-php5-fpm-not-working-with-nginx  display_errors for php5-fpm not working with nginx

在PHP腳本中修改錯誤輸出的級別
error_reporting(0); // Turn off all error reporting
error_reporting(E_ALL ^ E_NOTICE); // Report all errors except E_NOTICE, This is the default value set in php.ini
http://stackoverflow.com/questions/2867057/how-do-i-turn-off-php-notices


file_put_contents的LOCK_EX 參數
在php.net中,file_put_contens的描述是
int file_put_contents ( string $filename , mixed $data [, int $flags = 0 [, resource $context ]] )
而地三個參數$flags常用的有:
FILE_APPEND=>直接在該檔已有的內容後面追加內容
LOCK_EX=>寫檔的時候先鎖定,防止多人同時寫入造成內容丟失
當需要頻繁操作file_put_contents函數寫txt檔時,經常出現前半截內容缺失的情況,非常苦惱。此時LOCK_EX非常有用,加上它之後,再也沒有出現過內容缺失的情況了。LOCK_EX的意思很直白,就是寫檔時,先鎖上這個檔,這樣只允許某個客戶端訪問的時候寫,其他客戶端訪問不能寫了。
http://www.wyxuan.com/184.html  PHP写文件函数file_put_contents确实给力


unlink()函數
unlink() 函数删除文件。若成功,则返回 true,失败则返回 false。
Ex.
$ touch test2
$ ls
smarty  test  test2
$ php -r 'unlink("test2");'
$ ls
smarty  test

php的refrence符号&用法
& 可以當二位元AND運算也可以當reference,以下為reference的例子
$arr = array(1, 2, 3);
function a(&$arr2){
 foreach ($arr2 as &$value) {
  $value = $value*$value;
 }
 echo '$arr2:';
 print_r($arr2);
 // print_r($arr); // Notice: Undefined variable: arr
}
a($arr);
echo '<br>$arr:';
print_r($arr);
// print_r($arr2); // Notice: Undefined variable: arr2
// a(&$arr); // Fatal error: Call-time pass-by-reference has been removed => And as of PHP 5.4.0( PHP Version 5.6.8 on my server ), call-time pass-by-reference was removed, so using it will raise a fatal error.

輸出:
$arr2:Array ( [0] => 1 [1] => 4 [2] => 9 )
$arr:Array ( [0] => 1 [1] => 4 [2] => 9 )

如果a(&$arr)這樣呼叫,在PHP5.3.0會產生警告,在PHP5.4.0+會產生fatal error.

以純變數為例子:
$a='小妹哥';
$b=$a;
$c=&$a;
$b='大妹哥';
$c='拉屎小妹哥';
echo $a;
輸出:
拉屎小妹哥

傳值呼叫(call By Value) - 將變數的值傳入函數,並不會更變原來的值
傳址呼叫(call By Reference)將變數實際儲存的位址傳入,在函數變更參數值,也會同時更變傳入的變數值

參考資料:
http://stackoverflow.com/questions/3737139/reference-what-does-this-symbol-mean-in-php Reference - What does this symbol mean in PHP?
http://www.php.net/manual/en/language.references.php References Explained
http://php.net/manual/en/language.references.pass.php Passing by Reference
http://syunguo.blogspot.com/2013/04/php_1.html  [PHP]傳址與傳值呼叫

array_chunk()
http://w3school.com.cn/php/func_array_chunk.asp
把array分割,chunk=>大塊 的意思
Ex.
preserve_key = true - 保留原始数组中的键名
$ php -r '$a=array("a"=>"Cat","b"=>"Dog","c"=>"Horse"); print_r(array_chunk($a,2,true));'
Array
(
    [0] => Array
        (
            [a] => Cat
            [b] => Dog
        )

    [1] => Array
        (
            [c] => Horse
        )

)

$ php -r '$a=array("a"=>"Cat","b"=>"Dog","c"=>"Horse","d"=>"Cow"); print_r(array_chunk($a,2));'
Array
(
    [0] => Array
        (
            [0] => Cat
            [1] => Dog
        )

    [1] => Array
        (
            [0] => Horse
            [1] => Cow
        )

)

array_slice()
http://www.w3school.com.cn/php/func_array_slice.asp
陣列中根據條件取出一段值,並返回
Ex.
從第二個開始往後取兩個
$ php -r '$a=array("e"=>"Cat","b"=>"Dog","c"=>"Horse","d"=>"Cow"); print_r(array_slice($a,1,2));'
Array
(
    [b] => Dog
    [c] => Horse
)
從倒數第二個(與key的升降冪無關)開始往後取兩個
$ php -r '$a=array(5=>"Cat",1=>"Dog",2=>"Horse",3=>"Cow"); print_r(array_slice($a,-2,2));'
Array
(
    [0] => Horse
    [1] => Cow
)
preserve = true,數字型key保留鍵值
$ php -r '$a=array(5=>"Cat",1=>"Dog",2=>"Horse",3=>"Cow"); print_r(array_slice($a,-2,2,true));'
Array
(
    [2] => Horse
    [3] => Cow
)

字串轉unicdoe,unicode轉字串
http://stackoverflow.com/questions/7106470/utf-8-to-unicode-code-points
function unicode2utf8($str){
    if(!$str) return $str;
    $decode = json_decode($str);
    if($decode) return $decode;
    $str = '["' . $str . '"]';
    $decode = json_decode($str);
    if(count($decode) == 1){
            return $decode[0];
    }
    return $str;
}
echo json_encode('測試'); // \u6e2c\u8a66

echo "
unicode2utf8('\u6e2c\u8a66'):".unicode2utf8('\u6e2c\u8a66'); // 測試
s

快速檢查必填欄位有無缺失(無法判斷是缺哪個欄位)
http://stackoverflow.com/questions/13169588/how-to-check-if-multiple-array-keys-exists
//The values in this arrays contains the names of the indexes (keys) that should exist in the data array
$required = array('key1', 'key2', 'key3' );

$data = array(
    'key1' => 10,
    'key2' => 20,
    'key3' => 30,
    'key4' => 40
);

if(count(array_intersect_key(array_flip($required), $data)) === count($required)) {
    //All required keys exist!              
}

過濾掉不合法的欄位
http://stackoverflow.com/questions/4260086/php-how-to-use-array-filter-to-filter-array-keys
$my_array = array("foo" => 1, "hello" => "world");
$allowed = array("foo", "bar");
$inserted_data = array_intersect_key($my_array, array_flip($allowed)); // array("foo" => 1);

計算生肖
https://en.wikipedia.org/wiki/Chinese_zodiac
必須把每年除夕的日子存起來,再另外處理。最後我只用國曆做簡單的判斷

PHP多維陣列搜尋(由值找key)
http://stackoverflow.com/questions/8102221/php-multidimensional-array-searching-find-key-by-specific-value
function search($columns, $field, $value){
    foreach ($columns as $key => $column) {
        if ($column[$field] === $value) {
            return $key;
        }
    }
    return false;
}

$products = array (
1  => array(
        'shortname'     => 'The One-Touch Tea Maker',
        'price'         => '249.99',
        ),

2  => array(
        'shortname'     => 'Variable Temperature Kettle',
        'price'         => '129.99',
        ),
);

$key = search($products, 'shortname', 'Variable Temperature Kettle');
echo "\$key:".$key;
輸出:
$key:2
(找$porducts 中 shortname為 Variable Temperature Kettle 的key值(通常為id))

備份上傳的檔案(框架以ThinkPHP為例)
http://www.w3school.com.cn/php/func_filesystem_copy.asp
$project_path =pathinfo(THINK_PATH, PATHINFO_DIRNAME);
$import_path = $project_path."/Uploads/import";
$file_name = "import-".microtime(true)."-".$_FILES['excel']['name'];
$file_full_path = $import_path."/".$file_name;
copy($_FILES['excel']['tmp_name'],$file_full_path);
注意:
input[type=file]的name記得要寫$_FILES['excel']['tmp_name'],不然抓不到檔案會複製不出結果

從陣列的值求其key
http://stackoverflow.com/questions/8729410/php-get-key-name-of-array-value
$arr = array ('first' => 'a', 'second' => 'b', );
$key = array_search ('a', $arr); // $key = 'first'
array_search()可以從值反查key

避免PHP Deprecated:  preg_replace(): The /e modifier is deprecated, use preg_replace_callback instead in xxx.php on line xx錯誤
http://stackoverflow.com/questions/2082207/calling-function-inside-preg-replace-thats-inside-a-function  calling function inside preg_replace thats inside a function
http://stackoverflow.com/questions/21334934/deprecated-preg-replace-the-e-modifier-is-deprecated-use-preg-replace-call  Deprecated: preg_replace(): The /e modifier is deprecated, use preg_replace_callback instead in
http://stackoverflow.com/questions/20951133/deprecated-preg-replace-how-to-convert-to-preg-replace-callback  Deprecated: preg_replace(): How to convert to preg_replace_callback?
直接看程式
error_reporting(E_ALL);

function embed_video($url) {
 echo "\n\$url:".$url;
    return $url;
}

$result = "[video]http://youtube.com/id/xxxxxxpron[/video]";
$result = preg_replace(
    "/\[video\](.+?)\[\/video\]/e",
    "embed_video('$1')",
    $result
);
echo "\n\$result:" . $result."\n";

$result2 = "[video]http://youtube.com/id/xxxxxxpron[/video]";
$result2 = preg_replace_callback(
    "/\[video\](.+?)\[\/video\]/",
    function($matches) {
     print_r($matches);
        return embed_video($matches[1]);
    },
    $result2
);
echo "\n\$result2:" . $result2;
結果:
PHP Deprecated:  preg_replace(): The /e modifier is deprecated, use preg_replace_callback instead in xx.php on line 14
$url:http://youtube.com/id/xxxxxxpron
$result:http://youtube.com/id/xxxxxxpron
Array
(
    [0] => [video]http://youtube.com/id/xxxxxxpron[/video]
    [1] => http://youtube.com/id/xxxxxxpron
)
$url:http://youtube.com/id/xxxxxxpron
$result2:http://youtube.com/id/xxxxxxpron

注意:
改成preg_replace_callback後,第一個參數 $pattern ( "/\[video\](.+?)\[\/video\]/e" ),必須把 替換操作符 的 /e ( 替換字符串作為表達式 ) 拿掉,否則會報錯
PHP Warning:  preg_replace_callback(): Modifier /e cannot be used with replacement callback in xx.php on line xx
Don't use the \e modifier in your preg_replace_callback() call or php will throw the following warning and return nothing
不要使用 modifier 在 preg_replace_callback() 呼叫,否則php會丟警告且返回空值

較簡單的範例
error_reporting(E_ALL);

$x = 'abcd-efg-hijk-lmnop';

$x = preg_replace(
    '/-(.)/e', //pattern
    "strtoupper('$1')",
    $x //subject
);

echo "\n\$x:" . $x . "\n"; //abcdEfgHijkLmnop

$x = 'abcd-efg-hijk-lmnop';

$x = preg_replace_callback(
    '/-(.)/', //pattern
    function ($matches) {
        //callback
        print_r($matches);
        return strtoupper($matches[1]);
    },
    $x //subject
);

echo "\n\$x:" . $x; //abcdEfgHijkLmnop
結果:
PHP Deprecated:  preg_replace(): The /e modifier is deprecated, use preg_replace_callback instead in xx.php on line 10

$x:abcdEfgHijkLmnop
Array
(
    [0] => -e
    [1] => e
)
Array
(
    [0] => -h
    [1] => h
)
Array
(
    [0] => -l
    [1] => l
)

$x:abcdEfgHijkLmnop

避免PHP Strict Standards:  Only variables should be passed by reference in strict_standards.php on line xx錯誤
http://stackoverflow.com/questions/9848295/strict-standards-only-variables-should-be-passed-by-reference-error  “Strict Standards: Only variables should be passed by reference” error
直接看程式
error_reporting(E_ALL);
$value = "a.b.c.d";
$extension = strtolower(array_pop(explode(".", $value)));
print_r($extension);
結果:
PHP Strict Standards:  Only variables should be passed by reference in /home/ds/strict_standards.php on line 6
d

解法:
把explode()放到外面處理,多一行
error_reporting(E_ALL);
$value = "a.b.c.d";
$explodes = explode(".", $value);
$extension = strtolower(array_pop($explodes));
print_r($extension);
結果:
d
( PHP Strict Standards 錯誤沒了 )

檢查class有無方法和在一行中if兩次
http://stackoverflow.com/questions/10287789/check-if-class-has-method-in-php
$where = (method_exists($Model, "get_where")) ? $Model->get_where() : ( (isset($this->_map['where']))? $this->_map['where'] : null );
1. 使用 method_exists($Class, "method_name") 檢查
2. 在一行中使用兩次if:$result = ( condition1 ) ? a : ( ( condition2 )? b : c );

得到陣列中最後一個key
http://stackoverflow.com/questions/2348205/how-to-get-last-key-in-an-array
$array = array(
    'first' => 123,
    'second' => 456,
    'last' => 789, 
);
end($array);         // move the internal pointer to the end of the array
$key = key($array);  // fetches the key of the element pointed to by the internal pointer
echo $key; // last, 但$array的順序是不變的

使用sprintf連結字串
http://www.w3school.com.cn/php/func_string_sprintf.asp
喇叭王:要組合字串都改用sprintf,考量之後維護的人的關係。今天要是這字串比較長 超過一個畫面 你在維護的時候怎麼知道裡面有幾個變數 你能保證不會看漏嗎
例:
$a = "Daniel";
$b = "Lin";
$c = "Laba King";
$text = sprintf("%s %s, %s",
    $a,
    $b,
    $c
);
echo $text."\n";
$text2 = $a." ".
    $b.", ".
    $c;

echo $text2;
結果:
Daniel Lin, Laba King
Daniel Lin, Laba King
結果是一樣的,看起來用sprintf()好像比較好

XML字串轉陣列
http://stackoverflow.com/questions/12148662/xml-to-array-php
使用json_decode(json_encode(simplexml_load_string($xml)),TRUE)
$s =<<<EOS
<root>
<Formula>
<formulaname>Basic</formulaname>
<movespeed>1</movespeed>
<box>4</box>
<chicken>3</chicken>
<ducks>1</ducks>
<cereal>2</cereal>
</Formula>
</root>
EOS;
$array = json_decode(json_encode(simplexml_load_string($s)),TRUE);
echo "<pre>\$array:";
print_r($array);
echo "</pre>";
結果:
$array:Array
(
    [Formula] => Array
        (
            [formulaname] => Basic
            [movespeed] => 1
            [box] => 4
            [chicken] => 3
            [ducks] => 1
            [cereal] => 2
        )
)

in_array() 的BUG
https://stackoverflow.com/questions/7669589/in-array-does-not-work-as-expected
in_array('string', [1,0])  // true,因為 0 == "string"true
in_array('string', [1,0], true)  // false. working as expected

兩個變數中的&
https://stackoverflow.com/questions/22376222/what-does-a-single-ampersand-operator-do-in-php
What does a single ampersand ('&') operator do in PHP?
這是bitwise operator AND
ex.
$a =     9;
$b =     10;
echo $a & $b;

place value   128  64  32  16   8  4   2   1
$a                     0   0    0    0    1   0   0   1   =9
$b                     0   0    0    0    1   0   1   0   =10

result   8

使用microtime(true)計算php程序代碼執行消耗時間
https://blog.csdn.net/eflyq/article/details/19130141
https://stackoverflow.com/questions/33107182/php-microtime-is-correct
$start = microtime(true);
// code
$time_elapsed_secs = microtime(true) - $start; // code執行了多少「秒」


laravel判斷model的特定欄位是否被更改

https://stackoverflow.com/a/28866535  Laravel Eloquent update just if changes have been made
https://stackoverflow.com/a/40544518  How to search if the key value in an array exists in another array, using PHP?

$dirty = $user->getDirty();
$columns = [
    'phone', 'address',
];
if (array_intersect(array_keys($dirty), $columns)){
    // 用戶電話或地址已被更改
}
s








php的svn_log()速度過慢

目標:
在網頁上顯示svn log

使用php的svn_log()函數
php官方文件的svn_log():
array svn_log ( string $repos_url [, int $start_revision [, int $end_revision [, int $limit= 0 [, int $flags = SVN_DISCOVER_CHANGED_PATHS | SVN_STOP_ON_COPY ]]]] )
$start_revision 、$end_revision和$limit  設0 代表無限制
$flags:
SVN_DISCOVER_CHANGED_PATHS - 顯示所有值(預設)
SVN_OMIT_MESSAGES - 不顯示msg和paths
SVN_STOP_ON_COPY - 不顯示paths

例:
svn_auth_set_parameter(SVN_AUTH_PARAM_DEFAULT_USERNAME, 'username');
svn_auth_set_parameter(SVN_AUTH_PARAM_DEFAULT_PASSWORD, 'password');
$log = svn_log('http://svn.xxx/repos/trunk', 1, 2, 1);
OUTPUT:
$log:Array
(
    [0] => Array
        (
            [rev] => 1
            [author] => root
            [msg] => init
            [date] => 2014-10-29T04:13:15.632694Z
            [paths] => Array
                (
                    [0] => Array
                        (
                            [action] => A
                            [path] => /branches
                        )
                )
        )
)


因為某些commit提交檔案太多,即使調整$flags,svn_log()速度依然很慢
只好使用原生的svn log來在網頁上顯示紀錄
PHP:
exec("export LANG=zh_TW.UTF8; svn log file:///path/to/svndb/repos/ ", $svn_log);
注意:
export LANG=zh_TW.UTF8; 很重要,否則出來會是亂碼,我用system("export")看不到LANG的設定,而且export LANG=zh_TW.UTF8;和 svn log 必須寫在同一個exec()中

然後細節在處理$svn_log陣列即可顯示到網頁上

參考資料:
http://php.net/manual/en/function.svn-log.php

2015年1月7日 星期三

svn merge log的測試shell script

承前篇 Arch啟用http存取svn和svn搬移log到另一個repos下
後來寫了個建構環境和merge log的script:
建立該篇文章的環境:
用法:
$ bash create_env.sh /home/svn/ #在 /home/svn/ 下建立test和test2的repos,並建立那篇文章的資料結構
#!/bin/bash
# $ bash create_env.sh 
# eg. # bash create_env.sh /home/svn/

SVN_REPOS_PATH=$1
cd ${SVN_REPOS_PATH}
svnadmin create test
svnadmin create test2

# cook test repos
svn checkout file://${SVN_REPOS_PATH}test test_checkout
cd test_checkout
mkdir trunk tags branches
touch trunk/hello.txt
svn add *
svn commit -m "hello test"
svn update
svn log
cd ..

# cook test2 repos
svn checkout file://${SVN_REPOS_PATH}test2 test2_checkout
cd test2_checkout
mkdir trunk tags branches
mkdir -p trunk/media/cloud/web/internet
svn add *
svn commit -m "hello test2"
svn update
svn log
cd ..

rm -rf test_checkout test2_checkout
ls -l

merge log:
用法:
# bash test_merge_log.sh /home/svn/test /home/svn/test2 trunk/media/cloud/web/internet # 把test的log搬到test2的 trunk/media/cloud/web/internet 資料夾下
#!/bin/bash
# merge REPOS1 log to REPOS2 folder
# run it as root
# # bash test_merge_log.sh   
# eg. # bash test_merge_log.sh /home/svn/test /home/svn/test2 trunk/media/cloud/web/internet

SVN_REPOS1_PATH=$1
SVN_REPOS2_PATH=$2
REPOS2_FOLDER=$3

svnadmin dump ${SVN_REPOS1_PATH} > test.dump
cat test.dump | svndumpfilter include "trunk" --drop-empty-revs --renumber-revs > test_trunk.dump
find . -name test_trunk.dump | xargs perl -ne 'open NEW, ">>test_trunk_new.dump"; s/trunk\///g; print NEW'
svnadmin load ${SVN_REPOS2_PATH} --parent-dir ${REPOS2_FOLDER} --ignore-uuid < test_trunk_new.dump
rm *.dump

# check
svn checkout file://${SVN_REPOS2_PATH} test2_checkout
cd test2_checkout
svn log
cd ..
rm -rf test2_checkout

原始碼:
https://github.com/kalecgos0616/tools/tree/master/svn

注意:
要在該tty 也cd到資料夾要用 . xxx.sh (要多一個點),否則用bash xxx.sh不會換目錄,但仍然會在該目錄執行指令

The reason is that each process has its own current directory, and when you execute a program from the shell it is run in a new process. The standard "cd", "pushd" and "popd" are builtin to the shell interpreter so that they affect the shell process.
翻譯:
每個process 擁有自己的資料夾,當你執行一個shell程式時等於開了一個新的process。"cd"那些指令被內置到shell解釋器,仍影響shell進程。使用source(.) 會讓該process在現在的tty環境執行

參考資料:
http://stackoverflow.com/questions/874452/change-current-directory-from-a-script

2015年1月6日 星期二

Arch啟用http存取svn和svn搬移log到另一個repos下

系統:
Arch Linux
svn, version 1.8.9+
Apache/2.4.10

目標:
1. 使用http存取svn
2. 搬移一個svn repos的log到另一個repos下

使用http存取svn
我們假設現有的svn倉庫放在這裡:
/home/svn/test
1. 編輯 /etc/httpd/conf/httpd.conf,要注意順序
LoadModule dav_module           modules/mod_dav.so # 取消註解
LoadModule dav_fs_module        modules/mod_dav_fs.so # 取消註解
LoadModule dav_svn_module       modules/mod_dav_svn.so # 新增這行
LoadModule authz_svn_module     modules/mod_authz_svn.so # 新增這行

使用SSL? => 不使用,所以後面設定檔設在vhost中

新建 /home/svn/.svn-policy-file:
[/]
* = r
[REPO_NAME:/]
USER_NAME = rw

新建 /home/svn/.svn-auth-file:
# htpasswd -cs /home/svn/.svn-auth-file USER_NAME #沒有檔案時,新建第一個user
# htpasswd -s /home/svn/.svn-auth-file OTHER_USER_NAME  #在檔案後面新建其他的user

設定權限:
# chown -R http:http /home/svn/test
編輯 /etc/httpd/conf/sites-available/svn.localhost.conf:
(注意:這邊我是設在vhost設定檔中、網路上有些設在httpd-ssl.conf、mod_dav_svn.conf中)
<VirtualHost *:80>
    DocumentRoot "/srv/http/" 必須設不同於svn repos的位置否則出現錯誤:{
Redirecting to URL 'http://svn.localhost/test':
Redirecting to URL 'http://svn.localhost/test':

svn: E195019: Redirect cycle detected for URL 'http://svn.localhost/test'
}
    ServerName svn.localhost
    ServerAdmin you@example.com
    ErrorLog "/var/log/httpd/svn.localhost-error_log"
    TransferLog "/var/log/httpd/svn.localhost-access_log"

    <Directory />
        Options +Indexes +FollowSymLinks +ExecCGI
        AllowOverride All
        Order deny,allow
        Allow from all
    Require all granted
    </Directory>
    <Location /test/>
       DAV svn
       #SVNParentPath /home/svn/test
       SVNPath /home/svn/test
       AuthzSVNAccessFile /home/svn/.svn-policy-file
       AuthName "SVN Repositories"
       AuthType Basic
       AuthUserFile /home/svn/.svn-auth-file
       Require valid-user
    </Location>
    # 如果有其他repos就複製上面粗體那段加在這後面
</VirtualHost>

設定完就加入網址
# a2ensite svn.localhost
重啟apache
# systemctl restart httpd
沒設dns的話記得要設hosts

接著打開瀏覽器 http://svn.localhost/test/ 就能看到svn了,便可使用svn checkout http://svn.localhost/test/trunk/ 來下載svn
注意:
必須使用svn checkout http://svn.localhost/test/trunk/ ,否則會出現錯誤:
$ svn checkout http://svn.localhost/test/
svn: E175002: Unexpected HTTP status 405 'Method Not Allowed' on '/test'

svn: E175002: Additional errors:
svn: E175002: PROPFIND request on '/test' failed: 405 Method Not Allowed

若想要checkout出 svn.localhost/test/ 下所有東西(含branches、tags、trunk),則必須打開SSL用https去存取
打開SSL:
# cd /etc/httpd/conf/
# openssl req -new -x509 -keyout server.key -out server.crt -days 365 -nodes
(會在這資料夾下產生server.key和server.crt)
編輯 /etc/httpd/conf/extra/httpd-ssl.conf:
#SSLSessionCache        "shmcb:/run/httpd/ssl_scache(512000)" #註解掉,我不確定為什麼在這邊要註解掉,因為用 journalctl -xn 查到這行有syntax error,所以註解掉
#SSLSessionCacheTimeout  300 #註解掉
在</VirtualHost> 前加入:
<Location /svn>
   DAV svn
   SVNParentPath /home/svn/
   AuthzSVNAccessFile /home/svn/.svn-policy-file
   AuthName "SVN Repositories"
   AuthType Basic
   AuthUserFile /home/svn/.svn-auth-file
   Require valid-user
</Location>
編輯 /etc/httpd/conf/httpd.conf :
LoadModule ssl_module modules/mod_ssl.so #取消註解
Include conf/extra/httpd-ssl.conf #取消註解

重啟apache後就能用 svn checkout https://svn.localhost/svn/test (因為沒吃到vhost,所以 https://localhost/svn/test 也可以)下載含有trunk、branches、tags的svn資料了,只是他第一次連會要你接受他的憑證

如果要移除svn 在console的某個網址(http)的登錄設定:
$ rm ~/.subversion/auth/svn.simple/xxx #用grep查網址再刪除他

搬移一個svn repos的log到另一個repos下
有兩個repos
svn checkout file:///home/svn/test:
test
├── branches
├── tags
└── trunk
    └── hello.txt
svn checkout file:///home/svn/test2:
test2
├── branches
├── tags
└── trunk
    └── media
        └── cloud
            └── web
                └── internet
欲把test的trunck下的code(hello.txt)搬到test2的trunk/media/cloud/web/internet 下面:
$ sudo svnadmin dump /home/svn/test > test.dump 
$ cat test.dump | svndumpfilter include "trunk" --drop-empty-revs --renumber-revs > test_trunk.dump #只留trunk的資料
用vim移除trunk的資料夾,若沒做這步,之後檔案會移到 trunk/media/cloud/web/internet/trunk 下面
$ vim test_trunk.dump
:1,$s/trunk\///g
或用bash+perl 取代,注意:find到的檔案(test_trunk.dump)和>>儲存的檔案(test_trunk_new.dump)不能為同一隻檔案,否則會造成無窮迴圈,必須另外儲存
$ find . -name test_trunk.dump | xargs perl -ne 'open NEW, ">>test_trunk_new.dump"; s/trunk\///g; print NEW'
將dump檔load進test2 repos
sudo svnadmin load /home/svn/test2 --parent-dir trunk/media/cloud/web/internet --ignore-uuid < test_trunk_new.dump
檢查:
$ svn checkout file:///home/svn/test2/ test2_new
$ tree test2_new
test2_new
├── branches
├── tags
└── trunk
    └── media
        └── cloud
            └── web
                └── internet
                    ├── hello.txt
                    └── trunk
查log有沒有搬移進去:
$ cd test2_new/ ; svn log
------------------------------------------------------------------------
r2 | bear | 2015-01-06 16:33:57 +0800 (二, 06  1月 2015) | 1 line

hello test
------------------------------------------------------------------------
r1 | bear | 2015-01-07 09:29:23 +0800 (三, 07  1月 2015) | 1 line

hello test2
------------------------------------------------------------------------
最後再手動移除 trunk/media/cloud/web/internet 下的trunk資料夾,即成功

參考資料:
https://wiki.archlinux.org/index.php/Subversion_Setup
https://blog.tinned-software.net/merge-two-svn-repositories/ # Merge two SVN repositories
http://stackoverflow.com/questions/1948318/how-to-configure-svn-web-access-for-different-write-permissions # 注意:用SVNPath時要用 <Location /svn/> 而非 <Location /svn>
http://stackoverflow.com/questions/11240581/subversion-405-method-not-allowed # 405 Method Not Allowed 可改用https解決
http://stackoverflow.com/questions/10524646/tortoise-svn-giving-me-redirect-cycle-detected-for-url-domain-svn # DocumentRoot 和 SVNPath 不能設一樣
http://stackoverflow.com/questions/9234852/how-to-replace-strings-in-a-file-and-save-it-as-another-file # bash+perl 取代檔案並另存新檔