開發現代化 PHP 套件:從零開始

Outline

  • 自我介紹

  • 緣起

  • PHP 7.1+ 的新特性

  • 開發一個新套件

  • Live demo

自我介紹

  • Peter

  • GitHub

  • 目前在 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+ 環境

  • Composer Composer Composer

  • PSR-2, PSR-4

  • OOP principles

  • Design pattern

  • 軟體開發流程心法

  • 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,848