開發現代化 PHP 套件:從零開始
Outline
-
自我介紹
-
緣起
-
PHP 7.1+ 的新特性
-
開發一個新套件
-
Live demo
自我介紹
-
Peter
-
目前在 ITRI 服務
-
後端開發者
-
3+ years PHP 後端程式開發
-
PHP 5.3 → PHP 7+
-
會一點 Python
-
No framework→Slim→Laravel
-
2 years 開源專案貢獻者
緣起
PM
離職工程師
我
請長(ㄔㄢˇ)假工程師
有一天呢
-
PM:XXX 要請假,OOO 要離職
-
PM:你要去接 OOXX 平台
-
我:恩好
OOXX 平台
-
Laravel 5.2
-
Ubuntu 14.04
-
PHP 5.5.9
README
看完 README 之後
其實根本沒有 README
那去看看 source code?
line of source code
find . -name '*.php' | xargs wc -l
WOW
47 ./xxxxController.php
32 ./Auth/xxxxxxxController.php
73 ./Auth/xxxxxController.php
117 ./xxxxxController.php
928 ./xxxxxController.php
204 ./xxxxController.php
217 ./xxxxxController.php
73 ./xxxxxxxxController.php
13 ./Controller.php
260 ./xxxxxpController.php
12 ./xxxxxController.php
216 ./xxxxxController.php
43 ./xxxxxController.php
1965 ./xxxxxxController.php
362 ./xxxxxController.php
264 ./xxxxxxController.php
409 ./District/xxxxxxController.php
727 ./xxxxxxController.php
2753 ./xxxxxxController.php
41 ./xxxxxxController.php
Controllers 裡面到底?
public function getAminInfo(Request $request)
{
$adminId = Session::get('admin.id');
$isManager = Session::get('admin.alarmMesg');
$isExtended = Session::get('admin.extended');
$sql='SELECT admin.* FROM admin WHERE id = ?';
$admins = DB::select($sql, array($adminId));
if(empty($admins))
$admin=NULL;
else
$admin = $admins[0];
......
為什麼 Controller 有 DB 操作?
原來這專案 Controller 包山包海呢!
這專案到底有什麼問題?
-
MVC 的 pattern 不見了
-
用了框架 !== 變潮
-
-
Coding style → Free style
-
上線環境過度的老舊
PHP 5.5 → PHP 7.1+
PHP 7.1+ 帶來的特性
-
function testReturn(): void
-
function testReturn(): ?string
-
Sodium is now a core extension
-
Password hashing with Argon2
開發現代 PHP 套件你需要知道事情
-
PHP 7.1+
-
Ubuntu 16.04 or 18.04 or any Linux distro
-
sudo add-apt-repository ppa:ondrej/php -y
-
sudo apt-get update
-
sudo apt-get install php 7.1
-
參考資料
前置環境
-
curl -sS https://getcomposer.org/installer | php
-
https://getcomposer.org/download/
1.8.4/composer.phar
Composer Composer Composer
-
Single Responsibility
-
Open-Close Principle
-
Liskov Substitution
-
Interface Segregation
-
Dependency Inversion
Single Responsibility
-
A Class should be responsible for a single task.
Single Responsibility
class User
{
protected function formatResponse($data)
{
return [
'name' => $data->name,
'userName' => $data->userName,
];
}
protected function validator($user)
{
if ($user === '') {
throw new \InvalidArgumentException("User doesn't exist");
}
}
protected function fetchUserInformation($userId)
{
return DB::table('users')->findOrFail($userId);
}
}
Open-Close Principle
-
A Class should be open to extension and close to modification.
Open-Close Principle
interface LoginInterface
{
public function authUser($user);
}
class NormalLogin implements LoginInterface
{
public function authUser($user)
{
// auth logic...
}
}
Open-Close Principle
class FacebookLogin implements LoginInterface
{
public function authUser($user)
{
// auth logic...
}
}
class LoginModule
{
public function login(LoginInterface $user)
{
$user->authUser($user);
}
}
Liskov Substitution
-
This principle is about subtyping and inheritance.
-
Derived classes must be substitutable for their base classes.
Liskov Substitution
class Rectangle
{
protected $width;
protected $height;
public function setHeight($height)
{
$this->height = $height;
}
public function getHeight()
{
return $this->height;
}
Liskov Substitution
public function setWidth($width)
{
$this->width = $width;
}
public function getWidth()
{
return $this->width;
}
public function area()
{
return $this->height * $this->width;
}
}
Liskov Substitution
class Square extends Rectangle
{
public function setHeight($value)
{
$this->width = $value;
$this->height = $value;
}
public function setWidth($value)
{
$this->width = $value;
$this->height = $value;
}
}
Interface Segregation
-
Don’t make FAT Interfaces.
-
Many client-specific interfaces are better than one general-purpose interface.
Interface Segregation
interface Codeable
{
public function code();
}
interface Testable
{
public function test();
}
class Programmer implements Codeable, Testable
{
public function code()
{
return 'coding';
}
public function test()
{
return 'testing in localhost';
}
}
Interface Segregation
class Tester implements Testable
{
public function test()
{
return 'testing in test server';
}
}
class ProjectManagement
{
public function processCode(Codeable $member)
{
$member->code();
}
}
Dependency Inversion
-
Depend on abstractions, not on concretions.
Dependency Inversion
interface Mailer
{
public function send();
}
class SmtpMailer implements Mailer
{
public function send()
{
}
}
Dependency Inversion
class SendWelcomeMessage
{
private $mailer;
public function __construct(Mailer $mailer)
{
$this->mailer = $mailer;
}
}
class SendGridMailer implements Mailer
{
public function send()
{
}
}
Design pattern
-
You might manage to work as a programmer for many years without knowing about a single pattern.
-
They're a toolkit of tried and tested solutions to common problems in software design.
-
It defines a common language that you and your teammates can use to communicate more efficiently.
Further reading
軟體開發流程心法
分析 → 設計 → 實做 → 測試
分析
-
PM : 我需要一個兩個整數相加減乘除的功能
-
整數可能會很大
- GMP (sudo apt-get install php-gmp)
- BcMath (sudo apt-get install php-bcmath)
設計
$gmp = new GmpCalculator();
$bcMath = new BcMathCalculator();
$calculator = new Calculator($gmp);
$calculator->add('11111', '22222222');
$calculator->minus('11111', '22222222');
$calculator->mul('11111', '22222222');
$calculator->divide('11111', '22222222');
實做
interface CalculatorInterface
{
public function add($num1, $num2);
public function minus($num1, $num2);
public function mul($num1, $num2);
public function divide($num1, $num2);
}
class GmpCalculator extends Calculator
{
// calculation logic implementation
}
class BcMathCalculator extends Calculator
{
// calculation logic implementation
}
class Calculator
{
// calculation logic
}
測試
Doing unit test with PHPUnit
-
設計測試案例
剩下的就是你們的事情了!
Any questions?
開發現代化PHP套件:從零開始
By peter279k
開發現代化PHP套件:從零開始
SITCON 2019
- 1,978