范聖佑 (Shengyou Fan)
Modern Web 2016
2016/08/24
讓你的 PHP 開發流程
再次潮起來!
{	
  
	
  	
  	
  	
  "name":	
  "shengyou/self-­‐introduction",	
  
	
  	
  	
  	
  "description":	
  "個⼈人簡介",	
  
	
  	
  	
  	
  "authors":	
  [	
  
	
  	
  	
  	
  	
  	
  	
  	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  "name":	
  "范聖佑	
  (Shengyou	
  Fan)",	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  "email":	
  "shengyoufan@gmail.com",	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  "homepage":	
  "http://www.shengyoufan.com",	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  "company":	
  "得寬科技	
  (The	
  Qwan)",	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  "role":	
  ["研究員",	
  "Laravel	
  傳教⼠士"]	
  
	
  	
  	
  	
  	
  	
  	
  	
  }	
  
	
  	
  	
  	
  ],	
  
	
  	
  	
  	
  "support":	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  "facebook":	
  "http://fb.me/shengyoufan",	
  
	
  	
  	
  	
  	
  	
  	
  	
  "twitter":	
  "@shengyou",	
  
	
  	
  	
  	
  }	
  
}
v2.2 v4.0	
  ~	
  v5.2
Page Script
5.3
探索歷程
Laravel 台灣
https://www.facebook.com/groups/laravel.tw
成⽴立於 2013 現有 4000 位成員加⼊入
Laravel 道場
http://www.laravel-dojo.com
致⼒力於提供最好的 Laravel 教育訓練
2016.03
正修科⼤大資⼯工系
2016.01
中華電信學院
2015.07
新北市樹林國⼩小
2014.12
彰師⼤大資⼯工系
2015.05
臺中科⼤大資⼯工系
2015.12
⻁虎尾科⼤大電算中⼼心
2016.02
交通⼤大學資服中⼼心
2016.07
逢甲⼤大學⿊黑客社
2016.08
勤益科⼤大電算中⼼心
★
持續推廣中!
推廣了三年,
最常聽到的回應是…
Laravel 好棒!
但我們公司
沒辦法導⼊入…
我們還在⽤用舊版 PHP
歷史包衭、升級有難度
因為…
絕⼤大部份的程式碼都是 Page Script
產品流程複雜、維護⼯工作多
打掉重練不可能
上線已久、使⽤用者固定
⽽而且…它賺錢!
因為…
我很有興趣
對團隊⽂文化及導⼊入成本的疑慮
但我的主管和同事…
因為…
⽽而且我⽼老闆覺得…
• 先改善效益最⼤大的地⽅方
• ⼀一次改變⼀一點不要太多
• 每⼀一步都不會太難
• 讓成員感受到好處
• ⾃自然⽽而然慢慢習慣
⾝身為傳教⼠士的反思
六個可獨⽴立推⾏行的施⼒力點
優化
環境建置流程
升級
開發⼯工具
導⼊入
Composer
導⼊入
Migration
導⼊入
適⽤用框架
導⼊入
⾃自動化
視團隊可以導⼊入的點,⼀一次施⼒力⼀一點點
優化環境建置流程
依賴網路與測試機的開發
Mac Windows Linux
測試主機 測試資料庫
優化⽅方向
• 以本地端可以獨⽴立運作開發為⺫⽬目標
- 不需依賴開發機、網路,可單兵作業
- 在地端開發但⼜又不影響本機環境
各出奇招的開發環境
Mac Windows Linux
優化⽅方向
• 新/舊專案依需求和現況使⽤用不同的 PHP 版本
• 從作業系統到 PHP 各專案完全獨⽴立
• 開發時的環境就是上線時的環境
- 降低任何因環境不同⽽而可能出現的錯誤
新成員加⼊入專案時
• 先看專案 README
• 試著了解專案所需的環境
• 開始安裝
• 遭遇錯誤 → 解決 → 遭遇錯誤 → 解決
• 開始貢獻
(前提是有 README)
有新成員時就再循環⼀一次…
優化⽅方向
• 降低新成員進⼊入專案時的⾨門檻
- 讓成員可以專注在⾃自⼰己的貢獻上
• 減少建置時所需花費的時間成本
- 縮短成員融⼊入專案的時間
VirtualBox +Vagrant
Mac Windows Linux
Vagrant	
  Box Vagrant	
  Box Vagrant	
  Box
選定作業系統
https://atlas.hashicorp.com/boxes/search
下載 Box 並初始化
$	
  vagrant	
  box	
  add	
  {box	
  name}	
  
$	
  vagrant	
  init	
  {box	
  name}
Vagrantfile
Vagrant.configure(2)	
  do	
  |config|	
  
	
  	
  config.vm.box	
  =	
  "ubuntu/trusty64"	
  
	
  	
  config.vm.provider	
  "virtualbox"	
  do	
  |vb|	
  
	
  	
  	
  	
  vb.customize	
  ["modifyvm",	
  :id,	
  "-­‐-­‐memory",	
  "512"]	
  
	
  	
  	
  	
  vb.customize	
  ["modifyvm",	
  :id,	
  "-­‐-­‐cpus",	
  "1"]	
  
	
  	
  	
  	
  vb.customize	
  ["modifyvm",	
  :id,	
  "-­‐-­‐natdnsproxy1",	
  "on"]	
  
	
  	
  	
  	
  vb.customize	
  ["modifyvm",	
  :id,	
  "-­‐-­‐natdnshostresolver1",	
  "on"]	
  
	
  	
  end	
  
	
  	
  config.vm.network	
  "forwarded_port",	
  guest:	
  80,	
  host:	
  8080	
  
	
  	
  config.vm.network	
  "private_network",	
  ip:	
  "192.168.10.10"	
  
end
啟動VM
$	
  vagrant	
  up
模擬多主機環境
Vagrant.configure(2)	
  do	
  |config|	
  
	
  	
  config.vm.define	
  :app	
  do	
  |app|	
  
	
  	
  	
  	
  app.vm.box	
  =	
  "ubuntu/trusty64"	
  
	
  	
  	
  	
  #	
  ...	
  
	
  	
  	
  	
  app.vm.network	
  "private_network",	
  ip:	
  "192.168.10.10"	
  
	
  	
  end	
  
	
  	
  config.vm.define	
  :db	
  do	
  |db|	
  
	
  	
  	
  	
  db.vm.box	
  =	
  "ubuntu/trusty64"	
  
	
  	
  	
  	
  #	
  ...	
  
	
  	
  	
  	
  db.vm.network	
  "private_network",	
  ip:	
  "192.168.10.11"	
  
	
  	
  end	
  
end
登⼊入各別主機
$	
  vagrant	
  ssh	
  {machine}
$	
  vagrant	
  ssh	
  app $	
  vagrant	
  ssh	
  db
客製化需求
#	
  VM	
  外	
  
$	
  vagrant	
  ssh	
  
#	
  VM	
  裡	
  
$	
  apt-­‐get	
  install	
  ...	
  
$	
  exit	
  
#	
  VM	
  外	
  
$	
  vagrant	
  package	
  
$	
  vagrant	
  box	
  add	
  {box	
  name}	
  package.box
#	
  在專案資料夾內	
  
$	
  vagrant	
  init	
  {box	
  name}	
  
$	
  vagrant	
  up
1. 先客製化 Box 2. ⽤用客製化 Box 開機器
Homestead
• Laravel 官⽅方使⽤用VitrualBox 及Vagrant 技術製作出來的
VM Box
• 可以迅速在本機上建置開發 Laravel 所需的多合⼀一環境
• 不僅可以拿來開發 Laravel,拿來開發其他 PHP 專案也
是可以的!
• 套件新、更新勤、抽換快!
Homestead 操作流程
#	
  下載	
  Homestead	
  
$	
  git	
  clone	
  ...homestead.git	
  Homestead	
  
$	
  cd	
  Homestead	
  
$	
  bash	
  init.sh	
  	
  
#	
  設定	
  Homestead	
  
$	
  vim	
  ~/.homestead/Homestead.yaml	
  
folders:	
  
	
  	
  	
  	
  -­‐	
  map:	
  {本機資料夾}	
  
	
  	
  	
  	
  	
  	
  to:	
  {VM	
  內資料夾}	
  
sites:	
  	
  
	
  	
  	
  	
  -­‐	
  map:	
  {開發⽤用網址}	
  
	
  	
  	
  	
  	
  	
  to:	
  {VM	
  內網站根⺫⽬目錄}
#	
  設定	
  Host	
  
$	
  vim	
  /path/to/hosts	
  
#	
  啟動	
  Homestead	
  
$	
  vagrant	
  up
客製化 Homestead
#!/bin/sh	
  
if	
  [	
  !	
  -­‐f	
  /usr/local/extra_homestead_software_installed	
  ];	
  then	
  
	
  	
  echo	
  "installing	
  some	
  extra	
  software"	
  
	
  	
  sudo	
  -­‐s	
  	
  
	
  	
  apt-­‐get	
  install	
  -­‐y	
  ...	
  
	
  	
  touch	
  /usr/local/extra_homestead_software_installed	
  
else	
  	
  	
  	
  	
  
	
  	
  echo	
  "extra	
  software	
  already	
  installed...	
  moving	
  on..."	
  
fi
$	
  vim	
  ~/.homestead/after.sh
不能⽤用VM 的特殊狀況…
• Laravel 道場 拿來做教學的訓練機
• 整合 Cmder、UwAmp、git、Composer
等多項開放源始碼⼯工具於⼀一體
• 獨⽴立的環境變數、port 設定
• 免安裝、免設定、解壓縮即可使⽤用!
• 在會重開機⾃自動還原的環境下特別好⽤用!
⺫⽬目前正式發佈 v1.3.0 穩定版
http://www.laravel-dojo.com/opensource/wagon
wagon 懶⼈人包
升級開發⼯工具
總是有千萬個原因…
所以程式碼還沒有版本管理…
git 也是有 GUI 的
https://www.gitkraken.com/https://www.sourcetreeapp.com
git 也是可以接 svn 的
#	
  把	
  svn	
  裡的檔案取出來	
  
$	
  git	
  svn	
  clone	
  -­‐r	
  HEAD	
  {SVNSERVER}	
  {folder}	
  
#	
  git	
  ⽇日常指令	
  
$	
  cd	
  {folder}	
  
$	
  vim	
  {file}	
  
$	
  git	
  add	
  {file}	
  
$	
  git	
  commit	
  -­‐a	
  -­‐m	
  '{message}'	
  
#	
  同步到	
  svn	
  上	
  
$	
  git	
  svn	
  rebase	
  
$	
  git	
  svn	
  dcommit
git 也是可以⽤用 FTP 部署的
https://ftploy.com/https://git-­‐ftp.github.io/
不是不願意⽤用好的編輯器,
⽽而是壓根兒不知道有好的編輯器!
常⾒見錯誤
幼幼班 初級班 進階班
• 沒存檔
• 漏分號 ;
• 漏其符號 ,	
  '	
  "
• 括號	
  {	
  [	
  (	
  沒
成對或範圍錯誤
• 基本 PHP 語法錯
誤
• 物件沒實體化就
使⽤用
• 沒有⽤用 use (漏
namespace)
• 重覆的 function
名稱
• undefine function
• 沒有 return 或
return 的型別不
對
• 重複的邏輯
• Interface 定義了
但忘了實作
更多補助
• 語法⾼高亮度
• 語法檢查
• 語法提⽰示
• 程式碼⽚片段
• 程式碼⾵風格統⼀一
• 與版本管理系統整合
• 程式重構
⼯工具選⽤用
PhpStorm Visual	
  Studio	
  Code
EditorIDE
不⽤用 breakpoint 來偵錯?
這哪叫 debug?
- 點燈坊 Oomusou
http://oomusou.io/
除錯⼯工具
• ⼟土炮式掃雷
- echo (!) → exit
- var_dump() → exit
- print_r() → exit
• Laravel 式除錯
- dd() (底層為 SymfonyVarDumper)
編輯器 + XDebug
https://youtu.be/7Jwj2L51JaA
導⼊入 Composer
寧可把時間拿去玩寶可夢,
也不要花時間重造輪⼦子!
永遠扯不清的 include 地獄
//	
  需要⽤用到	
  lib1	
  裡的	
  Class	
  
include	
  __DIR__	
  .	
  "/libs/lib1/Class.php";	
  
//	
  lib1	
  相依於	
  lib2	
  及	
  lib3	
  
include	
  __DIR__	
  .	
  "/libs/lib2/Class.php";	
  
include	
  __DIR__	
  .	
  "/libs/lib3/Class.php";	
  
//	
  lib2	
  相依於	
  lib4	
  
include	
  __DIR__	
  .	
  "/libs/lib4/Class.php";	
  
//	
  lib3	
  相依於	
  lib999	
  
include	
  __DIR__	
  .	
  "/libs/lib999/Class.php";
寫 new 寫得提⼼心吊膽
//	
  實體化	
  Class	
  
$class	
  =	
  new	
  MyClass();	
  
//	
  載⼊入	
  Class	
  失敗	
  
PHP	
  Fatal	
  error:	
  	
  Class	
  'MyClass'	
  not	
  found	
  in	
  scripts.php	
  
on	
  line	
  x
優化⽅方向
• 不再落⼊入 include / require 地獄
- 解決裝 A 掉 B 少 C 的困境
• ⾃自動載⼊入專案內的 類別 / 函式
- 使⽤用類別時不⽤用⼿手動 include
- 使⽤用⾃自定函式不⽤用⼿手動 include
⼿手動管理專案套件庫
libs/
函式庫
函式庫 B
函式庫 C
函式庫 函式庫 E
移除
新增
函式庫 函式庫 D
置換
函式庫 A
更新
v.2v.1
優化⽅方向
• 以專案為基礎的套件管理機制
- 各專案內的套件獨⽴立管理,不互相影響
• ⾃自動化下載、更新套件
- 統⼀一的套件版本管理機制
- 流程由⾃自動化⼯工具輔助
• 加⼊入 PHP 的套件⽣生態系
- 不再重新發明輪⼦子
Composer 套件管理
composer.phar
執⾏行檔
+ +
git
版本控制
php 5.3.2
(openssl extension)
安裝 Composer
#	
  下載安裝指令	
  (請使⽤用官網完整指令)	
  
$	
  php	
  -­‐r	
  "copy('https://getcomposer.org/installer',	
  ...);"	
  
$	
  php	
  -­‐r	
  "..."	
  
$	
  php	
  composer-­‐setup.php	
  
$	
  php	
  -­‐r	
  "unlink('composer-­‐setup.php');"	
  
#	
  將	
  Composer	
  變成系統全域指令	
  
$	
  mv	
  composer.phar	
  /usr/local/bin/composer	
  
#	
  使⽤用	
  Composer	
  
$	
  [php]	
  composer[.phar]	
  {command}
https://getcomposer.org/download/	
  
增加 composer.json
{	
  
	
  	
  	
  	
  "name":	
  "{vendor}/{project}",	
  
	
  	
  	
  	
  "type":	
  "project",	
  
	
  	
  	
  	
  "license":	
  "proprietary",	
  
	
  	
  	
  	
  "authors":	
  [	
  
	
  	
  	
  	
  	
  	
  	
  	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  "name":	
  "{author	
  name}",	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  "email":	
  "{author	
  email}"	
  
	
  	
  	
  	
  	
  	
  	
  	
  }	
  
	
  	
  	
  	
  ],	
  
	
  	
  	
  	
  "minimum-­‐stability":	
  "stable",	
  
	
  	
  	
  	
  "require":	
  {}	
  
}
$	
  composer	
  init
軍⽕火庫
https://packagist.org
安裝/更新套件
$	
  composer	
  require	
  {vendor}/{project}	
  
$	
  composer	
  [install|update]
⾃自動載⼊入設定
<?php	
  
require	
  __DIR__	
  .	
  '/vendor/autoload.php';
從此只需要寫的	
  require
套件⽣生態系
https://github.com/ziadoz/awesome-­‐php
nesbot/carbon
<?php	
  
require	
  __DIR__	
  .	
  '/vendor/autoload.php';	
  
//	
  像	
  Facebook	
  ⼀一樣顯⽰示時間間距	
  
use	
  CarbonCarbon;	
  
$past	
  =	
  Carbon::yesterday()	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  -­‐>addHours(4)	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  -­‐>minute(30);	
  
Carbon::setLocale('zh-­‐TW');	
  
echo	
  $past-­‐>diffForHumans(Carbon::now());	
  //	
  ⼀一天前
intervention/image
<?php	
  
require	
  __DIR__	
  .	
  '/vendor/autoload.php';	
  
//	
  處理圖⽚片縮⼩小轉存	
  
use	
  InterventionImageImageManagerStatic	
  as	
  Image;	
  
Image::configure(array('driver'	
  =>	
  'imagick'));	
  
$image	
  =	
  Image::make('public/foo.jpg')	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  -­‐>resize(300,	
  200)	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  -­‐>save('public/bar.png',	
  60);
recca0120/laravel-­‐tracy
<?php	
  
require	
  __DIR__	
  .	
  '/vendor/autoload.php';	
  
use	
  Recca0120LaravelTracyTracy;	
  
Tracy::instance();
除錯畫⾯面及⾯面板
//	
  tracy.php	
  
<?php	
  
$config	
  =	
  [	
  
	
  	
  	
  	
  'enabled'	
  =>	
  true,	
  
	
  	
  	
  	
  'showBar'	
  =>	
  true,	
  
	
  	
  	
  	
  'accepts'	
  =>	
  [	
  
	
  	
  	
  	
  	
  	
  	
  	
  'text/html',	
  
	
  	
  	
  	
  ],	
  
	
  	
  	
  	
  'editor'	
  =>	
  'vscode://open?url=file://%file&line=%line',	
  
	
  	
  	
  	
  'panels'	
  =>	
  [	
  
	
  	
  	
  	
  	
  	
  	
  	
  '{name}'	
  =>	
  {boolean},	
  
	
  	
  	
  	
  ],	
  
];
VS Code URL Handler
https://github.com/shengyou/vscode-­‐handler
截錯、開檔⼀一鍵完成!
https://youtu.be/sxASikzX-­‐gc
先別管 Packagist 上的套件了,
你知道 Composer 也可以幫你載⼊入你
的類別和函式嗎?
三⼤大⾃自動載⼊入⽅方式
{	
  
	
  	
  	
  	
  "autoload":	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  "psr-­‐4":	
  {...},	
  
	
  	
  	
  	
  	
  	
  	
  	
  "classmap":	
  [...],	
  
	
  	
  	
  	
  	
  	
  	
  	
  "files":	
  [...]	
  
	
  	
  	
  	
  },	
  
	
  	
  	
  	
  "autoload-­‐dev":	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  "psr-­‐4":	
  {...},	
  
	
  	
  	
  	
  	
  	
  	
  	
  "classmap":	
  [...],	
  
	
  	
  	
  	
  	
  	
  	
  	
  "files":	
  [...]	
  
	
  	
  	
  	
  },	
  
}
$	
  vim	
  composer.json
以 psr-­‐4 載⼊入
{	
  
	
  	
  	
  	
  "autoload":	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  "psr-­‐4":	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  "App":	
  "app/"	
  
	
  	
  	
  	
  	
  	
  	
  	
  },	
  
	
  	
  	
  	
  }	
  
}
$	
  composer	
  dump-­‐autoload
//	
  app/MyAwesomeClass.php	
  
namespace	
  App;	
  
class	
  MyAwesomeClass	
  extends	
  SuperPower	
  
{	
  
	
  	
  	
  	
  //	
  ...	
  
}
MyAwesomeClass.php
app
composer.json
//	
  index.php	
  
<?php	
  
require	
  __DIR__	
  .	
  '/vendor/autoload.php';	
  
use	
  AppMyAwesomeClass;	
  
$awesomeness	
  =	
  new	
  MyAwesomeClass();	
  
?>
autoload.php
vendor
index.php
以 classmap 載⼊入
$	
  composer	
  dump-­‐autoload
{	
  
	
  	
  	
  	
  "autoload":	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  "classmap":	
  [	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  "libs"	
  
	
  	
  	
  	
  	
  	
  	
  	
  ],	
  
	
  	
  	
  	
  }	
  
}
composer.json
autoload.php
vendor
index.php
libs
my-­‐old-­‐school-­‐class.php
//	
  classes/my-­‐old-­‐school-­‐class.php	
  
class	
  my_old_school_class	
  
{	
  
	
  	
  	
  	
  //	
  ...	
  
}
//	
  index.php	
  
<?php	
  
require	
  __DIR__	
  .	
  '/vendor/autoload.php';	
  
$oldschool	
  =	
  new	
  my_old_school_class();	
  
?>
以 files 載⼊入
$	
  composer	
  dump-­‐autoload
{	
  
	
  	
  	
  	
  "autoload":	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  "files":	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  "helpers/my-­‐functions.php"	
  
	
  	
  	
  	
  	
  	
  	
  	
  },	
  
	
  	
  	
  	
  }	
  
}
//	
  helpers/my_functions.php	
  
<?php	
  
if	
  (!	
  function_exists('super_power'))	
  {	
  
	
  	
  	
  	
  function	
  super_power()	
  
	
  	
  	
  	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  //...	
  
	
  	
  	
  	
  }	
  
}
//	
  index.php	
  
<?php	
  
require	
  __DIR__	
  .	
  '/vendor/autoload.php';	
  
?>	
  
<!-­‐-­‐	
  ...	
  -­‐-­‐>	
  
	
  	
  	
  	
  <h1><?php	
  super_power()	
  ?></h1>	
  
<!-­‐-­‐	
  ...	
  -­‐-­‐>
my_functions.php
helpers
composer.json
autoload.php
vendor
index.php
團隊/公司內的私有套件也可以⽤用
Composer 管理嗎?
當然可以!
建⽴立私有套件
#	
  建⽴立專案⺫⽬目錄	
  
$	
  mkdir	
  secret-­‐ingredient	
  
$	
  cd	
  secret-­‐ingredient	
  
$	
  git	
  init	
  
$	
  composer	
  init	
  
$	
  composer	
  install	
  
#	
  撰寫套件程式碼	
  
$	
  vim	
  ...	
  
$	
  git	
  commit	
  ...	
  
#	
  設定套件版本並發佈	
  
$	
  git	
  tag	
  -­‐a	
  1.0	
  
$	
  git	
  push	
  ...
設定 Repositories
{	
  
	
  	
  	
  	
  "require":	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  "{vendor}/{project}":	
  "{version}"	
  
	
  	
  	
  	
  },	
  
	
  	
  	
  	
  "repositories":	
  [	
  
	
  	
  	
  	
  	
  	
  	
  	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  "type":	
  "vcs",	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  "url":	
  "{url}"	
  
	
  	
  	
  	
  	
  	
  	
  	
  }	
  
	
  	
  	
  	
  ],	
  
}
$	
  composer	
  update
套件資訊庫 / Proxy
https://toranproxy.com/https://github.com/composer/satis
導⼊入 Migration
共同 DB 開發的問題
成員間 DB 共⽤用,A 改 B 壞 C 等
資料庫結構同步的問題
成員間 DB 不同步,A 改 B 壞 C 等
不是每個團隊都有 DBA,
如何讓資料庫也有版本管理的功能?
優化⽅方向
• 導⼊入 Migration ⼯工具
- 透過撰寫程式碼的⽅方式紀錄資料庫異動
- 部份⼯工具也有 Seeding 的設計,輕鬆產⽣生測試資料
- 部份⼯工具也有對應的 ORM,可⼀一併處理 Model
Laravel 的 Migration
$	
  artisan	
  make:migration	
  {name}
//	
  database/migrations/{migration}.php	
  
public	
  function	
  up()	
  
{	
  
	
   Schema::{create|table}('{table}',	
  function	
  (Blueprint	
  $table)	
  {	
  
	
   	
   $table-­‐>increments('id');	
  
	
   	
   	
  
	
   	
   $table-­‐>{column	
  type}('{column	
  name}');	
  
	
   	
   	
  
	
   	
   $table-­‐>timestamps();	
  
	
   });	
  
}
資料庫變更回溯
//	
  database/migrations/{migration}.php	
  
public	
  function	
  down()	
  
{	
  
	
  	
  	
  	
  //	
  寫跟	
  up()	
  反向的動作	
  
	
  	
  	
  	
  Schema::drop('{table}');	
  
	
  	
  	
  	
  Schema::table('{table}',	
  function	
  (Blueprint	
  $table)	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  $table-­‐>dropColumn('{column}');	
  
	
  	
  	
  	
  });	
  
}
$	
  artisan	
  migrate	
  
$	
  artisan	
  migrate:rollback
填充測試資料
$	
  artisan	
  make:seeder	
  {name}	
  
$	
  artisan	
  db:seed
//	
  database/seeds/{seeder}.php	
  
public	
  function	
  run()	
  
{	
  
	
  	
  	
  	
  {Model}::truncate();	
  
	
  	
  	
  	
  foreach(range(1,	
  10)	
  as	
  $number)	
  {	
  
	
  	
  	
  	
  	
  	
  	
  {Model}::create([	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  '{column}'	
  =>	
  '{value}',	
  
	
  	
  	
  	
  	
  	
  	
  ]);	
  
	
  	
  	
  	
  }	
  
}
不想⽤用 Laravel 的話也有解嗎?
不⽤用 Laravel 時的選擇
Phinx Lazy	
  Record Propel	
  Orm
Phinx
$	
  composer	
  require	
  robmorgan/phinx	
  
$	
  vendor/bin/phinx	
  init
///	
  phinx.yml	
  
paths:	
  
	
  	
  	
  	
  migrations:	
  %%PHINX_CONFIG_DIR%%/db/migrations	
  
	
  	
  	
  	
  seeds:	
  %%PHINX_CONFIG_DIR%%/db/seeds	
  
environments:	
  
	
  	
  	
  	
  default_migration_table:	
  phinxlog	
  
	
  	
  	
  	
  default_database:	
  development	
  
	
  	
  	
  	
  production:	
  #	
  ...	
  
	
  	
  	
  	
  development:	
  #	
  ...	
  
	
  	
  	
  	
  testing:	
  #	
  ...
Phinx Migration
$	
  vendor/bin/phinx	
  create	
  {name}	
  
$	
  vendor/bin/phinx	
  migrate	
  
$	
  vendor/bin/phinx	
  rollback
//	
  db/migrations/{migration}.php	
  
public	
  function	
  change()	
  
{	
  
	
  	
  	
  	
  $table	
  =	
  $this-­‐>table('{name}');	
  
	
   	
  
	
  	
  	
  	
  $table-­‐>addColumn('{column}',	
  '{data	
  type}')	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  -­‐>create();	
  
}
Phinx Seeding
$	
  vendor/bin/phinx	
  seed:create	
  {name}	
  
$	
  vendor/bin/phinx	
  seed:run
///	
  db/seeds/{seeder}.php	
  
public	
  function	
  run()	
  
{	
   	
  
	
  	
  	
  	
  $data	
  =	
  [];	
  	
  
	
  	
  	
  	
  foreach(range(1,	
  10)	
  as	
  $number)	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  $data[]	
  =	
  [	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  //	
  ...	
  
	
  	
  	
  	
  	
  	
  	
  	
  ];	
  
	
  	
  	
  	
  }	
  
	
   	
  
	
  	
  	
  	
  $this-­‐>insert('{table}',	
  $data);	
  
}
導⼊入適⽤用框架
先問你想解決什麼問題?
再來討論要怎麼解決。
哪⼀一種適合專案需求?
開發敏捷 穩定⾼高效能
努⼒力了這麼久…
總算可以試著導⼊入了!
導⼊入之前
PHP ⾄至少先升到 5.5.9 以
上 (Laravel 5.2 最低需求)
專案已導⼊入 Composer,
專案內的類別已經完成
autloading 的設定
導⼊入進程
相容現有資料庫 新舊版共容 更換套件 相容舊有網址
先從資料庫開始
只要 PDO ⽀支援的都可以!
//	
  config/database.php	
  
return	
  [	
  
	
  	
  	
  	
  'default'	
  =>	
  '{connection}',	
  
	
  	
  	
  	
  'connections'	
  =>	
  [	
  
	
  	
  	
  	
  	
  	
  	
  	
  'sqlite'	
  =>	
  [/*	
  ...	
  */],	
  
	
  	
  	
  	
  	
  	
  	
  	
  'mysql'	
  	
  =>	
  [/*	
  ...	
  */],	
  
	
  	
  	
  	
  	
  	
  	
  	
  'pgsql'	
  	
  =>	
  [/*	
  ...	
  */],	
  
	
  	
  	
  	
  	
  	
  	
  	
  'sqlsrv'	
  =>	
  [/*	
  ...	
  */],	
  
	
  	
  	
  	
  ],	
  
];
原⽣生不⽀支援的,也有 Package
https://github.com/yajra/laravel-­‐oci8http://php.net/oci8
最新流⾏行的,也有 Package
https://github.com/jenssegers/laravel-­‐mongodb
時下流⾏行的也有 Package 可⽤用
反轉 DB 結構成 Migration/Seed
• 將現有 DB 結構反轉成 Migration
- https://github.com/Xethron/migrations-generator
- https://github.com/nWidart/DbExporter
- https://gist.github.com/bruceoutdoors/9166186
• 將現有 DB 資料反轉成 Seed
- https://github.com/orangehill/iseed
P.S 無法完美轉換,尤其是 Relation 的部份,建議轉出來後⼿手動調整
讓 Eloquent 相容現有 DB
class	
  ExisteingDbModel	
  extends	
  Model	
  
{	
  
	
  	
  	
  	
  //	
  指定使⽤用的	
  DB	
  (依照	
  config/database.php	
  的設定	
  
	
  	
  	
  	
  protected	
  $connection	
  =	
  '{connection}';	
  
	
  	
  	
  	
  //	
  指定	
  Model	
  對應的	
  table	
  
	
  	
  	
  	
  protected	
  $table	
  =	
  '{table}';	
  
	
  	
  	
  	
  //	
  指定	
  table	
  內的主鍵	
  
	
  	
  	
  	
  protected	
  $primaryKey	
  =	
  '{column}';	
  
}
設定 column 預設值及型別
class	
  ExisteingDbModel	
  extends	
  Model	
  
{	
  
	
  	
  	
  	
  protected	
  $attributes	
  =	
  [	
  
	
  	
  	
  	
  	
  	
  	
  	
  '{column}'	
  =>	
  {value},	
  
	
  	
  	
  	
  ];	
  
	
  	
  	
  	
  protected	
  $cast	
  =	
  [	
  
	
  	
  	
  	
  	
  	
  	
  	
  '{column}'	
  =>	
  '{data	
  type}',	
  
	
  	
  	
  	
  ];	
  
}
讓 Eloquent 轉換 column 名稱
class	
  ExisteingDbModel	
  extends	
  Model	
  
{	
  
	
  	
  	
  	
  //	
  Accessors	
  
	
  	
  	
  	
  public	
  function	
  get{Column}Attribute()	
  
	
  	
  	
  	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  return	
  $this-­‐>attributes['{column}'];	
  
	
  	
  	
  	
  }	
  
	
  	
  	
  	
  //	
  Mutators	
  
	
  	
  	
  	
  public	
  function	
  set{Column}Attribute($value)	
  
	
  	
  	
  	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  $this-­‐>attributes['{column}']	
  =	
  $value;	
  
	
  	
  	
  	
  }	
  
}
相容⽇日期/時間的 column
class	
  ExisteingDbModel	
  extends	
  Model	
  
{	
  
	
  	
  	
  	
  //	
  設定需要轉換為	
  date/time	
  的	
  column	
  
	
  	
  	
  	
  protected	
  $dates	
  =	
  ['{column}'];	
  
	
  	
  	
  	
  //	
  設定	
  DB	
  內⽇日期時間格式	
  
	
  	
  	
  	
  protected	
  $dateFormat	
  =	
  '{format}';	
  
	
  	
  	
  	
  //	
  設定	
  created_at	
  及	
  updated_at	
  對應到的	
  column	
  
	
  	
  	
  	
  const	
  CREATED_AT	
  =	
  'created_at';	
  
	
  	
  	
  	
  const	
  UPDATED_AT	
  =	
  'updated_at';	
  
	
  	
  	
  	
  //	
  設定	
  Eloquent	
  是否啟動	
  created_at	
  及	
  updated_at	
  
	
  	
  	
  	
  public	
  $timestamps	
  =	
  {boolean};	
  
}
新/舊版網站共存
• 先在新版 (Laravel 版) 實作⾸首⾴頁,取代 index.php
- 更換 HTTP 伺服器的 Document Root
• 把舊版 (Page Script 版) 的前台網站直接放到 Laravel 專案
的 public 資料夾裡
- HTTP 伺服器優先使⽤用已經存在實體檔案的 Page Script
P.S	
  也可以視情況使⽤用	
  symbolic	
  link	
  讓新/舊版共存
$	
  ln	
  -­‐s	
  path/to/page-­‐script-­‐ver	
  path/to/laravel/public
先重做後台
//	
  設定	
  app/Http/routes.php	
  
//	
  把後台放到前置詞底下	
  
Route::group(['prefix'	
  =>	
  'admin'],	
  function()	
  {	
  
	
  	
  	
  	
  //	
  實際上的	
  URL	
  會是	
  "/admin/{uri}"	
  
	
  	
  	
  	
  Route::get('{uri}',	
  [/*	
  ...	
  */]);	
  
});	
  
//	
  把後台放到	
  sub-­‐domain	
  去	
  
Route::group(['domain'	
  =>	
  '{domain	
  name}'],	
  function()	
  {	
  
	
  	
  	
  	
  //	
  在	
  {domain	
  name}	
  底下才有辦法看到此	
  Route	
  
	
  	
  	
  	
  Route::get('{uri}',	
  [/*	
  ...	
  */]);	
  
});
抽換功能改⽤用套件⽣生態系
• Laravel 已經有做的功能就不要⾃自⼰己再重做⼀一次
- Route、MVC、Mail、Template Engine、Cron
• Laravel 沒有的功能也先找有沒有套件已經做過
- https://github.com/chiraggude/awesome-laravel
- https://github.com/summerblue/laravel-package-top-100
使⽤用 Route 處理舊版網址
//	
  app/Http/routes.php	
  
Route::get('{all}',	
  [/*	
  ...	
  */])	
  
	
  	
  	
  	
  	
  -­‐>where('all',	
  '.*');	
  
//	
  app/Http/Controllers/SeoController.php	
  
class	
  SeoController	
  extends	
  Controller	
  
{	
  
	
  	
  	
  	
  public	
  function	
  reroute($uri)	
  
	
  	
  	
  	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  //	
  ...	
  
	
  	
  	
  	
  	
  	
  	
  	
  return	
  redirect('{uri}',	
  301);	
  
	
  	
  	
  	
  }	
  
}
專案就是不能導⼊入…
但⼜又很想⽤用 Laravel 的語法?
置⼊入性 Programming
只要能⽤用	
  Composer	
  裝得都好說!
{	
  
	
  	
  	
  	
  "require":	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  "php":	
  ">=5.5.9",	
  
	
  	
  	
  	
  	
  	
  	
  	
  "illuminate/view":	
  "^5.2",	
  
	
  	
  	
  	
  	
  	
  	
  	
  "illuminate/database":	
  "^5.2",	
  
	
  	
  	
  	
  	
  	
  	
  	
  "illuminate/events":	
  "^5.2",	
  
	
  	
  	
  	
  	
  	
  	
  	
  "illuminate/pagination":	
  "^5.2",	
  
	
  	
  	
  	
  	
  	
  	
  	
  "illuminate/http":	
  "^5.2",	
  
	
  	
  	
  	
  	
  	
  	
  	
  "recca0120/laravel-­‐tracy":	
  "^1.5"	
  
	
  	
  	
  	
  }	
  
}
載⼊入 Laravel 元件
//	
  bootstrap.php	
  
use	
  IlluminateDatabaseCapsuleManager	
  as	
  Capsule;	
  
$capsule	
  =	
  new	
  Capsule;	
  
$capsule-­‐>addConnection([	
  
	
  	
  	
  	
  'driver'	
  	
  	
  	
  =>	
  'mysql',	
  
	
  	
  	
  	
  'host'	
  	
  	
  	
  	
  	
  =>	
  'host',	
  
	
  	
  	
  	
  'database'	
  	
  =>	
  'database',	
  
	
  	
  	
  	
  //	
  ...	
  
]);	
  
$capsule-­‐>setAsGlobal();	
  
$capsule-­‐>bootEloquent();
$	
  composer	
  require	
  illuminate/database
嫌太⿇麻煩?
{	
  
	
  	
  	
  	
  "require":	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  "recca0120/laravel-­‐bridge":	
  "^1.0.0"	
  
	
  	
  	
  	
  }	
  
}
寫好	
  Pacakge	
  等你裝!
$	
  composer	
  require	
  recca0120/laravel-­‐bridge
//	
  bootstrap.php	
  
<?php	
  
use	
  Recca0120LaravelBridgeLaravel;	
  
require	
  __DIR__.'/vendor/autoload.php';	
  
$connections	
  =	
  [	
  
	
  	
  	
  //	
  ...	
  
];	
  
Laravel::instance()	
  
	
  	
  	
  	
  -­‐>setupView(...,	
  ...)	
  
	
  	
  	
  	
  -­‐>setupDatabase($connections)	
  
	
  	
  	
  	
  -­‐>setupPagination()	
  
	
  	
  	
  	
  -­‐>setupTracy([	
  
	
  	
  	
  	
  	
  	
  	
  	
  'showBar'	
  =>	
  true	
  
	
  	
  	
  	
  ]);
★ 跟 Codeigniter 整合範例:
https://github.com/recca0120/laraigniter
導⼊入⾃自動化
只要重覆兩次以上的事情,
都有做⾃自動化的價值。
- 得寬 DevOps
http://blog.chengweichen.com/	
  
優化時機
• 建⽴立環境時
- ⽤用 Bash、⽤用 Automation Tool 來建⽴立環境
• 建⽴立專案時
- 使⽤用 composer	
  create-­‐project 簡化專案啟動步驟
• 部署專案時
- 使⽤用 task runner 協助部署動作
⾃自動化安裝環境
⾄至少可以先從 Bash 開始!
https://goo.gl/Ntf9lrhttps://github.com/laravel/settler
使⽤用⾃自⼰己的 skeleton
$	
  composer	
  create-­‐project	
  {my/skeleton}
Composer Script Events
"scripts":	
  {	
  
	
  	
  	
  	
  "post-­‐root-­‐package-­‐install":	
  [	
  
	
  	
  	
  	
  	
  	
  	
  	
  "php	
  -­‐r	
  "copy('.env.example',	
  '.env');""	
  
	
  	
  	
  	
  ],	
  
	
  	
  	
  	
  "post-­‐create-­‐project-­‐cmd":	
  [	
  
	
  	
  	
  	
  	
  	
  	
  "php	
  artisan	
  key:generate"	
  
	
  	
  	
  	
  ],	
  
	
  	
  	
  	
  "post-­‐install-­‐cmd":	
  [	
  
	
  	
  	
  	
  	
  	
  	
  "IlluminateFoundationComposerScripts::postInstall",	
  
	
  	
  	
  	
  	
  	
  	
  "php	
  artisan	
  optimize"	
  
	
  	
  	
  	
  ]	
  
},
$	
  composer	
  run-­‐script	
  {event}
⾃自動化部署
Local
$	
  envoy	
  run	
  deploy
Server	
  3Server	
  1 Server	
  2
Cloud	
  Servers //	
  Envoy.blade.php	
  
@servers(['web'	
  =>	
  '192.168.1.1'])	
  
@task('deploy',	
  ['on'	
  =>	
  'web'])	
  
	
  	
  	
  	
  cd	
  site	
  
	
  	
  	
  	
  git	
  pull	
  origin	
  {{	
  $branch	
  }}	
  
	
  	
  	
  	
  php	
  artisan	
  migrate	
  
@endtask
讓你的 PHP 專案潮起來!
優化
環境建置流程
升級
開發⼯工具
導⼊入
Composer
導⼊入
Migration
導⼊入
適⽤用框架
導⼊入
⾃自動化
透過可獨⽴立各別導⼊入的施⼒力點,⼀一步⼀一步現代化你的專案!
⼿手上有 Legacy 專案想升到 Laravel 嗎?
歡迎來找我們聊聊!
http://www.theqwan.com/contact
Q & A
感謝聆聽.歡迎交流

[Modern Web 2016] 讓你的 PHP 開發流程再次潮起來