2019年1月16日 星期三

Google App Engine (GAE) 與 Laravel 5.7

2018/11 開始了新的專案,這個專案的目標是

  1. 取得Google Cloud Billing 資料然後整理呈現報表
  2. 使用者類別有:管理者/經銷商/一般客戶
  3. 不同類別使用者顯示的 Billing 金額不同,差異為服務費與折扣

主要的架構流程

  1. 取得每日的 Billing 資料並儲存到 Google BigQuery (Cloud SQL 無法應付每個月千萬筆的資料成長)
  2. 使用 Google service account & Oauth certification 透過 Google Cloud Platform 取得資料
  3. 透過 UI 呈現不同的資料分析(Chart / DataTable),提供不同的報表下載(CSV / PDF)

技術規格選用

  1. Laravel 5.7 (每個新案子都得要用最新的 stable的版本)
  2. PHP 7
  3. 第三方的 Library 一堆(這個都交給composer去管理吧)


所有的服務都建構在 Google Cloud Platform 上,包含 Version control。
在 GCE與GAE的選擇上做了些研究,最後選擇了GAE。理由很簡單,就是為了 Flexible。

整個案子很多環節遇到了很多問題,但是一開始的問題就是 Laravel 的安裝,GAE不像是 GCE 可以自由地瀏覽空間中的檔案並且及時修改。Deploy上去後就無法看到或是管理任何的檔案,而且最讓人困擾的就是每次的 Deploy 都要花上5-10分鐘,所以只要一次的疏忽就得要重新來一次,非常的浪費時間。

所以文章主要將問題簡單的拆分兩個,這兩個解決了,其他都可以參考個別的技術文件去學習。

  1. Laravel 的安裝與設定
  2. PDF 中文字型的安裝 (dompdf)

Laravel 的安裝與設定


laravel 5.7 + composer 1.8.0 + phpstorm 2018.1.16

下面就透過步驟來展示

使用 phpstorm 建立 laravel 專案

開啟 phpstorm > New project > Composer project ,Package 選擇 laravel/laravel 然後點選 create
你也可以只使用 composer 就可以完成





在根目錄下新增 app.yaml 檔案

這個檔案用於 GAE 的環境設定,也是唯一可以設定 GAE的方法,不需要懷疑,因為沒有其他方法了。


修改 app.yaml

直接把所有 .env 的設定都移到這個檔案來,下面我直接把說明備註在後面

runtime: php #php版本設定,在撰寫這篇文章的時候 php72 無法設定 env_variables
env: flex #GAE flexible選用

runtime_config:    document_root: public #網站根目錄設定, laravel 是public
    composer_flags:  --no-dev # composer 設定不安裝開發用套件

env_variables: # 下面都是 laravel 的環境設定,也就是說將需要在 .env 的設定都搬來這裡  APP_KEY:  'base64:hWWKPKQ6u48Cn8lw4WM00UHdCEQJerEQk5ognsy4u' # 必須使用 artisan 產生後複製過來  APP_LOG: errorlog 
  STORAGE_DIR: /tmp #GAE上除了 tmp 目錄外都只會有 readonly 權限
  APP_NAME: "[BETA]GCPBS| Summer "  APP_ENV:  production
  APP_DEBUG:  true
  CACHE_DRIVER: database
  SESSION_DRIVER:  database
  GCP_PROJECT_ID:  'summer-digital.com'  GCP_PROJECT_BUCKET_ID:  'summer-digital.appspot.com'
  GCP_BILLING_DATASET_NAME:  'summer-digital.com.billing-history'
  GCP_CLIENT_SECRET:  '/resources/gcp/summer-client-secret.json'
  GCP_SERVICE_ACCOUNT_CREDENTIAL:  '/resources/gcp/summer-service-account.json'  DB_CONNECTION:  mysql
  DB_HOST:  localhost
  DB_PORT:  3306
  DB_DATABASE:  summer
  DB_USERNAME:  summer
  DB_PASSWORD:  summer
  DB_SOCKET:  "/cloudsql/summer:asia-northeast1: summer-production"

修改 composer

deploy 後我們必須對 laravel 作 cache 的清理,否則會出現路徑上的問題,但是無法操作GAE所以只能透過 composer scripts 來代勞,在 composer.json 中新增下面的 scripts。GAE在 deploy後會執行 composer install,所以下面的 script 可以幫我們清理 laravel 的cache。

"scripts": {
  "post-autoload-dump": [
    "Illuminate\\Foundation\\ComposerScripts::postAutoloadDump",
    "@php artisan package:discover",
    "@php artisan config:cache",
    "@php artisan view:clear"]
}



部署到 GAE

執行指令前必須安裝 Gcloud SDK,而且需要設定 GCP。部署只需在 Terminal 執行下面指令


YUCHANs-MBP:~ yuchanlin$ gcloud app deploy

deploy log 會以文字訊息的方式顯示。

部署完成後執行


YUCHANs-MBP:~ yuchanlin$ gcloud app browse

就可以測試 laravel 是否安裝成功


PDF 中文字型的安裝 (dompdf)

PDF我透過 composer 安裝使用 dompdf 0.8.3 的版本,但是安裝後本身並無中文字型,所以必須要透過下面步驟安裝字型才能正常的顯示中文。

下載上方的資源體字型檔案,必須是 TTF 格式,然後到下面的位置

  1. storage/fonts/NotoSansTC-Medium.ttf
  2. storage/fonts/NotoSansTC-Bold.ttf

下載字型安裝工具

下載 load_font.php 然後放到 laravel 目錄下


安裝字型

開啟 Terminal 然後到 load_font.php 目錄下執行下面指令


php load_font.php storage/fonts/NotoSansTC-Medium.ttf storage/fonts/NotoSansTC-Bold.ttf 


執行完成後,會將字型安裝到 dompdf/lib/fonts 中,在 html 裡頭就可以透過 css 設定中文字型:
body {
    font-family: 'notosanstc';
    font-size: 16px;
    color: #555e6b;
}

設定 composer 


因為部署不會把 vendor 上傳上去,有兩個原因
  1. GAE限制 deploy 的檔案限制是 100,00
  2. 把 vendor 上傳上去需要花很多時間,很浪費時間
所以需要透過 composer scripts 在 install 後幫忙安裝字型檔案
"scripts": {
  "post-autoload-dump": [
    "Illuminate\\Foundation\\ComposerScripts::postAutoloadDump",
    "@php artisan package:discover",
    "@php artisan config:cache",
    "@php artisan view:clear",
    "@php load_font.php 'notosanstc' storage/fonts/NotoSansTC-Medium.ttf storage/fonts/NotoSansTC-Bold.ttf"  ]
}

下面的範本包含圖檔的處理與產生PDF的設定:



$arrContextOptions = array(    "ssl" => array(        "verify_peer" => false,        "verify_peer_name" => false,    ),);
$type = pathinfo($logoUrl, PATHINFO_EXTENSION);$avatarData = file_get_contents($logoUrl, false, stream_context_create($arrContextOptions));$avatarBase64Data = base64_encode($avatarData);$imageData = 'data:image/' . $type . ';base64,' . $avatarBase64Data;
$html = view('export/html',    compact('table', 'currency', 'total', 'logoUrl', 'imageData', 'billingName', 'firstDay', 'lastDay', 'time', 'businessSupport', 'discount', 'paymentDueDay'))    ->render();
$options = new Options();$options->setIsHtml5ParserEnabled(true);$options->setIsPhpEnabled(true);$options->setIsRemoteEnabled(true);$options->setIsJavascriptEnabled(true);
$dompdf = new Dompdf($options);$dompdf->setPaper('A4', 'portrait');
$dompdf->loadHtml($html, 'utf8');$dompdf->render();$dompdf->stream($downloadFileNmae . '.pdf', ['compress' => 1, 'Attachment' => 1]);

上面展示了 laravel  部署到 GAE的方式,也說明了 dompdf 中文字型的安裝方式,其餘開發上的問題都可以直接閱讀 laravel / composer / dompdf 官網文件來解決了。


希望大家可以透過我的分享省去些摸索的時間。


參考資料

  1. Google App Engine Document
  2. Google Cloud SDK
  3. Laravel 5.7 Document
  4. Composer Document
  5. Dompdf





2016年3月23日 星期三

如何在 MAC OS X 上開啟 Apache 的 SSL 支援(OpenSSL)

Mac 與 OSX 是非常棒的平台。
對於一般使用者使用上很簡單,沒有 Windows 上這麼多繁瑣的事情與中毒等等複雜的問題。
對於開發者來說,除了韌體跟Microsoft solution 之外,其他的都很方便,像是:Java/PHP/Ruby/C/C++,不管是 Web/Mobile 都是。
我想光是不會中毒跟當機就讓我很愛了!

但是對於開發者來說設定一直都是比較多問題的,因為得要手動做很多的事情。
OpenSSL 的設定我想就是讓很多人煩惱的一件事情,預設上 OSX的 Apache 是沒有 SSL Enablea的。

安裝跟設定 SSL有下面幾個步驟

  1. OpenSSL 安裝與設定
  2. Apache SSL Enable 的設定方式
  3. 測試&完工

需要的軟體有兩個

  1. OpenSSL (下面的步驟會有下載的地方)
  2. Apache (OSX已經內載了)

接下來就跟著一步步來吧!

OpenSSL 的安裝與設定

打開你的 Termal 然後輸入下列的指令
  1. 切換到本機暫存原始碼的目錄,然後下載 OpenSSL 原始碼
    cd /usr/local/src
    curl --remote-name http://www.openssl.org/source/openssl-1.0.2g.tar.gz
  2. 解壓縮後進入到原始碼目錄內
    tar -xzvf openssl-1.0.2g.tar.gz
    cd openssl-1.0.2g
    
  3. 設定/編譯然後進行安裝,下面的設定會安裝到 /usr/local/openssl-1.0.2g 目錄下
    ./configure darwin64-x86_64-cc --prefix=/usr/local/openssl-1.0.2g
    make depend
    make install
    
  4. 製作一個 openssl 的連結,如此做可以讓以後在更版的時候簡單點
    ln -s openssl-1.0.2g /usr/local/openssl
    
  5. 設定環境變數後讀取新的設定
    echo 'export PATH=/usr/local/openssl/bin:$PATH' >> ~/.bash_profile
    echo 'export MANPATH=/usr/local/openssl/ssl/man:$MANPATH' >> ~/.bash_profile
    source ~/.bash_profile
    
  6. 安裝憑證
    security find-certificate -a -p /Library/Keychains/System.keychain > /usr/local/openssl/ssl/cert.pem
    security find-certificate -a -p /System/Library/Keychains/SystemRootCertificates.keychain >> 
    /usr/local/openssl/ssl/cert.pem
    
  7. 測試安裝
    openssl version -a
  8. 如果出現錯誤則需要以照錯誤的訊息去做修正,如果看到正確的版本資訊那代表安裝成功

OpenSSL 成功安裝後的版本資訊

製作憑證

  1. 製作金鑰
    $ cd /etc/apache2
    $ sudo ssh-keygen -f server.key
    
  2. 產生憑證簽章需要的檔案
    $ sudo openssl req -new -key server.key -out server.csr
    
  3. 使用CSR去製作憑證
    $ sudo openssl x509 -req -days 365 -in server.csr -signkey server.key -out server.crt
    
  4. 製作RSA金鑰
    $ sudo openssl rsa -in server.key -out server.key
    

設定並啟動 Aapche 的 SSL 支援

下面只針對 SSL 設定步驟做描述
  1. 打開你的 Termal 執行指令修改 httpd.conf
    sudo nano /etc/apache2/httpd.conf
    
    將下列幾行的註解符號刪除後儲存(儲存的熱鍵是 control + x 然後選 Y)
    
    LoadModule socache_shmcb_module libexec/apache2/mod_socache_shmcb.so
    LoadModule ssl_module libexec/apache2/mod_ssl.so
    
  2. 編輯設定檔來打開 HTTPS 支援
        DocumentRoot "/Users/your_user/Sites"
        ServerName localhost
        SSLEngine on
        SSLCertificateFile "/private/etc/apache2/server.crt"
        SSLCertificateKeyFile "/private/etc/apache2/server.key"
    
  3. 測試 Apache 的設定,如果沒問題就重新啟動 Apache
    $ sudo apachectl configtest
    $ sudo apachectl -k restart
    
  4. 開啟瀏覽器打開 https://localhost/~{YOUR USERNAME} 測試看看

2013年1月18日 星期五

取得 Apple Device 的 Device ID




取得 Apple Device 的 Device ID

在開發 APP 階段我們時常需要進行測試,測試的時候因為不是正式的版本所以我們並不會將軟體發佈上 App Store。這時候我們會使用 Apple提供的 AD Hoc 機制來加入指定的特定人員的裝置可以使用 iTune 安裝尚未發佈的測試版本 APP。

取得裝置識別碼的方式

要使用者樣的方法開發人員則需要使用者提供 Device ID,而這有兩種方式可以取得:
  1. 安裝第三方的軟體到你的裝置上,然後輕鬆地點一個按鍵就可以自動地將你的裝置識別碼寄到你指定的郵件信箱去
  2. 連接你的裝置到電腦上 > 打開你的 iTune > 開啓你要的裝置 > 複製你的裝置識別碼 > 自己手動寄給對方

不同方法的風險

當然第一種方式是最方便的,不過這個方式之前曾經出現漏洞,這種漏洞會讓軟體開發人員可以取得你的特殊權限所以一般使用者很擔心這樣的方式會帶來的風險。
第二種方式說穿了簡單,步驟很多但是無痛也安全,所以如果需要的話可以參照下面的步驟操作。

使用 iTune 取得你的 Apple 裝置識別碼

連接你的裝置到電腦上並且開啟 iTune (MAC可以點右上角的放大鏡在上面打上 iTune 可以快速開啓)

1
點選

iTune 右上角的 Device 按鈕會顯示你電腦偵測到的所有 iDevice,點選你要的裝置

2

如果左邊出現的是 Serial Number則將你的滑鼠移到序號上面點一下滑鼠左鍵,Serial Number 會變成 identifier (UDID)

3

滑鼠移動到 identifier (UDID)右邊的識別碼上點選滑鼠右鍵就可以複製了

4


2013年1月11日 星期五

Xcode 無法調整 Base SDKs 的解決方式


xcode

IOS 藉由更新 Xcode 來提供最新的 IOS SDK 開發,對於開發者收到了更新通知很理所當然地會更新來取得最新的編輯器跟最新的SDK,而新版的 Xcode 也只會包含最新的 SDK,假設你下載4.5 版本的 Xcode 它只會包含最新的 IOS 6。 不過這遇到了一個很關鍵問題就是對於一個開發人員來說需要維護的版本不一定只有 IOS 6,在IOS 6推出的時候我們的客戶並不會馬上更新版本,所以我們還是需要維護 IOS 5 的APP,這時候我們需要的是調整設定中的Base SDK 這個項目,不能使用相融模式的原因是因為不同版本的SDK在 compiler階段有所不同,當然這也是有解不過這裡就不說明。 Xocde 在專案設定理提供了可以選擇 Base SDK 的項目,但是這時候你只會看到內建的一個版本的 IOS SDK,依照Xcode 4.5來說就是IOS,那如何安裝 IOS 5 或是 IOS 4 呢? 這個問題讓人就讓人很困擾,因為文件並沒有明白告訴我們這件事情,所以我們就來說一下怎樣安裝舊版的 SDK 到 Xcode裡。

自己手動的解決方式

需要的步驟如下,至於怎樣做文章最下方有個別的詳細方法可以參考:
  1. 取得舊版的 SDK
  2. 將舊版的SDK複製到當前的 Xcode 目錄下的SDKs中
  3. 重新開啓 Xcode

取得舊版SDK的兩種方式

  1. 如果還保留舊版的 Xcode 可以從目錄中複製出來使用 Find 或是 Terminal 下指令,目錄位置在 /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs
  2. 從官方網站下載特定版本的 Xcode DMG 檔案後 Mount 起來然後再從 Xcode.app 中複製出來 下載網址 https://developer.apple.com/downloads/index.action apple-download-site

Xcode SDKs 的目錄位置在哪裡

/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs

重新啓動後 Xcode 的 Base SDKs 可以選擇了

base_sdk_options

2012年10月1日 星期一

IOS多國語系規劃與開發-ios multi language design and imeplementation

多國語系是在APP的規劃與開發設計中是一個很重要的一環,很簡單的一個原因就是「市場」!其實說簡單也算是簡單,因為 SDK 本身就提供了多國語系的功能,我們只需要簡單的幾個步驟就可以達到,下面是簡單的步驟:

多國語系的規劃
  1. 規劃好哪些產出需要多國語系: UI, Message, Mail Template ..ext
  2. 決定好上面的東西要怎樣做,例如:是否要將XIB, infos.plist 變成多國語系, 是否要對圖檔做多國語系, 是否要將 Strings 與 Template 做成多國語系
  3. 作出一個 POC Template 與 Design Guideline for Multi-language 給 UI Design , Designer (Technical Designer), Programmer。
一個方式
個人喜歡的方式是將所有文字抽出來做成多國語系檔,因為這樣你只需要維護的是各種語系的文字檔案,但是這樣需要考慮到的是:
  1. 所有圖片不能含有文字,UI設計師只能使用IOS內建的字型,並且必續要產出 Design Guideline 讓開發人員清楚地知道每個UI的字形大小, 字型種類, 文字的色碼, 最多支援行數
  2. UI設計師必須在設計的時候清楚知道有多少種語言需要被支援,並且在設計所有 UI 元件的時候考慮到「文字長度」,並且在視覺設計上儘量在不破壞美觀下定義長寬的最大值
  3. Designer必須要將所有文字的 KEY 值作名稱規劃,並且需要讓所有 Programmer能藉由KEY值就能知道文字的用途。
這樣的設計方式可以達到多國語系的設計而且可以將所有語系的內容框在 Localizable.strings 與 Template File 裡,解由修改修改語系檔案或是 Mail or Publish template就可以快速地達到修改多國語內容的目的。

SDK也支援 XIB的多國語系設計方式,只是當 View component 跟多國語系攪在一起,那可想而知的是修改的 Effort 太大了。

開發步驟
  1. 建立 Localizable.strings Files, New 一個 File , 選擇 Resource > Strings File, 將它命名為 Localizable.strings
  2. 點選左側 Navigator 中的「專案名字」> 右側點選 Info , 右邊的 Localizations 可以增加你要支援的語言, 點選後會有詢問你哪些檔案要變成多國語系,以上面的例子因為我的方式只有用到 Localizable.strings 所以只要選擇 Localizable.strings就可以
  3. 編輯 Localizable.strings , 基本我將所有的 strings 分為下面幾個類別
    // button
    "Done" = "Done";
    "Close" = "Close";
    "Tutorials" = "Tutorial";
    // message
    "Please insert valid information" = "Please insert valid information";
    // label string
    "You’ve traveled" = "You’ve traveled";

    // exception string
    "The hardware does not support" = "The hardware does not support";
  4. 在程式裡取用所有的 strings 只需要如下的寫法
    cell.value.text = NSLocalizedString(@"You’ve traveled"nil);
  5. 若需要在 UI 上顯示現在使用者所選用的語言, 你可以使用下面的方法顯示出各國語言表現的語言文字, 像當選擇日文時會顯示「日本語」
     NSLocale *locale=[NSLocale currentLocale];
     NSString *langName= [locale displayNameForKey:NSLocaleIdentifier
                                                            value:[[NSLocale preferredLanguages] objectAtIndex:0]];

結論
很多種方式可以達道不同程度的多國語系方式,只是需要注意的層面不同,這樣做法上很簡單,但是對於設計來說會需要注意的地方很多,一不注意就會讓文字超出UI或是變成...。在開發上唯一需要注意的是 Key 的命名規則,否則平行開發下 Programmer 若看不懂語系檔那只會有兩種情形:一種是花很多時間去搞清楚什麼是什麼,第二種就是會多出很多同樣的文字參數。

2012年8月21日 星期二

ios : the best way to implement rotation 實現 rotation 的好方式

在開發 app 時候不免需要對翻轉效果做很多克制化,有的需要換掉整個界面,有的需要換背景,一般來說通常都需要針對那種看起來很簡單的東西寫很多程式,做起來很麻煩,真的是件很苦的差事。

下面是幾個我試過的方法

  1. 在 翻轉的時候去重新配置所有元件的位置
  2. 直接在翻轉後去換一個特定方向的 UIViewController
這兩種方法都很麻煩而且費工,簡直就是大茶包一個。


今天發現一個新的方法!
不僅簡單又方便,可以大大省去很多時間,下面這個 Method 是關鍵。

NSBundle UIKit Additions Reference
- (NSArray *)loadNibNamed:(NSString *)name owner:(id)owner options:(NSDictionary *)options



不囉唆來一段簡單的範例程式,相信簡單幾行大家就會很快地瞭解了。

- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
                                         duration:(NSTimeInterval)duration
{
    NSLog(@" do rotation of the view controller");
    
    if( UIInterfaceOrientationIsLandscape(interfaceOrientation) )
    {
        [self viewDidUnload];
        [[NSBundle mainBundle] loadNibNamed: [NSString stringWithFormat:@"%@-landscape", NSStringFromClass([self class])]
                                      owner: self
                                    options: nil];
        [self viewDidLoad];
        NSLog(@" landscape");
    }
    else
    {
        [self viewDidUnload];
        [[NSBundle mainBundle] loadNibNamed: [NSString stringWithFormat:@"%@", NSStringFromClass([self class])]
                                      owner: self
                                    options: nil];
        [self viewDidLoad];
        NSLog(@" portrait");
    }
}

利用 NSBundle loadNibNamed 的方法在 Rotation 的後去替換掉 Views,如此可以快速地在 NIB 上面拉UI 不需要再去用程式算出每個UIs的位置,這樣的話方便很多。

當然別忘記在 Reload Views之後重新 init 所有的 values與行為,不過我想這應該原本設計客制化的 UIs 的時候就已經做好了。


2011年12月29日 星期四

Javascript build function prototype 不同的函示寫法



問題
JavaScript 的寫法很多,端看開發者的喜好(這不是個好現象),今天看到了一個寫 function call 的方式,因為是第一次看到這樣的寫法所以就稍稍的研究了一下。


var id = that.profile.id;
(function(id){
   // do business logic
})(id);

寫 Web 寫了非常久了是第一次看到這樣的寫法所以一開始看到覺得非常的困惑。
用 chrome developer tool 的 console 研究了一下後才知道原來這是一種 function call 的寫法,

解答
這段程式告訴 browser 的是:

  1. var id = that.profile.id;
    宣告一個 id 變數
  2. (function(id) { // do business logic })(id);
    建立一個  function call,可以將上面的 code 看成 function(id);
所以第一個 function(id) 這個 id 的參數是由最後面的 (id) 傳遞進來的,等於:
(id)  -> function(id) ,也就是說 function(id) 參照的不是 var id = that.profile.id 。

結論
個人覺得,這樣的寫法容易讓人混淆,因為第一個無法讓人一目瞭然地看出程式邏輯,上面只是我截出來的程式語法重點,其實這是一個 70 line 的 function call。
再來就是 that,這個是變數,但是第一眼會想到的是 this。也因此會容易混淆看 code 的人。
變數應該要取得明確且清晰,像是 personalSession 。
function call 獨立出來會讓開發人員可以快速的瞭解程式碼, (function(id){})(id) 跟 parser(id) 我想兩個比較起來很明顯了。

很多小地方多注意,可以減少自己與其它開發人員的維護時間與 survey code 的速度,有時候簡單了當下換來的只是未來的加倍的償還。