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使用者