2017年12月5日 星期二

laravel binding database config 機制

        今天需要在一個地方連續切換資料庫,所以連帶的也要設定database config,但發生db table not found的狀況,dd了config發現沒有設定錯誤。

        查了stackoverflow才知道,laravel似乎有個binding的機制,如果基於一開始的config建立連線,那這個連線就會持續存在,之後不管是eloquent還是db connection都會基於這個連線運行sql,即使之後調整了config也一樣:

$page = Page::find(1);

// 設定另外一個config
config([
    'database.connections.mysql.host' => $hostname,
    'database.connections.mysql.username' => $username,
    'database.connections.mysql.password' => $password,
    'database.connections.mysql.database' => $database,
    'database.connections.mysql.charset' => $char_set
]);

// 原本的db沒有orders table,發生錯誤
$order = Order::find(1);

        這裡的解決方式:

$page = Page::find(1);

// disconnect 連線
DB::disconnect('mysql');

// 設定另外一個config
config([
    'database.connections.mysql.host' => $hostname,
    'database.connections.mysql.username' => $username,
    'database.connections.mysql.password' => $password,
    'database.connections.mysql.database' => $database,
    'database.connections.mysql.charset' => $char_set
]);

// 原本的db沒有orders table,發生錯誤
$order = Order::find(1);

        這樣一來就會連接到之後設定的config了。


參考資料:

https://stackoverflow.com/questions/35466362/laravel-5-changing-database-name-in-runtime

2017年11月23日 星期四

array_filter的注意事項

        今天前端回報一個api的型別問題,發現有一個應該要return array的卻return成object了,
只有array型別是associative array的時候才會return成object,也就是一個key對一個value,但是之前寫的程式沒有任何key的設定,最後發現是array_filter的坑。

        以下是程式碼:

$ary = [1, 2, 3, 4, 5];

$ary = array_filter($ary, function($v) {
    return $v != 3;
});

echo json_encode($ary);

        當$content進入到array_filter之後,array_filter把不符合label的刪除,但同時也會幫你保留keys值的相對位置,進而把你的array變成associative array。

        也就是說結果變成:{"0":1,"1":2,"3":4,"4":5} 。

        所以這裡我們必須對array_filter產出的結果在處理一次:

$ary = [1, 2, 3, 4, 5];

$ary = array_filter($ary, function($v) {
    return $v != 3;
});

$ary = array_values($ary);

echo json_encode($ary);

        用array_values重新轉換成sequential array就行了。

        結果回到我們預想中的:[1,2,4,5]。


        參考資料:


2017年11月22日 星期三

CI/CD Journey from on-premise to AWS Cloud 筆記


  1. 講者推alb
  2. opsworks一堆坑,直接用ec2設定auto scaling
  3. 之前用opsworks,加掛monit做看門狗
  4.  cloudformation沒有提供隱藏或編碼db密碼的function,講者團隊自己寫一個lambda
  5. cliff推薦用serverless framework,對CI/CD會事半功倍

cliff => https://blog.clifflu.net/2016/09/lambda-container-reuse/

ps. 團隊用的是github+circleci+cloudformation做CI/CD

2017年11月14日 星期二

docker port無法釋出的問題

        已經停止所有的container了,但要起一台新的container卻一直無法成功,不然就說是有衝到port,查詢了之後可能是osx版本docker的network還可能佔有的樣子。

        https://github.com/docker/compose/issues/3277

        
         照著指示清除network就沒事了,主要可以先執行最後一行查看目前的port試試。

2017年9月10日 星期日

用docker快速建立開發環境 (php7.1 + mysql + apache)

        以下範例的程式碼都在github上。
     
        https://github.com/keepgoing147/docker-php7-apache-mysql

        安裝好docker之後,新建一個資料: test,cd 到test,新增docker-compose.yml,並新增以下code:

web:
  build: .
  links:
    - db
  ports:
    - 80:80
  volumes:
    - ./www/html:/var/www/html
    - ./src/your-project/:/var/www/html/your-project

db:
  image: mysql:latest
  restart: always
  volumes:
    - ./mysql:/var/lib/mysql
  environment:
    MYSQL_ROOT_PASSWORD: rootpassword
  ports:
    - 8887:3306

        這邊要注意yml編排格式。

        接著,在同樣的資料夾新增一個Dockerfile,並新增以下code:

FROM php:7.1.9-apache

RUN apt-get update
RUN docker-php-ext-install pdo pdo_mysql

        最後,新增src/your-project/index.php,index.php的內容打:

<?php

echo 'success execute in docker!';


        這個時候,你的資料夾結構應該會長這樣:


        執行:docker-compose up -d
         (-d是為了讓docker-compose命令在背景執行,不追蹤成功run起來之後的後續動作)

        會出現開始build image的訊息


        這次我們用了兩個image:php:7.1.9-apache, mysql:latest,所以會需要pull下來兩個image檔。

        

    執行完成之後,存取 http://localhost/your-project/ ,就會看見成果了:


        也可以再your-project裡面新增這段code來測試mysql是不是也架好了:

<?php
try {
    $db = new PDO(
       'mysql:host=db;dbname=docker-table;port=3306;charset=utf8',
      'root',
      'rootpassword'   );
} catch (PDOException $e) {
    echo 'DB connection failed';
   exit;
}

$data = $db->query('select * from comments');
$data = $data->fetchAll();
var_dump($data);

        osx上的話可以透過sequel pro去連接localhost:8887的db,去建立一個docker-table的database,再新增一個table為comments,去測試能否成功query就行了。

        結果會像這樣:

        連接成功!

2017年7月19日 星期三

「淺談WAF在AWS的架構」小聚筆記

新知:
  1. OWASP top10 => https://www.owasp.org/index.php/Category:OWASP_Top_Ten_Project
  2. CVE & NVD
  3. PCI DSS
  4. DVWA => 資安測試
需研究:
  1. aws vpc
  2. aws alb
  3. aws cloudfront cooperate with waf
        總結來說aws的waf以業界的waf廠商來看還不是這麼完善,像log只能追到三小時前,不然只能自己寫script去定時抓,或是per web ACL只能支援10條....etc,不過夾帶著aws可以跟其他service一起管理的優勢,以及之後aws一定會更完善waf的架構,市佔率一定也會穩定提升的吧。

        講者建議白皮書必讀,六月剛發行的。

2017年7月16日 星期日

介紹php array_map

        有時候需要對array做一點另外的處理,譬如為每一個value做另外的處理:

$nameAry = ['Hong', 'Peiwen'];

$helloNameAry = array();
foreach ($nameAry as $name) {
    $helloNameAry[] = 'hello, ' . $name;
}

var_dump($helloNameAry);

        幫每一個name都加上一個hello,結果會是:


        但這樣一來可讀性很低,而且需要多新增一個$helloNameAry的參數去存result。

        這時候array_map就能派上用場了:

function addHello($name)
{
    return 'hello, ' . $name;
}

$nameAry = ['Hong', 'Peiwen'];

$nameAry = array_map('addHello', $nameAry);

var_dump($nameAry);

       這裏array_map會遍歷所有array的value去跑callback function,這裡的callback function指的是addHello,這樣寫的話,不僅重新parse array value的function可以複用,而且寫起來也比較優雅。

       如果不想另外另一個function的話,也可以直接寫在array_map的parameter裡面:

$nameAry = ['Hong', 'Peiwen'];

$nameAry = array_map(function ($v) {
    return 'hello, ' . $v;
}, $nameAry);

var_dump($nameAry);

        $v就是遍歷$nameAry裏的value,結果都會是:



參考資料:

http://php.net/manual/en/function.array-map.php


2017年5月10日 星期三

zshrc搞壞的補救方法(zsh: command not found: vim)


        現在人大部分都會裝iterm2+zsh,而裝了zsh的關係,設定檔也會從.bashrc or .bash_profile變成.zshrc。

        今天要討論的就是萬一把.zshrc的PATH改壞了,而且又source它發生憾事的時候,該如何處理?

        首先當下一定會看到類似這個狀況:



        command明明就存在,但是zsh卻一直報錯,嘗試著用vim去編輯.zshrc也沒辦法,似乎就卡在這裡無法做任何事了。

        要解決這件事,首先我們可以先把zsh切換為原本的bash,用bash的環境去執行:

exec bash -l

        -l 就是login的意思,換成--login or -login也可以:   exec bash --login

        接著就可以用vim去把.zshrc修回來囉!


        source完變更之後,zsh又是一條好漢!



        參考資料:

http://stackoverflow.com/questions/10341271/switching-from-zsh-to-bash-on-osx-and-back-again

2017年4月10日 星期一

public vs protected vs private比較

        這是在php裡,最常看到三種宣告function的方式。

        下面用code做個簡單解說:

class Test
{
    public function public_function()
    {
        echo 'I am public function!';
    }

    protected function protected_function()
    {
        echo 'I am protected function!';
    }

    private function private_function()
    {
        echo 'I am private function!';
    }
}

$test = new Test();

$test->public_function();


       這三個宣告方式,如果要在class外面使用的話,只有public可以,因為public意思就是公眾化的,開放的,大家都可以任意取用的。

       而protected則是要透過parent class的用法,也就是extends就能調用了:

class Test
{
    public function public_function()
    {
        echo 'I am public function!';
    }

    protected function protected_function()
    {
        echo 'I am protected function!';
    }

    private function private_function()
    {
        echo 'I am private function!';
    }
}

class Test2 extends Test
{
    public function call_parents_function_protected()
    {
        $this->protected_function();
    }
}


$test = new Test();

$test->public_function();

$test2 = new Test2();

$test2->call_parents_function_protected();


        像這樣的方式,就能成功調用了!

        至於private在所處的class裡面才能調用:

class Test
{
    public function public_function()
    {
        echo 'I am public function!';
    }

    protected function protected_function()
    {
        echo 'I am protected function!';
    }

    private function private_function()
    {
        echo 'I am private function!';
    }

    public function call_in_same_class()
    {
        $this->private_function();
    }

}

$test = new Test();

$test->call_in_same_class();

        這樣就行了!

        總結來說,public大家都能取用,protected則是父類別才能使用,private則是只能在宣告function的class裡面使用。



        參考資料:

http://stackoverflow.com/questions/4361553/what-is-the-difference-between-public-private-and-protected


2017年3月29日 星期三

__call()的妙用

        php有個magic method叫__call(),寫法是這樣的:

class Demo
{
    public function __call($function , $args)
    {
        echo 'function: *' . $function . '* don\'t exist!';
    }
}

(new Demo)->target();



        __call的兩個參數可以任意取名,只要確定是兩個就好。

        當呼叫的target不存在,class Demo就會轉為呼叫__call(),讓開發者可以去設定如果function呼叫不到的時候做的事情,以上面的code執行下來,結果會是:

function: *target* don't exist!

        當沒有建立__call()的時候,會出現報錯:

Fatal error: Call to undefined method Demo::target() in /Users/Hong/php/test/test.php on line 11

        這裡有個今天讀code剛學到的小技巧:

class Animal
{
    public function __call($function , $args)
    {
        if ($function == 'dog') {
            $this->bark();
        } elseif ($function == 'cat') {
            $this->meow();
        }
    }

    private function bark()
    {
        echo 'bark!';
    }

    private function meow()
    {
        echo 'meow!';
    }
}

(new Animal)->dog();



        假設今天不確定動物的叫聲,這裡只需要呼叫動物的種類當作function name,然後因為Animal class找不到該function name,所以會呼叫__call(),__call()拿到之後就可以根據該function name去做對應的動作。

        注意到了嗎?本來應該是找不到function name做的預防措施,這邊卻反過來利用了這個特性,故意讓class找不到該function name,然後做統一分發。

        所以如果運用到API的溝通部分就是,先看看$_SERVER['REQUEST_METHOD']進來的是什麼,GET,POST,PUT?接著再從__call()統一分發到對應的function去呼叫後端API,達到整合的作用。

        寫code真的是一門永遠無法精通的學問,就算把單一語言的特性跟用法用得滾瓜爛熟,但永遠都會有更出其不意,或是更聰明的方式去完成任務,這次的__call()反其道而行的用法著實讓我大開了眼界。


參考資料:

http://php.net/manual/en/language.oop5.overloading.php#object.call





2017年3月28日 星期二

error_reporting 與 display_errors 區別

        當安裝好php,並開始coding的時候,有時候會看到頁面報錯:


        或是看到有warning,但頁面依然正常執行:


        身為開發人員,看著報錯上的提示去debug是很重要的,但有時候已經到了demo階段,頁面上有的報錯還來不及解決,在漂亮的UI上如果出現幾行Notice,會嚴重影響美觀以及客戶對系統的第一印象,這時候便需要對報錯的機制做一些調適。

        在mac安裝好php之後,會有一個php.ini的參數配置檔,php在server上運行就是靠這個配置檔,我是用brew安裝的,php.ini路徑在/usr/local/etc/php/x.x/php.ini,根據版本的不同再去更改x.x就好。

        預設的配置為:



        可以看到,php.ini在不同的環境下有不同的建議配置。

        display_errors在開發的時候,建議用On,但是正在線上運行的專案則是Off,開發的時候秀出錯誤,盡可能方便掌握系統的狀況,而在線上環境的時候,保持頁面的整潔,不出現抱錯or程式碼。

        error_reporting有很多種配置方式,在php.ini裡都有很好的說明,而~則是代表不包含,例如E_ALL & ~E_DEPRECATED & ~E_STRICT就是秀出所有error,但除了E_DEPRECATED以及E_STRICT,這點在php.ini裡也有解釋。

        但error_reporting以及display_errors到底差在哪?

        display_errors意思是要不要秀到頁面上,也就是一開始的兩張圖片,Notice和Parse error。

        error_reporting則是決定要秀什麼到頁面上,全秀?還是把一些訊息先排除?


        display_errors:要不要秀?

        error_reporting:要秀什麼?




2017年2月5日 星期日

laravel 1071 Specified key was too long; max key length is 767 bytes 解決方法

        laravel new 一個新專案之後,在.env用好資料庫設定,接著執行php artisan migrate的時候,拋出了兩個例外:

  [Illuminate\Database\QueryException]                                                                                                                                                 
  SQLSTATE[42000]: Syntax error or access violation: 1071 Specified key was too long; max key length is 767 bytes (SQL: alter table `users` add unique `users_email_unique`(`email`))  
                                                                                                                 
  [PDOException]                                                                                                   
  SQLSTATE[42000]: Syntax error or access violation: 1071 Specified key was too long; max key length is 767 bytes  

        可以看到共通點就是Specified key was too long; max key length is 767 bytes。

        因為laravel5.4用的是utf8mb4編碼,這種編碼跟默認的uft8編碼差別就在於utf8編碼最多支援到3個字節,而utf8mb4可以支援到四個字節,所以utf8mb4可以支援四個字節的emojis儲存進資料庫,mb4指的就是「most bytes 4」,而utf8mb4同時也支援許多的漢語難字。

        但在5.7.7之前的Mysql版本,以及10.2.2之前的MariaDB版本必須要手動調整預設的字串長度讓MySQL可以為他們新增索引。

        實際做法是在AppServiceProvider.php裡面加兩行程式碼:

<?php
namespace App\Providers;

use Illuminate\Support\ServiceProvider;

// 1
use Illuminate\Support\Facades\Schema;

class AppServiceProvider extends ServiceProvider
{
    /**
     * Bootstrap any application services.
     *
     * @return void
     */
    public function boot()
    {
        // 2
        Schema::defaultStringLength(191);
    }

    /**     * Register any application services.
     *
     * @return void
     */
    public function register()
    {
        //    
    }
}

        引入Schema之後再去呼叫defaultStringLength去更改預設字串長度就行了。


參考資料:


2017年2月1日 星期三

PHP else if 和 elseif 的研究

        開門見山的說,用 {} 的寫法運行起來是完全一樣的,舉例:

$a = 2;

if ($a == 1) {

} elseif ($a == 2) {

}

vs

$a = 2;

if ($a == 1) {

} else if ($a == 2) {

}

        會顯示出一樣的結果,而php在解析語法的時候,會將第二種寫法,也就是else if剖析成:

if ($a == 1) {

} else {
    if ($a == 2) {

    }   
}

        不過如果用colon的寫法則會有所不同,以php官方文件的程式碼為例:

/* Incorrect Method: */
if($a > $b):    
    echo $a." is greater than ".$b;
else if($a == $b): // Will not compile.    
    echo "The above line causes a parse error.";
endif;

/* Correct Method: */
if($a > $b):    
    echo $a." is greater than ".$b;
elseif($a == $b): // Note the combination of the words.    
    echo $a." equals ".$b;
else:    
    echo $a." is neither greater than or equal to ".$b;
endif;

         可以看到上方寫成else if分開,運行會parse error,下方連在一起的則不會。      

         psr-2的規範也說了必須連在一起:

     
        綜此上述,elseif目前是php裡判斷是的最佳選擇。

        參考資料:      
        http://www.php-fig.org/psr/psr-2/#if-elseif-else      
        http://www.php.net/manual/en/control-structures.elseif.php
        http://stackoverflow.com/questions/3662412/are-elseif-and-else-if-completely-synonymous