2015年12月29日 星期二

linode Centos 7 安裝pptp 伺服器

因為linode是新的,且centos系列不熟,參考網路上說要安裝pptpd,但是yum 一直搜不到 pptpd
# yum search pptp
Loaded plugins: fastestmirror
Repodata is over 2 weeks old. Install yum-cron? Or run: yum makecache fast
Loading mirror speeds from cached hostfile
 * base: mirrors.linode.com
 * extras: mirrors.linode.com
 * updates: mirrors.linode.com
====================================================== N/S matched: pptp ======================================================
pptp.x86_64 : Point-to-Point Tunneling Protocol (PPTP) Client
pptp-setup.x86_64 : PPTP Tunnel Configuration Script

  Name and summary matches only, use "search all" for everything.

解法:
安裝epel-release
# yum install epel-release

最後yum搜尋得到pptpd了,
# yum search pptp
...
NetworkManager-pptp.x86_64 : NetworkManager VPN plugin for PPTP
NetworkManager-pptp-gnome.x86_64 : NetworkManager VPN plugin for PPTP - GNOME files
pptp.x86_64 : Point-to-Point Tunneling Protocol (PPTP) Client
pptp-setup.x86_64 : PPTP Tunnel Configuration Script
pptpd.x86_64 : PoPToP Point to Point Tunneling Server
pptpd-sysvinit.noarch : PoPToP Point to Point Tunneling Server

因為linode的centos 7 原本就有裝ppp和iptables了,所以只需要裝pptpd就好

安裝pptpd
# yum install pptpd

編輯/etc/pptpd.conf
# vim  /etc/pptpd.conf
將最底下localip和remoteip 的註解拿掉
localip 192.168.0.1
remoteip 192.168.0.234-238,192.168.0.245

編輯/etc/ppp/options.pptpd
# vim /etc/ppp/options.pptpd
搜尋 ms-dns,加入下面這兩行
ms-dns 8.8.8.8
ms-dns 8.8.4.4

編輯 /etc/ppp/chap-secrets,設定帳號密碼
# vim /etc/ppp/chap-secrets
username pptpd password *

編輯/etc/sysctl.conf
# vim /etc/sysctl.conf
加入
net.ipv4.ip_forward=1
然後改變設定
# sysctl -p

設定iptables
# iptables -A INPUT -i eth0 -p tcp --dport 1723 -j ACCEPT
# iptables -A INPUT -i eth0 -p gre -j ACCEPT
# iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
# iptables -A FORWARD -i ppp+ -o eth0 -j ACCEPT
# iptables -A FORWARD -i eth0 -o ppp+ -j ACCEPT
# service iptables save
The service command supports only basic LSB actions (start, stop, restart, try-restart, reload, force-reload, status). For other actions, please try to use systemctl.
# service iptables restart
Redirecting to /bin/systemctl restart  iptables.service
Failed to issue method call: Unit iptables.service failed to load: No such file or directory.

重啟pptpd
# service pptpd restart
Redirecting to /bin/systemctl restart  pptpd.service

最後用手機連vpn後打開http://www.iplocationfinder.com/,檢測翻牆有沒有成功

重開機啟動pptpd服務後連不上
http://linux.it.net.cn/CentOS/fast/2014/1102/7635.html
With RHEL 7 / CentOS 7, firewalld was introduced to manage iptables. IMHO, firewalld is more suited for workstations than for server environments.
It is possible to go back to a more classic iptables setup. First, stop and mask the firewalld service:
RHEL 7/CentOS 7 firewalld是被做為管理iptables,述我直言,firewalld比較適合工作站而不是服務器環境。
這有可能把他調回傳統的iptables設定
查firewalld狀態
# systemctl status firewalld
firewalld.service - firewalld - dynamic firewall daemon
   Loaded: loaded (/usr/lib/systemd/system/firewalld.service; disabled)
   Active: inactive (dead)
啟動iptables報錯
# systemctl start iptables
Failed to issue method call: Unit iptables.service failed to load: No such file or directory.
stop和mask firewalld服務
# systemctl stop firewalld
# systemctl mask firewalld
安裝search iptables-services
# yum install iptables-services
開機時啟動iptables的服務
systemctl enable iptables
儲存防火牆設定
# service iptables save
iptables: Saving firewall rules to /etc/sysconfig/iptables:[  OK  ]
啟動iptables服務(這次無報錯)
# systemctl start iptables.service

某天iPhone手機連不上PPTP服務
在CentOS 7 server查pptpd的log
方法1
# journalctl -f
方法2
# tail -f /var/log/messages
有看到 CTRL: PTY read or GRE write failed (pty,gre)=(6,7) 錯誤, 但是查這篇 https://www.lidaren.com/archives/1229 後,試著把 /etc/pptpd.conf 的 logwtmp 註解掉,但手機仍然連不上
結果:
使用Windows7電腦連上了,看來是手機的問題


參考資料:
https://www.photonvps.com/billing/knowledgebase.php?action=displayarticle&id=58   How to setup VPN server (PPTP on CentOS, RedHat and Ubuntu)?

2015年12月15日 星期二

VirtualBox 虛擬機跑MySQL

寫下我最近用VirtualBox跑測試環境過慢的問題

虛擬機
ubuntu 14.04
mysql  Ver 14.14 Distrib 5.5.41, for debian-linux-gnu (x86_64) using readline 6.3

問題一
靜態網頁phpinfo()跑很慢
因為我的虛擬機測試環境已經很髒了,之前在這篇 haproxy使用心得 設定了load balance,所以
先用telnet xxx 80 查連不連得到 => 連不到
用kill把nginx進程(process)全殺了
# killall nginx
再重啟
# /etc/init.d/nginx start
robert:可能是進程被咬死,用service nginx restart 是起不了作用的

問題二
開有連虛擬機上資料庫的網頁非常慢,但設定改為連其他機器時,速度正常
在mysql用show processlist檢查後發現
mysql> show processlist;
...
| 10 | unauthenticated user | 192.168.169.147:51391 | NULL | Connect | NULL | login          | NULL

這個連線在卡
解法:
http://stackoverflow.com/questions/8034706/access-mysql-server-on-linux-throu-virtualbox-windows7-laggs
在 /etc/mysql/my.cnf 的 [mysqld] 中新增
skip-name-resolve

然後重啟mysql
# service mysql restart

問題三
phpmyadmin 編輯欄位速度過慢
mysql> show processlist;
...
| 19 | root | 192.168.169.140:51359 | progmate_test | Query   |   39 | NULL  | SELECT `column_name`, `table_name`, `table_schema`, `referenced_column_name` FROM `information_schem |
卡在SELECT information_schema這個表

簡單的測試
https://www.percona.com/blog/2011/12/23/solving-information_schema-slowness/  Solving INFORMATION_SCHEMA slowness
mysql> select count(*),sum(data_length) from information_schema.tables;
+----------+------------------+
| count(*) | sum(data_length) |
+----------+------------------+
|      769 |       5880654489 |
+----------+------------------+
1 row in set (24.77 sec)

mysql> set global innodb_stats_on_metadata=0;
Query OK, 0 rows affected (0.00 sec)

mysql> select count(*),sum(data_length) from information_schema.tables;
+----------+------------------+
| count(*) | sum(data_length) |
+----------+------------------+
|      769 |       5880654489 |
+----------+------------------+
1 row in set (0.07 sec)

解法:
http://stackoverflow.com/questions/7283915/poor-performance-of-information-schema-key-column-usage-in-mysql  Poor performance of INFORMATION_SCHEMA.key_column_usage in MySQL
/etc/mysql/my.cnf 的 [mysqld] 中新增
innodb_stats_on_metadata=0

然後重啟mysql
# service mysql restart

什麼是innodb_stats_on_metadata?
https://www.percona.com/blog/2013/12/03/innodb_stats_on_metadata-slow-queries-information_schema/  innodb_stats_on_metadata and slow queries on INFORMATION_SCHEMA
When the option is set to ON, InnoDB index statistics are updated when running SHOW TABLE STATUS, SHOW INDEX or when querying INFORMATION_SCHEMA.TABLES or INFORMATION_SCHEMA.STATISTICS. These statistics include the cardinality and the number of entries, they are used by the optimizer to find an optimal execution plan.
當這個選項被設置為ON時。當執行 SHOW TABLE STATUS、SHOW INDEX、操作INFORMATION_SCHEMA.TABLES或INFORMATION_SCHEMA.STATISTICS表時, InnoDB的索引統計正被更新。這個統計包括基數( cardinality ) 和 entry數,他們用於優化找到一個最佳的執行計畫
So even if SELECT statements cannot change the real statistics, MySQL updates the statistics for InnoDB tables. This is counterintuitive.
因此,如果SELECT語句不能改變真正的統計(MySQL更新InnoDB表的統計)。這是違反直覺的。
Is it useful? Not really, because InnoDB will always compute statistics when you open a table for the first time and when significant portions of the table have been modified (and when you run ANALYZE TABLE).
它有用嗎?不是,因為InnoDB總是計算統計當你第一次打開一個表和當一個表顯著的部分被修改了(和當你運行ANALYZE TABLE)
The problem was magnified in the example because the whole dataset was not fitting in memory, the number of tables was high and the I/O subsystem was not very powerful.
該問題是被放大的例子,因為整個dataset未嵌合在記憶體中,表的數量很大而子系統的I/O不夠強

值得提出來的是,這個值在 MySQL 5.6 內的 InnoDB 預設是關閉的,在 5.1 與 5.5 則是開啟的。

參考資料:
http://sourceforge.net/p/phpmyadmin/bugs/4592/  #4592 (ok 4.4) Editing columns (tbl_structure.php) is incredibly slow
https://blog.gslin.org/archives/2013/12/09/3918/innodb_stats_on_metadata-%E5%B0%8D%E6%95%88%E8%83%BD%E7%9A%84%E5%BD%B1%E9%9F%BF/  innodb_stats_on_metadata 對效能的影響...








2015年12月14日 星期一

PHP的魔術方法(magic methods)

會寫下這篇原因是 上海逸尚信息咨询 曾經考過這題

magic methods是一系列以__開頭的方法名稱,如果在類別中定義了這些方法,系統會在特定的時機呼叫。

 __construct
在PHP4時,Class的建構函數( constructor )是跟類別同名的方法,到PHP5,則改名為__construct()這個magic method。
在Class new成 Object時呼叫

__destruct()
解構函數( destructor ),當物件要被從系統中「消滅」時,會呼叫這個方法。

__call / __callStatic
這是PHP實作method overload的方式,如果呼叫物件的某方法,而這個方法沒有在類別中定義的話,系統會嘗試呼叫__call()。實作__call()然後過濾系統傳入的方法名稱,就可以讓物件表現的像是有定義這個方法。__callStatic()也是一樣的作用,只是是針對靜態方法。
例:
class a {
    function __call($name, $args) {
        echo $name . " : " . print_r($args, true) . "\n";
    }
}

$a = new a;
$a->func1('abc', 'def');

class b {
    function __call($name, $args) {
        echo "\n__call:";
        switch ($name) {
        case 'add':
            if (count($args) === 2) {
                if (is_numeric($args[0]) && is_numeric($args[1])) {
                    return $args[0] + $args[1];
                }

                if (is_string($args[0]) && is_string($args[1])) {
                    return $args[0] . $args[1];
                }

            }
        default:
            throw new Exception("[warning] b::$name method not found.\n");
        }
    }
    public static function __callStatic($name, $args){
        echo "\n__callStatic:";
        switch ($name) {
            case 'add':
                if (count($args) === 2) {
                    if (is_numeric($args[0]) && is_numeric($args[1])) {
                        return $args[0] + $args[1];
                    }

                    if (is_string($args[0]) && is_string($args[1])) {
                        return $args[0] . $args[1];
                    }

                }
                break;
            
            default:
                throw new Exception("[warning] b::$name method not found.\n");
                break;
        }
    }
}

$b = new b;
echo $b->add(2, 3) . "\n";
echo $b->add('hello', ' world.') . "\n";
try {
    echo $b->add(2, ' world.') . "\n";
} catch (Exception $e) {
    echo $e->getMessage();
}

echo $b::add(2, 3) . "\n";
echo $b::add('hello', ' world.') . "\n";
結果:
func1 : Array
(
    [0] => abc
    [1] => def
)


__call:5

__call:hello world.

__call:[warning] b::add method not found.

__callStatic:5

__callStatic:hello world.
注意:
function __callStatic() 必須是 public static ,否則會跳Warning:
PHP Warning:  The magic method __callStatic() must have public visibility and be static in magic_methods/method_overloading.php on line 30
上面例子如果$b->add()的兩個參數不同時為數字或字串,ex. $b->add(2, ' world.'); 。 則會報錯

__set() / __get() / __isset() / __unset()
PHP透過這幾個方法實現屬性的overload。讀取物件屬性時,如果屬性不存在,系統會嘗試呼叫__get()。如果類別有實作這個方法,就可以過濾傳入的屬性名稱,看看是否要返回值。__set則是對應到寫入物件屬性的狀況。另外,PHP可以透過isset()函數檢查物件屬性是否已設定、unset()函數來讓物件屬性回到未設定(null)的狀態,這時如果物件屬性不存在,則會嘗試呼叫__isset()及__unset()。
attribute overload例:
class a {
    function __get($name) {
        if ($name === 'var1') {
            return 'abc';
        }

    }
}

$a = new a;
echo $a->var1 . "\n";
echo $a->var2 . "\n";

class b {
    private $var1 = 'abc';
    private $var2 = 'def';
    function __get($name) {
        switch ($name) {
        case 'var1':
            return $this->var1;
            break;
        case 'var2':
            return 'ghi';
            break;
        }
    }
}

$b = new b;
echo $b->var1 . "\n";
echo $b->var2 . "\n";

class c {}

$c = new c;
echo $c->var1 . "\n";
結果:
abc

abc
ghi
PHP Notice:  Undefined property: c::$var1 in /cygdrive/e/www/test/magic_methods/attribute_overloading.php on line 37
__set() / __get() / __isset() / __unset()所有例子:
class PropertyTest {
    /**  被重载的数据保存在此  */
    private $data = array();

    /**  重载不能被用在已经定义的属性  */
    public $declared = 1;

    /**  只有从类外部访问这个属性时,重载才会发生 */
    private $hidden = 2;

    public function __set($name, $value) {
        echo "Setting '$name' to '$value'\n";
        $this->data[$name] = $value;
    }

    public function __get($name) {
        echo "Getting '$name'\n";
        if (array_key_exists($name, $this->data)) {
            return $this->data[$name];
        }

        $trace = debug_backtrace();
        trigger_error(
            'Undefined property via __get(): ' . $name .
            ' in ' . $trace[0]['file'] .
            ' on line ' . $trace[0]['line'],
            E_USER_NOTICE);
        return null;
    }

    /**  PHP 5.1.0之后版本 */
    public function __isset($name) {
        echo "Is '$name' set?\n";
        return isset($this->data[$name]);
    }

    /**  PHP 5.1.0之后版本 */
    public function __unset($name) {
        echo "Unsetting '$name'\n";
        unset($this->data[$name]);
    }

    /**  非魔术方法  */
    public function getHidden() {
        return $this->hidden;
    }
}

echo "\n";

$obj = new PropertyTest;

$obj->a = 1;
echo $obj->a . "\n\n";

var_dump(isset($obj->a));
unset($obj->a);
var_dump(isset($obj->a));
echo "\n";

echo $obj->declared . "\n\n";

echo "Let's experiment with the private property named 'hidden':\n";
echo "Privates are visible inside the class, so __get() not used...\n";
echo $obj->getHidden() . "\n";
echo "Privates not visible outside of class, so __get() is used...\n";
echo $obj->hidden . "\n";
結果:
Setting 'a' to '1'
Getting 'a'
1

Is 'a' set?
bool(true)
Unsetting 'a'
Is 'a' set?
bool(false)

1

Let's experiment with the private property named 'hidden':
Privates are visible inside the class, so __get() not used...
2
Privates not visible outside of class, so __get() is used...
Getting 'hidden'
PHP Notice:  Undefined property via __get(): hidden in magic_methods/set.php on line 68 in magic_methods/set.php on line 28
說明:
__set()在 $obj->a = 1;時呼叫,並改寫存到$this->data中
__get()在 echo $obj->a; 時呼叫,因為前面$obj->a = 1;被__set()改寫了,所以取不到值,會呼叫__get()
__isset() 在 isset($obj->a) 時呼叫,所以被叫了兩次
__unset() 在 unset($obj->a); 時呼叫

關於overloading
定義:藉由接收的參數串列之型態或數量之不同,以求達到多個函式可共用相同函式名稱
overloading的目的是:
1. 降低所需命名的函式名稱
2. 提高user的易用性
PHP用__call()實作method overloading( function overloading、廣義或上面定義的overloading )
__get()實作 attribute overloading( operator overloading )


__wakeup() / __sleep()
如果有在類別中定義的話,這兩個magic methods會在物件序列化( serialize() )/反序列化( unserialize() )時被呼叫。
如果類別有定義這個方法,__sleep()就會在物件開始序列化被呼叫。他會返回一個陣列,裡面列舉需要被序列化的屬性名稱。這樣在進行序列化時,系統會針對這些屬性來進行操作。

例:
class a {
    public $name;
    public function __sleep() {
        $this->name = 'fillano';
        return array("name");
    }
    public function __wakeup() {
        $this->age = 18;
    }
}
$a = new a;
$str = serialize($a);
print_r($str);
echo "\n";
$b = unserialize($str);
print_r($b);
結果:
O:1:"a":1:{s:4:"name";s:7:"fillano";}
a Object
(
    [name] => fillano
    [age] => 18
)
說明:
如果 __sleep() 沒有返回值 return array("name"); ,則會跳Notice:
PHP Notice:  serialize(): __sleep should return an array only containing the names of instance-variables to serialize in magic_methods/sleep.php on line 13

__toString()
如果定義了這個方法並且回傳一個字串,那把物件當做字串操作時,系統會呼叫__toString()來取得代表物件的字串。
例:
class hello {
    public function __toString() {
        return "Hello ";
    }
    function test() {
        echo "test\n";
    }
}
class world {
    public function __toString() {
        return "World.\n";
    }
}
$a = new hello;
$b = new world;
echo $a . $b;
$a->test();
結果:
Hello World.
test
說明:
hello->test() 依然可以呼叫,不受__toString()影響

__invoke()
這是PHP5.3才有的magic method。當物件被當做函數來呼叫時,就會呼叫這個方法。可以用is_callable()函數來檢查物件是否可以當做函數執行,可以的話會回傳true。
例:
class a {
    public function __invoke() {
     $this->name = 'Jim';
        return __CLASS__ . " is invoked.\n";
    }
}
$a = new a;
print_r($a);
if (is_callable($a)) {
    echo $a();
}
print_r($a);
結果:
a Object
(
)
a is invoked.
a Object
(
    [name] => Jim
)
說明:
在$a()觸發__invoke()前,$a->name值是空的,在__invoke()中賦值後才有

__set_state()
這個從PHP5.1加入的magic method。PHP有一個var_export()函數,可以輸出變數的結構化資訊字串,這個資訊同時也是合法的PHP程式碼,所以可以被eval()執行。
如果類別定義了這個靜態方法,當使用var_export()來處理物件實例時,系統會先檢查這個方法是否存在,然後產生呼叫這個靜態方法的程式碼字串,在程式碼中,會把物件實例的屬性陣列當做參數傳遞給他。
例:
class A
{
    public $var1;
    public $var2;

    public static function __set_state($an_array) // As of PHP 5.1.0
    {
        echo "__set_state\n";
        $obj = new A;
        $obj->var1 = $an_array['var1'];
        $obj->var2 = $an_array['var2'];
        return $obj;
    }
}

$a = new A;
$a->var1 = 5;
$a->var2 = 'foo';

eval('$b = ' . var_export($a, true) . ';'); // $b = A::__set_state(array(
                                            //    'var1' => 5,
                                            //    'var2' => 'foo',
                                            // ));
var_dump($b);
結果:
__set_state
object(A)#2 (2) {
  ["var1"]=>
  int(5)
  ["var2"]=>
  string(3) "foo"
}
注意:
必須同時使用eval()和var_export(),如: eval('$b = ' . var_export($a, true) . ';'); 才會觸發__set_state()

__clone()
__clone()會在複製( clone )完畢時對新的物件執行,所以可以在需要時,調整複製後的物件屬性。
例:
class A {
    public $obj;
    public function __construct() {
        $this->obj = new B;
    }
    public function __clone() {
        $this->obj = clone $this->obj;
    }
}

class B {
    public $name = 'fillano';
}

$a = new A;
$b = clone $a;
$b->obj->name = 'james';
var_dump($a->obj->name);
var_dump($b->obj->name);
結果:
string(7) "fillano"
string(5) "james"

物件Assignment 和 Cloning有什麼差別?
http://stackoverflow.com/questions/16893949/php-object-assignment-vs-cloning  PHP Object Assignment vs Cloning
例:
class Bar {

}

$foo = new Bar;   // $foo holds a reference to an instance of Bar
$bar = $foo;      // $bar holds a copy of the reference to the instance of Bar
$baz = &$foo;     // $baz references the same reference to the instance of Bar as $foo

$blarg = clone $foo;  // the instance of Bar that $foo referenced was copied
                      // into a new instance of Bar and $blarg now holds a reference
                      // to that new instance
$foo->bear = 'bear';
$bar->xyz = 'xyz';
$baz->ted = 'ted';

echo "\nspl_object_hash(\$foo):".spl_object_hash($foo);

echo "\$foo:";
print_r($foo);

echo "\nspl_object_hash(\$bar):".spl_object_hash($bar);
echo "\$bar:";
print_r($bar);

echo "\nspl_object_hash(\$baz):".spl_object_hash($baz);
echo "\$baz:";
print_r($baz);

echo "\nspl_object_hash(\$blarg):".spl_object_hash($blarg);
echo "\$blarg:";
print_r($blarg);
結果:
spl_object_hash($foo):000000003db193cd00000003cd585be1$foo:Bar Object
(
    [bear] => bear
    [xyz] => xyz
    [ted] => ted
)

spl_object_hash($bar):000000003db193cd00000003cd585be1$bar:Bar Object
(
    [bear] => bear
    [xyz] => xyz
    [ted] => ted
)

spl_object_hash($baz):000000003db193cd00000003cd585be1$baz:Bar Object
(
    [bear] => bear
    [xyz] => xyz
    [ted] => ted
)

spl_object_hash($blarg):000000003db193ce00000003cd585be1$blarg:Bar Object
(
)
說明:
用 assign( = )和assign reference( =& )在object上無意義,結果是一樣的,用spl_object_hash()查出物件的reference ID也一樣改變其中一個屬性,其他跟著變。但用clone時,改變$foo、$bar、$baz屬性時,$blarg不受影響。
PHP的assign( = )和assign reference( =& )在 PHP常見問題::php的refrence符号&用法 可見不同之處

assignment 和 clone的另一個差別是, clone可以觸發 __clone()魔術方法

參考資料:
http://ithelp.ithome.com.tw/question/10135522  逐步提昇PHP技術能力 - PHP的語言特性 : magic methods
http://ithelp.ithome.com.tw/question/10132318  逐步提昇PHP技術能力 - PHP的語言特性 : 多載 (overloading)
http://antrash.pixnet.net/blog/post/79139547-%E8%AB%96%E7%89%A9%E4%BB%B6%E5%B0%8E%E5%90%91part-8%EF%BC%9Awhy-overloading%E3%80%81overriding  Why Overloading、Overriding
http://www.5idev.com/p-php_object_clone.shtml  PHP 对象克隆 clone 关键字与 __clone() 方法
http://stackoverflow.com/questions/16893949/php-object-assignment-vs-cloning  PHP Object Assignment vs Cloning
http://stackoverflow.com/questions/1953782/php-getting-reference-id  PHP: Getting reference ID







2015年12月11日 星期五

設定session時間的長短


● session 的參數說明:
1、session.use_cookies:默認的值是「1」,代表 SessionID 使用 Cookie 來傳遞,反之就是使用 Query_String 來傳遞。
2、session.name:這個就是 SessionID 儲存的變量名稱,可能是 Cookie,也可能是 Query_String 來傳遞,默認值是「PHPSESSID」。
3、session.cookie_lifetime「單位(秒)- 86400 表示是1天24小時」:這個代表 SessionID 在客戶端 Cookie 儲存的時間,預設值是 0,代表瀏覽器一關閉 SessionID 就作廢,就是因為這個所以 Session 不能永久使用!
4、session.gc_maxlifetime「單位(秒)」:這個是 Session 數據在 Server 端儲存的時間,如果超過這個時間,那麼 Session 數據就自動刪除!

如果不想用程式設定,可以使用如下設定:
.htaccess 裡加上這一行
php_value session.save_path [你的session save_path]

減少session存活時間(到幾秒鐘)
The session garbage collector will fire as part of session_start(). Since you're changing the setting AFTER you start a session, you're too late to change the settings.
session的垃圾收集器(garbage collector )將做為session_start()的一部分。因為你改變php.ini設定( ini_set('session.gc_maxlifetime',60); )在session啟動之後,這時候改變設定已經太晚了。

這中間發生了什麼事?
1. The session file sits in its temp directory
session檔案在temp資料夾( php.ini的session.save_path )產生
2. You session_start(), causing PHP to open/lock the file, read its contents, deserialize the data, and incidentally, possibly update the session file's "last used" timestamp (atime on Unix boxes).
你的session_start() 導致php打開/鎖定這個檔案、讀取她的內容、反序列化成資料。然後順便更新這個session檔案最後使用的時間戳
3. If the stars and moon are aligned correctly with Neptune ascendant in the fifth house, the session garbage collector MAY fire up and delete old session files.
如果時間到了,垃圾收集器(garbage collector )可能會開火然後刪除舊的session檔案
4. The garbage collector will happily iterate through the session directory, and delete any files which are older than the max_liftime, BUT WILL NOT DELETE ANY FILES CURRENTLY OPEN/IN USE. Since you've not closed()'d your session, your session's file is still in use, so will not get deleted.
垃圾收集器會隨他高興的遍歷session資料夾並刪除早於max_liftime的文件,但不會刪除現在正在打開和使用的檔案。因為你還沒關閉你的session。你的session檔案依然在使用中,將不會被刪除

如果你寫一個程式像下面這樣
ini_set('session.gc_maxlifetime', 5); // set GC probability to max, short session lifetime, etc...
session_start(); // populate $_SESSION
session_write_close(); // dump $_SESSION out to file, close file, release lock.
sleep(7); // Sleep for 7 seconds;
session_start(); // re-populate $_SESSION;

That might increase the probability that the garbage collector.
這可能會增加機率讓垃圾蒐集器清掉你的session檔案(實測後沒成功過...)


參考資料:
http://blog.xuite.net/tunedgr02/data/14681314-%E3%80%90PHP+session+maxlifetime+%E3%80%91%E8%87%AA%E8%A1%8C%E8%AA%BF%E6%95%B4%E8%87%AA%E5%B7%B2%E6%9E%B6%E8%A8%AD+PHP+%E7%B6%B2%E7%AB%99%E6%88%96%E8%99%9B%E6%93%AC%E4%B8%BB%E6%A9%9F%E4%B8%AD%E7%9A%84+session+%E6%99%82%E9%96%93%E9%95%B7%E7%9F%AD!!   【PHP session maxlifetime 】自行調整自已架設 PHP 網站或虛擬主機中的 session 時間長短!!
http://stackoverflow.com/questions/12032306/why-ini-setsession-gc-maxlifetime-60-doesnt-work  why ini_set('session.gc_maxlifetime',60) doesn't work?
http://stackoverflow.com/questions/3428153/php-ini-setsession-gc-maxlifetime-5-why-it-doesnt-end-the-session  PHP - ini_set('session.gc_maxlifetime', 5) - Why it doesn't end the session?




MySQL新增使用者

phpmyadmin
任意主機所有權限
登陸後點上方的 "伺服器: localhost" => "使用者帳號" =>  新增使用者帳號 =>
填寫"帳號"、主機名稱、密碼、重新輸入、還有選擇權限 => 執行

刪除使用者
登陸後點上方的 "伺服器: localhost" => "使用者帳號" => 勾選使用者 => 點擊下方" 刪除已選擇的使用者帳號"方塊的"執行"

CLI
任意主機所有權限
mysql> CREATE USER 'user'@'%' IDENTIFIED WITH mysql_native_password;
mysql> GRANT ALL PRIVILEGES ON *.* TO 'user'@'%' REQUIRE NONE WITH GRANT OPTION MAX_QUERIES_PER_HOUR 0 MAX_CONNECTIONS_PER_HOUR 0 MAX_UPDATES_PER_HOUR 0 MAX_USER_CONNECTIONS 0;
mysql> SET PASSWORD FOR 'user'@'%' = PASSWORD('password');

或簡化
mysql> CREATE USER 'user'@'%' IDENTIFIED BY 'password';
mysql> GRANT ALL PRIVILEGES ON *.* TO 'user'@'%';
20160607出現以下錯誤可以用上面方法解
$ mysql -u root -p -h 127.0.0.1
Enter password:
ERROR 1045 (28000): Access denied for user 'root'@'127.0.0.1' (using password: YES)


或只指定部分權限
mysql> CREATE USER 'user'@'%' IDENTIFIED BY 'password';
mysql> GRANT SELECT, INDEX, SHOW DATABASES ON *.* TO 'user'@'%';


刪除使用者
mysql> drop user 'user'@'%';
注意: 要用drop,下面這組直接刪mysql.user無效
mysql> delete from mysql.user where user='user' and host='%';
mysql> flush privileges;

參考資料:
http://coding.memory-forest.com/%E7%94%A8%E5%91%BD%E4%BB%A4%E5%88%97%E7%AE%A1%E7%90%86mysql%E4%BD%BF%E7%94%A8%E8%80%85.html  用命令列管理MySQL使用者



2015年11月27日 星期五

2015年11月24日 星期二

process與thread差異


Both processes and threads are independent sequences of execution. The typical difference is that threads (of the same process) run in a shared memory space, while processes run in separate memory spaces.
Process和Thread都是獨立的執行序列。不同的是,(同一個Process的)Threads 在共享的記憶體空間中運行,而processes在不同的記憶體中運行

在2.4內核中,不存在線程組的概念,當運行一個多線程得程式時,使用ps命令,可以看到有許多個進程,在ps命令看來,線程基本上是等同於進程,在信號處理中,情況也是如此,只有指定進程號的線程,可以接收到信號。在2.6內核中引入了線程組的概念,在2.6內核中,如果使用ps命令看,一個多線程的進程,只會顯示一個進程,在給線程組中的任何一個線程發送信號的時候,整個線程組中的進程都能收到信號。

PTT
NDark:
code是炮操手冊 cpu是兵 process是跳完砲操 thread是砲管數
lovdkkkk:
各個 Process 共享 CPU/Memory 的資源
然後 Thread 共享 Process 的資源

參考資料:
http://stackoverflow.com/questions/200469/what-is-the-difference-between-a-process-and-a-thread  What is the difference between a process and a thread
http://www.longene.org/forum/viewtopic.php?f=5&t=94&p=399#p399  Linux2.6内核中的线程组初探
http://blog.chinaunix.net/uid-24774106-id-3650136.html  Linux线程 之 线程 线程组 进程 轻量级进程(LWP)
https://www.ptt.cc/bbs/Soft_Job/M.1406625146.A.971.html  [請益] 要如何讓人搞懂Process與Thread



2015年11月13日 星期五

nginx 心得

LINUX上有兩個php
/usr/bin/php
/usr/local/php55/bin/php
$ php --version
PHP 5.4.42 (cli)
$ /usr/local/php55/bin/php --version
PHP 5.5.28 (cli)

要怎麼讓nginx 跑的是 /usr/local/php55/bin/php 的php?
=> 看你的  php-fpm 是跑在哪裡啊,nginx 一定得用 php-fpm 的 fastcgi

找不到php-fpm指令,但有在跑
$ which php-fpm
/usr/bin/which: no php-fpm in (/usr/local/bin:/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/sbin:/home/bear/bin)
$ ps axu | grep php-fpm
root       827  0.0  0.1 220016  4720 ?        Ss   Sep21   2:51 php-fpm: master process (/etc/php-fpm.conf)                   
www        829  0.0  0.4 231124 17476 ?        S    Sep21  17:56 php-fpm: pool www     
www      22389  0.0  0.3 232648 14560 ?        S    Sep21  17:54 php-fpm: pool www  
www      22563  0.0  0.3 227552 13332 ?        S    Oct05  14:00 php-fpm: pool www

CentOS
查有沒有裝php-cli(編譯裝的查不到)
$ rpm -qa | grep php-cli
查php-cli裝在哪裡(編譯裝的查不到)
$ rpm -qs php-cli

Ubuntu
$ dpkg -L php5-fpm # 列出檔案
$ dpkg -S php5-fpm # 搜尋放在哪

查nginx ( sites-available )設定檔
...
fastcgi_pass   127.0.0.1:9000;
 127.0.0.1:9000 這個是 php-fpm,表示你的 127.0.0.1:9000 這個 port 是 php5.5 開啟來的

就是 sudo service php5-fpm start ( => 去 call /etc/init.d/php5-fpm )所開啟的

查 php5-fpm startup scripts
# vim /etc/init.d/php5-fpm
...
prefix=/usr/local/php55

=> 就是他,只是我沒想到他會去改那隻 startup scripts,不要搞髒他啊 . .

Rellik:
我討厭用一些 pip rvm 之類的東西
因為這實際上並沒有進 centos 官方測試
安裝對應的路徑也不一樣
你隨便用一些奇怪套件安裝方式會把自己系統搞髒
最恐怖的是萬一這類的工具沒有把 dependency 弄好的話
其實很恐怖
他可能會蓋掉一些centos 或是 ubuntu 官方的套件
所以除非有特定版本需求
不然我大部分都還是以官方的套件管理系統為主
這樣管理機器上也不用擔心會踩到地雷 . . .
我在 freebsd 上用 ports 安裝的話就是下載原始碼啊
問題是  linux 上原始碼並不會進套件資料庫去管控起來
所以裝完以後你久了就忘了
根本不記得你有裝過什麼東西
freebsd 上面 ports 下載來的會盡套件資料庫控管
只是你要花時間編譯就是
總之你要自己編譯安裝也是可以
但前提是你要怎麼控管整個 flow ?
不要今天想到缺什麼就自己跑去下載原始碼開始編譯 . . .
系統就是這樣子被搞髒的


參考資料:
http://www.howtogeek.com/howto/ubuntu/see-where-a-package-is-installed-on-ubuntu/  See Where a Package is Installed on Ubuntu



2015年11月12日 星期四

firefox常見問題

關閉flash
https://support.mozilla.org/zh-TW/kb/adobe-flash-protected-mode-firefox
ctrl+shift+a => Shockwave Flash => 選項 => (下拉選單)  "總是啟用" 改成 "永不啟用"

重灌電腦後打開firefox會忽然報flash錯誤
解法:
到 https://support.mozilla.org/zh-TW/kb/keep-flash-up-to-date-and-troubleshoot-problems 點  Flash Player 下載頁面 安裝flash(注意:McAfee防毒不要勾選)後重啟電腦。firefox打開有flash的網頁(ex. http://wechoosethemoon.org/)測試有沒有安裝成功


2015年11月10日 星期二

ThinkPHP心得

某天發現我用thinkphp寫的ajax呼叫,返回原始檔中會有需多空行
http://localhost/api.html
Ex.
 



success

ThinkPHP/Common/common.php
require_array($array,$return=false) 呼叫 require_cache()
require $filename; // $filename:./app/Lib/Action/MyAction.class.php
經過這行後就開始產生空行

原來是./app/Lib/Action/MyAction.class.php 這隻檔案我在<?php ... ?> 之後多寫了很多空白行造成的
MyAction.class.php:
<php?
...
?>




  <= 多出的空行
把那些空行拿掉就可以了,所以class檔在<?php ?>結束後請不要多空行埋這個坑

execute和query方法的区别
http://blog.csdn.net/chamtianjiao/article/details/6405951
excute() - insert or update
query() - select
$model = new Model();
$result = $model ->execute( 'select * from MyTable' ); //将返回总行数
$result = $model ->query( 'select * from MyTable' ); //将返回array()

Nginx 下實現ThnkPHP 的 pathinfo模式
癥狀:
http://www.thinkphp.cn/topic/21478.html
除了首頁外全顯示404 Not Found ( Linux + Nginx環境 )
http://www.aaa.com/index.php => 能夠顯示
http://www.aaa.com/index.php/User/login => 顯示 404
解法:
http://www.thinkphp.cn/topic/3138.html
編輯該站的配置文件( sites-available/www.aaa.com.conf ) location ~ \.php$ { 將$去掉 location ~ \.php { ,去掉$是為了不匹配行末,即可以匹配.php/,以實現pathinfo
location ~ \.php {
    # 原有代碼

    ##pathinfo支持start
    #定義變數 $path_info ,用於存放pathinfo資訊
    set $path_info "";
    #定義變數 $real_script_name,用於存放真真實位址
    set $real_script_name $fastcgi_script_name;
    #如果地址與引號內的規則運算式匹配
    if ($fastcgi_script_name ~ "^(.+?\.php)(/.+)$") {
        #將檔位址賦值給變數 $real_script_name
        set $real_script_name $1;
        #將檔位址後的參數賦值給變數 $path_info
        set $path_info $2;
    }
    #配置fastcgi的一些參數
    fastcgi_param SCRIPT_FILENAME $document_root$real_script_name;
    fastcgi_param SCRIPT_NAME $real_script_name;
    fastcgi_param PATH_INFO $path_info;
    ###pathinfo支持end
}

避免xdebug在chrome斷點兩次

https://stackoverflow.com/a/42012288  How to do not redirect specific file on nginx?
寶塔中thinkphp的偽靜態預設配置是:
location / {
if (!-e $request_filename){
rewrite  ^(.*)$  /index.php?s=$1  last;   break;
}
}
但是這樣會造成Chrome的 favicon.ico 的也會進入斷點,把偽靜態設置改成
location / {
    location ~ ^/favicon\.ico$ {
        root   www;
    }
    if (!-e $request_filename){
        rewrite  ^(.*)$  /index.php?s=$1  last;   break;
    }
}













2015年11月5日 星期四

PHP urlencode() 和 rawurlencode()的差異


It will depend on your purpose. If interoperability with other systems is important then it seems rawurlencode is the way to go. The one exception is legacy systems which expect the query string to follow form-encoding style of spaces encoded as + instead of %20 (in which case you need urlencode).
這將取決於你的目的。如果與其他系統的互操作性是很重要的話,使用rawurlencode。
除非是舊系統的查詢字符串(query string)把空白視為+而不是%20,在這種情況下,才需要使用urlencode()

rawurlencode follows RFC 1738 prior to PHP 5.3.0 and RFC 3986 afterwards
rawurlencode 在PHP5.3.0之前遵循RFC 1738、之後遵循RFC3986
$ php -r 'echo urlencode(" ");'
+

urlencode encodes spaces as plus signs (not as %20 as done in rawurlencode)
urlencode 把空白編碼成加號,而不是rawurlencode 的%20
$ php -r 'echo rawurlencode(" ");'
%20

結論:
So which is preffered?
哪一種是首選
rawurlencode. go with the standard in this case. urlencode is only kept for legacy use
rawurlencode. 是標準,urlencode只是給舊版用的

參考資料:
http://stackoverflow.com/questions/996139/urlencode-vs-rawurlencode  urlencode vs rawurlencode?



linode centos7 安裝gitlab

在linode上新開(rebuild)一台centos 7 後

先更新軟體
# yum update
安裝自己常用的軟體
# yum install screen bash-completion
設定.screenrc ( linux screen 使用心得 )
# vim .screenrc


安裝gitlab

1. 安裝和設定所需的依賴( Install and configure the necessary dependencies )
# yum install curl openssh-server => linode已安裝
...
Nothing to do
# systemctl enable sshd => 無反應
# systemctl start sshd => 無反應
# yum install postfix => linode已安裝
...
Nothing to do
# systemctl enable postfix
ln -s '/usr/lib/systemd/system/postfix.service' '/etc/systemd/system/multi-user.target.wants/postfix.service'
# systemctl start postfix => 無反應
# firewall-cmd --permanent --add-service=http => 失敗,但跳過不打緊
FirewallD is not running
# systemctl reload firewalld => 失敗,但跳過不打緊
Job for firewalld.service failed. See 'systemctl status firewalld.service' and 'journalctl -xn' for details.

2. 安裝gitlab ( Add the GitLab package server and install the package )
# curl https://packages.gitlab.com/install/repositories/gitlab/gitlab-ce/script.rpm.sh | sudo bash
...
Generating yum cache for gitlab_gitlab-ce...
Importing GPG key 0xE15E78F4:
 Userid     : "GitLab B.V. (package repository signing key) <packages@gitlab.com>"
 Fingerprint: 1a4c 919d b987 d435 9396 38b9 1421 9a96 e15e 78f4
 From       : https://packages.gitlab.com/gpg.key
# yum install gitlab-ce
...
Installed:
  gitlab-ce.x86_64 0:8.1.2-ce.0.el7

Complete!

3. 設定和啟動GitLab ( Configure and start GitLab )
# sudo gitlab-ctl reconfigure (自動跑,無需設定)
... 
gitlab Reconfigured!

4. 打開瀏覽器登入Gitlab ( Browse to the hostname and login )
預設帳號:
Username: root
Password: 5iveL!fe
第一次登陸後他會要你改密碼

這方法比我 2015年年初看到的這個方法
https://github.com/gitlabhq/gitlab-recipes/tree/master/install/centos
還要容易多了


更改GitLab的port

1. 編輯 /etc/gitlab/gitlab.rb 
# vim /etc/gitlab/gitlab.rb

external_url 'http://xxx.members.linode.com' => 'http://xxx.members.linode.com:1234'
2. 重啟GitLab
# gitlab-ctl reconfigure
重啟後原本80port就不能訪問了,改用1234 port訪問

更改Gitlab倉庫的port

https://stackoverflow.com/a/26935369  Gitlab with non-standard SSH port (on VM with Iptable forwarding)
修改 /etc/gitlab/gitlab.rb 
gitlab_rails['gitlab_shell_ssh_port'] = 766
重新配置gitlab:
# gitlab-ctl reconfigure

然後在gitlab後台可以看到新的倉庫地址,如: ssh://git@git.domain.com:766/group/project.git

測試gitlab是否能連上

$ ssh git@gitlab.com
Welcome to GitLab, @root
Connection to gitlab.com closed.



安裝nginx
# yum install nginx
啟動
# service nginx restart
http://xx.xx:1234/ => 開啟gitlab
http://xx.xx/ => 開啟nginx,與gitlab不衝突
CentOS 7 預設nginx的路徑在 /usr/share/nginx/html

設vhost
https://www.digitalocean.com/community/tutorials/how-to-set-up-nginx-server-blocks-on-centos-7
建立vhost路徑
# mkdir -p /var/www/example.com/html
編輯首頁內容
# vim /var/www/example.com/html/index.html  # 隨便寫些內容進去
模仿ubuntu格式建立sites-available和sites-enabled資料夾
# mkdir /etc/nginx/sites-available
# mkdir /etc/nginx/sites-enabled
編輯nginx.conf
# vim /etc/nginx/nginx.conf
http {}  區塊之間加入
include /etc/nginx/sites-enabled/*.conf; 
server_names_hash_bucket_size 64;
編輯vhost設定檔
# vim /etc/nginx/sites-available/example.com.conf
server {
    listen  80;

    server_name example.com www.example.com;

    location / {
        root  /var/www/example.com/html;
        index  index.html index.htm;
        try_files $uri $uri/ =404;
    }

    error_page  500 502 503 504  /50x.html;
    location = /50x.html {
        root  /usr/share/nginx/html;
    }
}
啟用設定檔
# ln -s /etc/nginx/sites-available/example.com.conf /etc/nginx/sites-enabled/example.com.conf
重啟nginx
# service nginx restart

同時開 http://example.com (記得新增DNS或hosts) 和原本網址,可以開啟兩個不同位置的程式

將A主機gitlab的project複製到B主機
A主機gitlab版本 GitLab 7.4.3
B主機gitlab版本 GitLab Community Edition 8.1.2
1. B主機gitlab登入後 => Profile Settings => SSH Keys => ADD SSH KEY => 將 A主機gitlab登陸用戶同名的SSH公鑰 cat ~/.ssh/id_rsa.pub  新增到B主機gitlab SSH Keys上
2. Dashboard => Projects => NEW PROJECT => Import project fromANY REPO BY URL =>
這時候把A主機的git網址貼上,如: http(s)://username:password@gitlab.company.com/group/project.git

即可導入
ps. 這方法雖然方便,但是當專案一大之後導入時間過久

安裝Percona 5.6
https://www.percona.com/doc/percona-server/5.6/installation/yum_repo.html
安裝Percona yum repository ( percona-release )
# yum install http://www.percona.com/downloads/percona-release/redhat/0.1-3/percona-release-0.1-3.noarch.rpm
測試 repository
# yum search percona
...
Percona-Server-server-55.x86_64 : Percona Server: a very fast and reliable SQL database server
Percona-Server-server-56.x86_64 : Percona Server: a very fast and reliable SQL database server
Percona-Server-server-57.x86_64 : Percona Server: a very fast and reliable SQL database server
...
安裝
# yum install Percona-Server-server-56
===============================================================================================================================
 Package                             Arch              Version                         Repository                         Size
===============================================================================================================================
Installing:
 Percona-Server-server-56            x86_64            5.6.28-rel76.1.el7              percona-release-x86_64             18 M
 Percona-Server-shared-56            x86_64            5.6.28-rel76.1.el7              percona-release-x86_64            652 k
     replacing  mariadb-libs.x86_64 1:5.5.44-1.el7_1
Installing for dependencies:
 Percona-Server-client-56            x86_64            5.6.28-rel76.1.el7              percona-release-x86_64            5.8 M
 libaio                              x86_64            0.3.109-13.el7                  base                               24 k
 perl-Data-Dumper                    x86_64            2.145-3.el7                     base                               47 k
檢查裝好了沒
# rpm -qa | grep Percona
Percona-Server-client-56-5.6.28-rel76.1.el7.x86_64
Percona-Server-shared-56-5.6.28-rel76.1.el7.x86_64
Percona-Server-server-56-5.6.28-rel76.1.el7.x86_64

調大linode的swap
由 Arch swap心得 得知,swap大小為ram兩倍(2GB)為佳,如何提高linode上的swap呢?
登陸linode後台 => 先將linode關機(Shut down) => 先將ext4的空間按Edit調小 ( CentOS 7 Disk (24064 MB, ext4) )成24064-1024-512=22528MB  => 將swap空間按Edit調大 ( 512MB Swap Image (512 MB, swap) )成2048MB => 重新啟動(Boot)機器


安全

因為用戶是透過ssh去gitlab拉取code的,所以要對sshd服務做些限制,以免外部IP惡意去try伺服器22端口(你ssh登錄成功後會跟你說之前失敗登錄了幾次和最後一次失敗的IP、時間)
https://unix.stackexchange.com/a/406264  +  https://unix.stackexchange.com/a/490120  Limit SSH access to specific clients by IP address

編輯 sshd_config 檔案

AllowUsers johndoe@192.168.1.* admin2@192.168.1.*
編輯後需重啟sshd服務

使用TCP包裝器(Using TCP wrappers)

/etc/hosts.deny 禁止所有主機

sshd : ALL

/etc/hosts.allow 加白

sshd : 192.168.1.0/24
sshd : x.x.x.x


gitlab 啟動失敗

https://stackoverflow.com/a/65439226   Gitlab Cannot start runsv is not running
# gitlab-ctl restart
fail: gitlab-git-http-server: runsv not running
fail: logrotate: runsv not running
fail: nginx: runsv not running
fail: postgresql: runsv not running
fail: redis: runsv not running
fail: sidekiq: runsv not running
fail: unicorn: runsv not running

原因: 

gitlab-runsvdir 沒跑
# systemctl status gitlab-runsvdir
● gitlab-runsvdir.service - GitLab Runit supervision process
   Loaded: loaded (/usr/lib/systemd/system/gitlab-runsvdir.service; disabled; vendor preset: disabled)
   Active: inactive (dead)

解法

# systemctl start gitlab-runsvdir
# gitlab-ctl restart
ok: run: gitlab-git-http-server: (pid 24475) 1s
ok: run: logrotate: (pid 24481) 0s
ok: run: nginx: (pid 24488) 1s
ok: run: postgresql: (pid 24493) 0s
ok: run: redis: (pid 24502) 0s
ok: run: sidekiq: (pid 24507) 0s
ok: run: unicorn: (pid 24513) 0s



參考資料:
https://about.gitlab.com/downloads/#centos7 Install a GitLab CE Omnibus package on CentOS 7 (and RedHat/Oracle/Scientific Linux 7)
http://linode.blog6.info/install-os/installcentos2 Linode VPS 2-2 安裝CentOS作業系統
http://serverfault.com/questions/585528/set-gitlab-external-web-port-number Set gitlab external web port number







2015年10月28日 星期三

PHP使用fsockopen() post資料到URL並取得內容

在 php使用curl上傳檔案 中,PHP使用curl去呼叫API,
在ubuntu系統中 curl必須安裝 php5-curl
除了curl還有另一種方法,使用fsockopen()

測試API接口
http://test.localhost/PHP/wget_server.php
echo json_encode($_REQUEST);

stackoverflow的範例
// http://stackoverflow.com/questions/2367458/php-post-data-with-fsockopen
$fp = fsockopen('test.localhost', 80); // 必須寫 test.localhost。不能加protocol、位址和參數 http://test.localhost/PHP/wget_server.php?bear=2

// post的資料
$vars = array(
    'hello' => 'world',
    'bear' => 123
);
$content = http_build_query($vars); // $content:hello=world&bear=123

fputs($fp, "POST /PHP/wget_server.php?bear=2 HTTP/1.1\r\n"); // 位址和參數寫在這,使用POST
fputs($fp, "Host: test.localhost\r\n"); //再寫一次host
fputs($fp, "Content-Type: application/x-www-form-urlencoded\r\n");
fputs($fp, "Content-Length: ".strlen($content)."\r\n");
fputs($fp, "Connection: close\r\n");
fputs($fp, "Referer: https://www.google.com.tw/?gws_rd=ssl\r\n"); //偽造 $_SERVER['HTTP_REFERER']
fputs($fp, "Cookie: test=456;ai=789\r\n"); // 設定$_COOKIE
fputs($fp, "\r\n");

fputs($fp, $content);

header('Content-type: text/plain'); // 設定Content-Type為純文字
while (!feof($fp)) {
    echo fgets($fp, 1024);
}

fclose($fp);
結果:
HTTP/1.1 200 OK
Date: Wed, 28 Oct 2015 07:16:26 GMT
Server: Apache/2.4.12 (Win32) OpenSSL/1.0.1l PHP/5.6.8
X-Powered-By: PHP/5.6.8
Content-Length: 30
Connection: close
Content-Type: text/html; charset=UTF-8

{"bear":"123","hello":"world"}
$_SERVER['HTTP_REFERER']:https://www.google.com.tw/?gws_rd=ssl
$_COOKIE:Array
(
    [test] => 456
    [ai] => 789
)

說明:
Content-Type,内容类型,一般是指网页中存在的Content-Type,用于定义网络文件的类型和网页的编码,决定浏览器将以什么形式、什么编码读取这个文件
如果未指定 ContentType,默认为TEXT/HTML
指令為text/plain 後,瀏覽器會直接顯示<tag>而不會視為HTML

blog.hsin.tw 的範例
//接收POST參數的URL
$url = "http://test.localhost/PHP/wget_server.php?bear=2";

//POST參數,在這個陣列裡,索引是name,值是value,沒有限定組數
$postdata = array('post_name' => 'post_value', 'acc' => 'hsin', 'nick' => 'joe');

//函式回覆的值就是取得的內容
$result = sendpost($url, $postdata);

echo "\$result:";
print_r($result);

function sendpost($url, $data) {
    
    //先解析url 取得的資訊可以看看http://www.php.net/parse_url
    $url = parse_url($url);
    $url_port = $url['port'] == '' ? (($url['scheme'] == 'https') ? 443 : 80) : $url['port'];
    if (!$url) return "couldn't parse url";
    echo "\$url_port:".$url_port;
    echo "\$url:";
    print_r($url);
    
    //對要傳送的POST參數作處理
    $encoded = "";
    while (list($k, $v) = each($data)) {
        $encoded.= ($encoded ? '&' : '');
        $encoded.= rawurlencode($k) . "=" . rawurlencode($v);
    }

    echo "$encoded:";
    print_r($encoded);
    
    //開啟一個socket
    $fp = fsockopen($url['host'], $url_port);
    if (!$fp) return "Failed to open socket to " . $url['host'];
    
    //header的資訊
    fputs($fp, 'POST ' . $url['path'] . ($url['query'] ? '?' . $url['query'] : '') . " HTTP/1.0\r\n");
    fputs($fp, "Host: " . $url['host'] . "\n");
    fputs($fp, "Content-type: application/x-www-form-urlencoded\n");
    fputs($fp, "Content-length: " . strlen($encoded) . "\n");
    fputs($fp, "Connection: close\n\n");
    fputs($fp, $encoded . "\n");
    
    //取得回應的內容
    $line = fgets($fp, 1024);
    if (!eregi("^HTTP/1.. 200", $line)) return;
    $results = "";
    $inheader = 1;
    while (!feof($fp)) {
        $line = fgets($fp, 2048);
        if ($inheader && ($line == "\n" || $line == "\r\n")) {
            $inheader = 0;
        } 
        elseif (!$inheader) {
            $results.= $line;
        }
    }
    fclose($fp);
    return $results;
}
結果:
$url_port:80
$url:Array
(
    [scheme] => http
    [host] => test.localhost
    [path] => /PHP/wget_server.php
    [query] => bear=2
)
$encoded:post_name=post_value&acc=hsin&nick=joe
$result:{"bear":"2","post_name":"post_value","acc":"hsin","nick":"joe"}

說明:
該網站上的範例有誤,某些換行特殊符號忘記跳脫,如
if($inheader&&($line == "n" || $line == "rn")){
應為
if($inheader&&($line == "\n" || $line == "\r\n")){

\r\n通常是微軟的文件會產生的,在其它的編輯器裡面,會在一行的節尾看到 ^M,那就是\r\n
在php中,字串中的\r\n或\n,要用雙引號才有效。
'\r'是回車,'\n'是換行,前者使光標到行首,後者使光標下移一格。通常用的Enter是兩個加起來

參考資料:
http://blog.hsin.tw/2009/php-post-method-fsockopen/ php 傳送POST到別的URL並取得回應內容 使用fsockopen
http://stackoverflow.com/questions/2367458/php-post-data-with-fsockopen PHP Post data with Fsockopen
http://www.t086.com/code/php/function.php-fsockopen.php 函数:fsockopen()
http://seacatcry.pixnet.net/blog/post/13732061-%E3%80%90%E8%BD%89%E8%B2%BC%E3%80%91%5Cr%5Cn%E5%92%8C%5Cn%E7%9A%84%E5%B7%AE%E7%95%B0 【轉貼】\r\n和\n的差異


2015年10月23日 星期五

PHPExcel 使用心得


如果Excel欄位的數字長度太長(如身分證號、訂單編號)。造成資料儲存問題
http://stackoverflow.com/questions/5753469/problem-reading-numbers-from-excel-with-phpexcel
解法:
ini_set("precision", "20");  // 預設值是12




PHP Array 相加與 Array_merge


例:
$arr_a = array('a'=>1, 'b'=>2, 1=>3);
$arr_b = array('b'=>1, 4, 5);

print_r(array_merge($arr_a, $arr_b));

$arr_c = $arr_a + $arr_b;
print_r($arr_c);
結果:
array_merge($arr_a, $arr_b):Array
(
    [a] => 1
    [b] => 1
    [0] => 3
    [1] => 4
    [2] => 5
)

$arr_c:Array
(
    [a] => 1
    [b] => 2
    [1] => 3
    [0] => 4
)

結論:
array_merge() - key 相同=>後蓋前。沒有 key (流水號 key)的值,則會以附加在尾端 (append) 的方式合併上去,而所有流水號 key 的 index 則會重排
array + array - 有 key 的值的部分是相反的前蓋後,而沒有 key(流水號 key)的部分也會前蓋後,流水號 index 不會重排


參考資料:
http://blog.hsatac.net/2012/11/php-array-plus-array-versus-array-merge/ PHP Array 相加與 Array_merge

2015年10月22日 星期四

在windows和linux使用網路共享資料夾( samba 和cifs),非NFS

因為在 在Windows 7 使用docker  中要配置Virtual Box和windows 7 的共享資料夾失敗
所以改用samba和cifs的方式

在windows上產生共享資料夾
1. 開啟網路和共用中心 => 變更進階共用設定 => 開啟網路探索  /  開啟檔案及印表機共用 => 保存
2. 右鍵你要共享的資料夾 => 內容 => 共用 => 進階共用 => 勾選"共用此資料夾" => 權限 =>
選 Everyone 後下方勾選 "完全控制"
3. 右鍵你要共享的資料夾 => 內容 => 安全性 => 編輯 => 新增 => 輸入物件名稱來選取 填入 Everyone => 確定

在linux上存取windows的共享資料夾
$ sudo apt-get install cifs-utils
$ mkdir ~/windows_shared
掛載
$ sudo mount.cifs //192.168.169.140/test_shared /home/bear/windows_shared -o gid=1000,uid=1000

$ sudo mount -t cifs -o uid=1000,gid=1000 //192.168.169.140/test_shared /home/bear/windows_shared
注意: -o 後面的參數需以逗號(,)分開,不能用空白
查uid 和 gid
$ id
必須指定uid和gid,否則資料夾權限在linux會變成root:root

卸載
$ sudo umount -a -t cifs -l windows_shared/

NFS和samba的差異
In a closed network (where you know every device), NFS is a fine choice.
在封閉網路中( 你知道每個裝置 ),NFS是不錯的選擇
It might look more complicated than it really is but it's solid, predictable and fast. Something you can't level against Samba... At least, in my experience.
NFS比較複雜,但他可靠的、可預見的和快速的。有些事情無法和Samba放在同一層級比較
Samba will probably be a bit slower but is easy to use, and will work with windows clients as well..
Samba 可能會慢一點,但容易使用。並且能與windows端工作

$ sudo apt-get install nfs-kernel-server => 看字面就知道意思 nfs已經算linux模組了
samba其實就是網路芳鄰

在Linux上產生共享資料夾
安裝samba
# sudo apt-get install samba
建立samba的使用者和密碼
# smbpasswd -a bear
建立共享資料夾
$ mkdir ~/ubuntu_shared
編輯設定檔
# vim /etc/samba/smb.conf
格式:
[<folder_name>]
path = /home/<user_name>/<folder_name>
available = yes
valid users = <user_name>
read only = no
browsable = yes
public = yes
writable = yes
Ex.
[ubuntu_shared]
path = /home/bear/ubuntu_shared
available = yes
valid users = bear
read only = no
browsable = yes
public = yes
writable = yes
重啟samba
# service smbd restart

在windows上存取linux的共享資料夾
(假設要建在桌面)在桌面上點右鍵 => 新增 => 捷徑 =>
輸入項目的位置:
\\192.168.169.147\ubuntu_shared

之後便可在sublime 將該捷徑加入project

Windows開分享資料夾給Windows(以前的網路上的芳鄰、網芳)
https://support.microsoft.com/zh-tw/kb/2702421
先在資料夾上點右鍵 => 內容 => 共用 => 共用(按鈕) => 新增Everyone => 共用(按鈕) => 完成
其他電腦嘗試連接該共用資料夾時,出現詢問帳號密碼的對話視窗,若您沒有共用資料夾電腦上的使用者名稱及密碼是無法順利存取該資料夾。
問題的發生原因
這是因為預設是啟用以密碼保護共用,所以共用對象若沒有共用資料夾所在之電腦的使用者帳戶和密碼,即便是開放 Everyone 仍無法存取共用的檔案。
問題的解決方法
控制台 => 網路和網際網路 => 網路和共用中心 => 變更進階共用設定 => ( 家用或工作場所 => 以密碼保護的共用 )選取"關閉以密碼保護的共用"
取消共用
資料夾上點右鍵 => 內容 => 共用 => 進階共用(按鈕) => 取消勾選"共用此資料夾"

參考資料:
http://www.howtogeek.com/176471/how-to-share-files-between-windows-and-linux/ How to Share Files Between Windows and Linux
http://stackoverflow.com/questions/74626/how-do-you-force-a-cifs-connection-to-unmount  How do you force a CIFS connection to unmount
http://askubuntu.com/questions/7117/which-to-use-nfs-or-samba  Which to use NFS or Samba?









PHP和javascript 的 hex、byte陣列、string轉換

某次使用SlowAES  函數cryptoHelpers.generateSharedKey(8)產生的iv經過base64加密後的結果如下
R1We4y0JRP5w06Z8tUBPAw==

先看 https://code.google.com/p/slowaes/source/browse/trunk/js/cryptoHelpers.js?r=33 的 generateSharedKey 怎麼產生的
generateSharedKey:function(len)
{
 if(len === null)
  len = 16;
 var key = [];
 for(var i = 0; i < len*2; i++)
  key.push(this.getRandom(0,255));
 return key;
}
產生長度為8*2 ,內容為 0-255 的 byte 陣列

使用 cryptoHelpers.js 對他做處理,觀察各個型態的內容
// need include cryptoHelpers.js
var base64 = 'R1We4y0JRP5w06Z8tUBPAw==';
console.log(cryptoHelpers.base64.decode(base64)); // base64 decode => byte array
console.log(cryptoHelpers.convertByteArrayToString(cryptoHelpers.base64.decode(base64))); // to string
console.log(cryptoHelpers.toHex(cryptoHelpers.base64.decode(base64))); // to hex

結果:
cryptoHelpers.base64.decode(base64):[71, 85, 158, 227, 45, 9, 68, 254, 112, 211, 166, 124, 181, 64, 79, 3]
cryptoHelpers.convertByteArrayToString(cryptoHelpers.base64.decode(base64)):GUžã- DþpÓ¦|µ@O
cryptoHelpers.toHex(cryptoHelpers.base64.decode(base64)):47559ee32d0944fe70d3a67cb5404f03

使用PHP處理
$iv = 'R1We4y0JRP5w06Z8tUBPAw==';

echo "
base64_decode(\$iv):".base64_decode($iv); // base64 decode => binary string

$iv64 = base64_decode($iv);
echo "
strlen(\$iv64):".strlen($iv64);  // 長度16
echo "
pack('H*', bin2hex(\$iv64)):".pack('H*', bin2hex($iv64)); // 與base64_decode($iv)結果相同
echo "
bin2hex(\$iv64):".bin2hex($iv64); // to hex

// to bytes array
for ($i=0; $i < strlen($iv64); $i++) { 
    $data[] = ord(substr($iv64,$i,1)); // 使用ord將字元轉成int
}
echo "
\$data:";
print_r($data);
echo "
";

結果:
base64_decode($iv):GU��- D�pӦ|�@O
strlen($iv64):16
pack('H*', bin2hex($iv64)):GU��- D�pӦ|�@O
bin2hex($iv64):47559ee32d0944fe70d3a67cb5404f03
$data:Array
(
    [0] => 71
    [1] => 85
    [2] => 158
    [3] => 227
    [4] => 45
    [5] => 9
    [6] => 68
    [7] => 254
    [8] => 112
    [9] => 211
    [10] => 166
    [11] => 124
    [12] => 181
    [13] => 64
    [14] => 79
    [15] => 3
)

還可以去 Unicode/UTF-8-character table 做字元最後的檢查
http://dev.networkerror.org/utf8/?start=0&end=255&cols=4&search=&show_uni_int=on&show_uni_hex=on&show_html_ent=on&show_raw_hex=on&show_raw_bin=on  0-255 的 Unicode Number (int) / Unicode Number (hex) / Char
http://www.scarfboy.com/coding/unicode-tool?s=000047  以hex 搜尋字元

參考資料:
http://stackoverflow.com/questions/11044802/php-hex-and-binary PHP Hex and Binary






2015年10月20日 星期二

在Windows 7 使用docker

下載安裝 docker-install.exe , 如遇上這個錯誤:
https://github.com/boot2docker/windows-installer/issues/67
則移除VirtualBox 後重裝 VirtualBox 4.3.12 版本,重啟boot2docker-vm

中間為了讓virtualbox 與Win7共用資料夾 ( 失敗 )
http://askubuntu.com/questions/456400/why-cant-i-access-a-shared-folder-from-within-my-virtualbox-machine
必須從 C:\Program Files\Oracle\VirtualBox\VBoxGuestAdditions.iso 安裝 Guest Additions
在開機前先設定掛載的光碟
然後設定共享文件夾
開機進入VM後
1. I had to manually mount the CD:
$ sudo mount /dev/cdrom /media/cdrom
掛載之後 /media/cdrom 下面就有光碟內的東西了
/media/cdrom# ls
32Bit  AUTORUN.INF  cert  runasroot.sh            VBoxSolarisAdditions.pkg        VBoxWindowsAdditions.exe
64Bit  autorun.sh   OS2   VBoxLinuxAdditions.run  VBoxWindowsAdditions-amd64.exe  VBoxWindowsAdditions-x86.exe

2. Install the necessary packages:
$ sudo apt-get install make gcc linux-headers-$(uname -r)
3. Install the Guest Additions:
$ sudo /media/cdrom/VBoxLinuxAdditions.run
中間報錯
...
The headers for the current running kernel were not found. If the following
module compilation fails then this could be the reason.

Building the main Guest Additions module ...done.
Building the shared folder support module ...fail!
(Look at /var/log/vboxadd-install.log to find out what went wrong)
...
Setting up the Window System to use the Guest Additions ...done.
You may need to restart the hal service and the Window System (or just restart
the guest system) to enable the Guest Additions.

Installing graphics libraries and desktop services components ...done.

嘗試mount共享資料夾
$ mkdir new
$ sudo mount -t vboxsf New ~/new
/sbin/mount.vboxsf: mounting failed with the error: No such device

嘗試用其他方法解
http://stackoverflow.com/questions/28328775/virtualbox-mount-vboxsf-mounting-failed-with-the-error-no-such-device
# cd /opt/VBoxGuestAdditions-4.3.12/init/
# ./vboxadd setup
與跑 VBoxLinuxAdditions.run 一樣的錯誤

嘗試解決 Error: kernel headers not found.的問題
http://askubuntu.com/questions/98416/error-kernel-headers-not-found-but-they-are-in-place
# apt-get install xserver-xorg xserver-xorg-core
安裝了以下東西
The following NEW packages will be installed:
  libmtdev1 libxi6 libxinerama1 xserver-xorg xserver-xorg-input-all
  xserver-xorg-input-evdev xserver-xorg-input-mouse
  xserver-xorg-input-synaptics xserver-xorg-input-vmmouse
  xserver-xorg-input-wacom
0 upgraded, 10 newly installed, 0 to remove and 1 not upgraded.

嘗試解決The headers for the current running kernel were not found.
http://linuxconfig.org/ubuntu-the-headers-for-the-current-running-kernel-were-not-found-solution
# apt-get install linux-headers-`uname -r` dkms build-essential
報錯:
...
The following packages have unmet dependencies:
 build-essential : Depends: libc6-dev but it is not going to be installed or
                            libc-dev
                   Depends: g++ (>= 4:4.4.3) but it is not going to be installed
E: Unable to correct problems, you have held broken packages.

原因:
# apt-get install libc6-dev
...
libc6-dev : Depends: libc6 (= 2.19-0ubuntu6) but 2.19-0ubuntu6.5 is to be installed
E: Unable to correct problems, you have held broken packages.
# apt-show-versions libc6
libc6:amd64 2.19-0ubuntu6.5 newer than version in archive
看來是libc6 太新了
切勿移除libc6重裝,剛剛VM就這樣被我弄掛的。還好昨天剛好有備份

嘗試更新Virtual Box到最新版 4.3.30
更新到最新版要執行原本的虛擬電腦時報錯:
unable to load r3 module vboxdd.dll (vboxdd) getlasterror=1790 (verr_unresolved_error)
嘗試過這篇 http://blog.sina.com.cn/s/blog_4dc988240102vj8a.html 但無效
只好把Virtual Box再還原回4.3.12版


PHP 顯示syntax error


如果在寫php時忘記加分號( Semicolon ),訪問下面這個頁面會得到空白頁
ini_set("display_errors", "1");
error_reporting(E_ALL);
$bear = array(1,2,3)
如何顯示錯誤呢?
error_setting.php
ini_set("display_errors", "1");
error_reporting(E_ALL);
include 'error.php';
error.php
$bear = array(1,2,3)
echo "test";
訪問error_setting.php會得到錯誤訊息:
Parse error: syntax error, unexpected 'echo' (T_ECHO) in /var/www/html/test.vm/error.php on line 
但直接訪問error.php仍然會得到空白頁

20160519 找到原因
修改 /etc/php5/fpm/php.ini
error_reporting = E_ALL
error_reporting = E_ALL & ~E_NOTICE & ~E_STRICT #忽略notice和Strict Standards錯誤,但會報Fatal錯誤
syntaxfatal 的error log 與  /etc/php5/fpm/php-fpm.conf 無關,與nginx /etc/nginx/sites-available/test.vm 的 error_log /var/www/html/test.vm/logs/error.log; 有關
這樣畫面上和error log 都有錯誤出現
http://stackoverflow.com/questions/1911920/does-php-error-reporting0-affect-error-logging-or-just-display
結論:看來沒辦法只顯fatal和syntax和notice示錯誤在error log,除非用error_log()或用框架

參考資料:
http://stackoverflow.com/questions/16933606/error-reportinge-all-does-not-produce-error  error_reporting(E_ALL) does not produce error




2015年10月14日 星期三

AES 加密心得

前言:要跟java那邊用AES加密後的資料對接

在網路上找到許多不同語言的實作方式,但加密出來的結果都不一樣

原來是 AES encryption 有以下幾種mode

● ECB should not be used if encrypting more than one block of data with the same key.
當使用相同key加密一個block以上的資料時,ECB不應該被使用
● CBC, OFB and CFB are similar, however OFB/CFB is better because you only need encryption and not decryption, which can save code space.
CBC, OFB 和CFB類似。OFB/CFB比較好,因為你只需要加密不需要解密,以減少code的量
● CTR is used if you want good parallelization (ie. speed), instead of CBC/OFB/CFB.
當你想要好的平行處理(如:速度),使用CTR。而不是CBC/OFB/CFB。
The most important caveat with CTR mode is that you never, ever re-use the same counter value with the same key. If you do so, you have effectively given away your plaintext. ( http://stackoverflow.com/questions/4951468/ctr-mode-use-of-initial-vectoriv CTR mode use of Initial Vector(IV) )
最重要的是,你不要重複使用相同的key(IV,隨機產生),如果你這樣做,你已有效地給了你的明文
● XTS mode is the most common if you are encoding a random accessible data (like a hard disk or RAM).
XTS用在硬碟和RAM上
● OCB is by far the best mode, as it allows encryption and authentication in a single pass. However there are patents on it in USA.
OCB最好,因為他允許加密和認證在單通道。然而美國擁有其專利。( 所以意味著你在網路上找不到實作他的code )

你必須每次都用獨特的IV去加密,如果你不能保證他的隨機性,請用只需要隨機數(非IV)的OCB。固定的IV使得人們能不斷的猜測下一個,隨機數能避免這個風險

初始向量 Initialization vector (IV) 可被公開
http://ijecorp.blogspot.com/2013/08/python-m2crypto-aes-encrypt-decrypt.html
IV 本身並不需要保護,它是可以被公開的。而IV的最大長度必須是 16 bytes,而且產生IV的方式必須是無法預測的,也就是隨機產生即可。
http://stackoverflow.com/questions/8804574/aes-encryption-how-to-transport-iv
There is no security hole by sending the IV in clear text - this is similar to storing the salt for a hash in clear: As long as the attacker has no controll over the IV/salt, and as long as it is random, there is nor problem.
用明文傳送IV沒有安全的漏洞。這就像你做hash加了salt一樣,只要攻擊者無法掌握IV(salt)並且他是隨機的,就不會有問題。

使用php做AES CBC 128 pkcs5padding加密
$value = "张根";
$key = "Bar12345Bar12345"; //16 Character Key

echo strToHex('张根'); // hex: e5bca0e6a0b9

$encrypted = getEncrypt($value, $key);
echo "\n\$encrypted:".$encrypted;
echo "\n\getDecrypt(\$encrypted, \$key):".getDecrypt($encrypted, $key);

function pkcs5_pad ($text, $blocksize) { // https://github.com/stevenholder/PHP-Java-AES-Encrypt/blob/master/security.php
 $pad = $blocksize - (strlen($text) % $blocksize); 
 return $text . str_repeat(chr($pad), $pad); 
} 

function getEncrypt($sStr, $sKey) { // http://stackoverflow.com/questions/4537099/problem-with-aes-256-between-java-and-php
 global $iv;
 $sStr = pkcs5_pad($sStr, 16); // 這個16是 mcrypt_get_block_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC) 的結果
 echo "\n\$sStr:".$sStr;  // 測試pkcs5 padding的結果
 // 產生$iv,如果用class寫,可以避免全域變數
 // $iv = mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_ECB), MCRYPT_RAND);
 // echo "\n\$iv:".$iv; // 這是隨機產生的內容
  return base64_encode( // 用bin2hex()亦可,但解密時要用hex2bin()
    mcrypt_encrypt(
        MCRYPT_RIJNDAEL_128, 
        $sKey,
        $sStr,
        MCRYPT_MODE_CBC,
        "ThisIsASecretKet" // $iv,測試時寫死
    )
  );
}

function getDecrypt($sStr, $sKey) {
 global $iv; // 要與 getEncrypt()產生的$iv一致,才能解出來
  return mcrypt_decrypt(
    MCRYPT_RIJNDAEL_128, 
    $sKey, 
    base64_decode($sStr), // 加密時用bin2hex(),則解密時要用hex2bin()
    MCRYPT_MODE_CBC,
    "ThisIsASecretKet"
  );
}

function strToHex($string) // http://ditio.net/2008/11/04/php-string-to-hex-and-hex-to-string-functions/
{
    $hex='';
    for ($i=0; $i < strlen($string); $i++)
    {
        $hex .= dechex(ord($string[$i]));
    }
    return $hex;
}

結果:
e5bca0e6a0b9 //這邊是utf8中文字轉hex的結果,如果其他地方字串轉hex不是這個代表他們的字串原本編碼不是utf-8
$sStr:张根 // pkcs5 padding的結果
$encrypted:LE/jvtjPWJk7qJc49Xl3eQ== // aes加密後 base64_decode()的結果
\getDecrypt($encrypted, $key):张根 // aes解密結果

說明:
因為java那邊只能用 128bit,所以只能選 MCRYPT_RIJNDAEL_128
$iv = Initial Vector(IV) 初始向量
在 https://github.com/stevenholder/PHP-Java-AES-Encrypt/blob/master/security.php 範例中,我們可以看到他加密不是用 mcrypt_encrypt 而是
public static function encrypt($input, $key) {
    $size = mcrypt_get_block_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_ECB);
    $input = Security::pkcs5_pad($input, $size);
    $td = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_ECB, '');
    $iv = mcrypt_create_iv(mcrypt_enc_get_iv_size($td), MCRYPT_RAND);
    mcrypt_generic_init($td, $key, $iv);
    $data = mcrypt_generic($td, $input);
    mcrypt_generic_deinit($td);
    mcrypt_module_close($td);
    $data = base64_encode($data);
    return $data;
}
mcrypt_generic() => 低階API ,更有彈性
mcrypt_encrypt() => 高階工具( higher-level utility )
參考資料:
http://stackoverflow.com/questions/2773535/mcrypt-generic-vs-mcrypt-encrypt mcrypt_generic vs mcrypt_encrypt

http://php.net/manual/en/function.mcrypt-encrypt.php
string mcrypt_encrypt ( string $cipher , string $key , string $data , string $mode [, string $iv ] )
mode的格式是 MCRYPT_MODE_modename ,modename可使用"ecb", "cbc", "cfb", "ofb", "nofb" or "stream"
如: MCRYPT_MODE_ECB

因為mcrypt_encrypt() 出來的結果打印是二進位亂碼,所以都用 bin2hex()或base64_encode()去轉換一次

報錯:
Warning: mcrypt_encrypt(): Key of size 15 not supported by this algorithm. Only keys of sizes 16, 24 or 32 supported
如果你出現這個錯誤,請把$key補到16位(或24, 32位)
$key=$key."\0"; //缺幾位就補幾個


PHP範例參考資料:
https://github.com/stevenholder/PHP-Java-AES-Encrypt/blob/master/security.php 使用pkcs5_pad()方法
http://stackoverflow.com/questions/4537099/problem-with-aes-256-between-java-and-php  getEncrypt($sStr, $sKey)和getDecrypt($sStr, $sKey) 原型

使用Java做AES CBC 128 pkcs5padding加密
import java.io.UnsupportedEncodingException;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

import org.apache.commons.codec.binary.Base64;

public class Encryptor {
    public static String encrypt(String key1, String key2, String value) {
        try {
            IvParameterSpec iv = new IvParameterSpec(key2.getBytes("UTF-8"));

            SecretKeySpec skeySpec = new SecretKeySpec(key1.getBytes("UTF-8"),
                    "AES");
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
            cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv);
            byte[] encrypted = cipher.doFinal(value.getBytes());
            System.out.println("encrypted string:"
                    + Base64.encodeBase64String(encrypted));
            return Base64.encodeBase64String(encrypted);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return null;
    }

    public static String decrypt(String key1, String key2, String encrypted) {
        try {
            IvParameterSpec iv = new IvParameterSpec(key2.getBytes("UTF-8"));

            SecretKeySpec skeySpec = new SecretKeySpec(key1.getBytes("UTF-8"),
                    "AES");
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
            cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv);
            byte[] original = cipher.doFinal(Base64.decodeBase64(encrypted));

            return new String(original);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return null;
    }

    public static void main(String[] args) throws UnsupportedEncodingException {
        String key1 = "Bar12345Bar12345"; // 128 bit key
        String key2 = "ThisIsASecretKet";
        System.out.println(decrypt(key1, key2,
                encrypt(key1, key2, new String("张根".getBytes("utf-8")))));
        System.out.println(parseByte2HexStr("张根".getBytes("utf-8"))); // print "张根" utf-8 in hex
    }
    
    /**
     * 将16进制转换为二进制
     * 
     * @param hexStr
     * @return
     */
    public static byte[] parseHexStr2Byte(String hexStr) {
        if (hexStr.length() < 1)
            return null;
        byte[] result = new byte[hexStr.length() / 2];
        for (int i = 0; i < hexStr.length() / 2; i++) {
            int high = Integer.parseInt(hexStr.substring(i * 2, i * 2 + 1), 16);
            int low = Integer.parseInt(hexStr.substring(i * 2 + 1, i * 2 + 2), 16);
            result[i] = (byte) (high * 16 + low);
        }
        return result;
    }

    /**
     * 将二进制转换成16进制
     * 
     * @param buf
     * @return
     */
    public static String parseByte2HexStr(byte buf[]) {
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < buf.length; i++) {
            String hex = Integer.toHexString(buf[i] & 0xFF);
            if (hex.length() == 1) {
                hex = '0' + hex;
            }
            sb.append(hex.toLowerCase());
        }
        return sb.toString();
    }
}

結果:
encrypted string:LE/jvtjPWJk7qJc49Xl3eQ== // aes加密後 base64_decode()的結果,需與php結果一致
张根 // aes 解密結果
e5bca0e6a0b9 // "张根"utf8轉hex的結果

說明:
parseHexStr2Byte(String hexStr) 和 parseByte2HexStr(byte buf[]) 這兩個function在這邊純粹測試用,與加解密無關。可忽略
我遇到的最大難點之一就是,java和php 英文字加密出來結果一樣,但是中文加密結果不一樣。( 這邊23樓也遇到一樣問題: http://my.oschina.net/Jacker/blog/86383?p=3#comments  )
原因是java在加密前的中文編碼有問題。先用
System.out.println(parseByte2HexStr("张根".getBytes("utf-8")));
檢測hex是否相同
加密時在外面就轉成utf-8再加密
encrypt(key1, key2, new String("张根".getBytes("utf-8"))) 

如果你的cipher(密文),想要用No Padding,如
Cipher cipher = Cipher.getInstance("AES/CBC/NoPADDING");
這樣你要加密的明文( "张根" ) 必須改成16位的字,否則java會報錯
encrypt(key1, key2, new String("123456789012345".getBytes("utf-8"))) // 必須改成16位字串,如1234567890123456
報錯:
javax.crypto.IllegalBlockSizeException: Input length not multiple of 16 bytes

雖然php不做padding可以加密,但結果不一樣。為了配合java這邊,php那邊還是要做pkcs5 padding

原因:
java文件檔的編碼不是utf-8
解法:
eclipse => "左邊導航欄"你的"專案"上點右鍵 => Properties => Resource => Text file encoding 選
Other: UTF-8 ( 不要選Inherited from container (GBK) ) => OK ( 這樣你原本GBK的文件中文內容會變成亂碼,代表原本編碼錯誤 )


JAVA範例參考資料:
http://stackoverflow.com/questions/15554296/simple-java-aes-encrypt-decrypt-example Simple java AES encrypt/decrypt example

使用javascript ( SlowAES )做AES CBC 128 pkcs5padding加密
<!doctype html>
<html>
    <head>
        <meta charset="utf-8">
        <meta name="description" content="aes">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>aes</title>
        <script src="/jquery-1.10.2.min.js"></script>
        <script src="../js/aes.js"></script>
        <script src="../js/cryptoHelpers.js"></script>
        <script src="../js/jsHash.js"></script>
    </head>
    <body>
        <div id="output"></div>
        <script type="text/javascript">
        /**
        * An encryption setup to match our server-side one; see there for
        * documentation on it.
        **/
        function decrypt(input, key){
            var originalSize = 6;
            // var iv = 'R1We4y0JRP5w06Z8tUBPAw==';
            // iv = cryptoHelpers.base64.decode(iv); // 解密時需把base64加密後的iv解密成byte array
            var iv = "ThisIsASecretKet";
            iv = cryptoHelpers.convertStringToByteArray(iv);
            var cipherIn = input;
            // Set up encryption parameters
            var keyAsNumbers = cryptoHelpers.toNumbers( bin2hex( key ) );
            cipherIn = cryptoHelpers.base64.decode(cipherIn);
            var decrypted = slowAES.decrypt(
                cipherIn,
                slowAES.modeOfOperation.CBC,
                keyAsNumbers,
                iv
            );
            return cryptoHelpers.decode_utf8(cryptoHelpers.convertByteArrayToString(decrypted));
        }
        function encrypt( plaintext, key ){
            // Set up encryption parameters
            plaintext = cryptoHelpers.encode_utf8(plaintext);
            var inputData = cryptoHelpers.convertStringToByteArray(plaintext);
            var keyAsNumber = cryptoHelpers.toNumbers(bin2hex(key));
            var iv = cryptoHelpers.generateSharedKey(8); // 假設自動生成的iv做base64 encode加密後的結果是 R1We4y0JRP5w06Z8tUBPAw==
            var iv = "ThisIsASecretKet";
            iv = cryptoHelpers.convertStringToByteArray(iv);
            var encrypted = slowAES.encrypt(
                inputData,
                slowAES.modeOfOperation.CBC,
                keyAsNumber,
                iv
            );
            return cryptoHelpers.base64.encode(encrypted);
        }
        // Equivilent to PHP bin2hex
        function bin2hex (s) {
            var i, f = 0,
                a = [];
            s += '';
            f = s.length;
            for (i = 0; i < f; i++) {
                a[i] = s.charCodeAt(i).toString(16).replace(/^([\da-f])$/, "0$1");
            }
            return a.join('');
        }
        // Equivilent to PHP hex2bin
        function hex2bin(hex) {
            var str = '';
            for (var i = 0; i < hex.length; i += 2)
                str += String.fromCharCode(parseInt(hex.substr(i, 2), 16));
            return str;
        }
        /**
        * Some simple testing code
        **/
        $(function(){
            var key = "Bar12345Bar12345"; // key
            var plaintext = "张根";
            var output = "";
            var cipherText = encrypt(plaintext,key);
            var newPlaintext = decrypt(cipherText,key);
            output += ("<br>plaintext=" + plaintext);
            output += ("<br>cipherText=" + cipherText);
            output += ("<br>newPlaintext=" + newPlaintext);
            $('#output').html(output);
        });
        </script>
    </body>
</html>
結果:
plaintext=张根
cipherText=LE/jvtjPWJk7qJc49Xl3eQ==
newPlaintext=张根

如果要傳做過base64加密後的iv給php端,php端的iv要這樣設定,才能解密
$aes->set_iv(base64_decode($iv));

SlowAES的aes.js、cryptoHelpers.js、jsHash.js
https://code.google.com/p/slowaes/source/browse/trunk/js/

加密出來的結果要傳送
POST
1. 塞入表單後submit POST
GET
1. 塞入表單後submit GET
2. 組URL
url = $('#action').val()+"&aes_encrypt="+encodeURIComponent($('#reqParam').val())+"&iv="+$('#iv').val();
location.href = url;
必須用在字段上使用 encodeURIComponent 。
1. 勿組出url後再encodeURIComponent(url), 因為http:// 也會被encode
2. 使用encodeURI無效


其他java或php實作AES範例:
http://www.movable-type.co.uk/scripts/aes-php.html  Aes Ctr <PHP>
http://www.movable-type.co.uk/scripts/aes.html Aes Ctr <javascript> => github: https://github.com/chrisveness/crypto
http://aesencryption.net/ AES encryption <PHP/JAVA> =>Java驗證未過,可能是當初測時編碼問題
https://code.google.com/p/crypto-js/#AES crypto-js<javascript>
http://point-at-infinity.org/jsaes/ jsaes: AES in JavaScript <javascript>
http://www.cnblogs.com/yipu/articles/3871576.html [转]php与java通用AES加密解密算法 (最初對接成功的範例,但有java中文編碼問題) <PHP/JAVA>
https://github.com/stevenholder/PHP-Java-AES-Encrypt  PHP-Java-AES-Encrypt<PHP/JAVA>
http://www.java2s.com/Code/Java/Security/BasicIOexamplewithCTRusingAES.htm Basic IO example with CTR using AES : File Secure IO « Security « Java <JAVA>
http://magiclen.org/aes/ 在Java、Android、PHP實現AES加解密,並且互通的方式 <PHP/JAVA>

參考資料:
https://zh.wikipedia.org/wiki/%E9%AB%98%E7%BA%A7%E5%8A%A0%E5%AF%86%E6%A0%87%E5%87%86 高階加密標準
http://stackoverflow.com/questions/1220751/how-to-choose-an-aes-encryption-mode-cbc-ecb-ctr-ocb-cfb  How to choose an AES encryption mode (CBC ECB CTR OCB CFB)?