In the last few years, I’ve been swimming in the JavaScript and Golang ecosystem, and I’ve discovered many of the benefits of NPM and Go Mod. I recently went back to work on MixPHP V3 and realized that Composer has always been a very good tool. However, there are a lot of things that Phperers do not know about Composer very well. Help you understand the modern native PHP development process.

PHP developers are probably the most spoiled of all languages, because almost every framework provides scaffolding, like this:

composer create-project

This feature is not available in NPM, go mod, and requires you to create your own skeleton. Of course, NPM and go ecology have their own solution, which is to build scaffolding tools like vue-cli and mixcli.

Create a project

As with NPM init and go mod init, we use composer init to create a project

mkdir hello
cd hello
composer init 

After a few interactive fillings, a composer.json file is generated

{
    "name": "liujian/hello",
    "type": "project",
    "autoload": {
        "psr-4": {
            "Liujian\\Hello\\": "src/"
        }
    },
    "require": {}
}

The file was created using the standard composer library and had to have a two-level name, which annoyed me a lot, so I changed it

{
    "name": "project/app",
    "type": "project",
    "autoload": {
        "psr-4": {
            "App\\": "src/"
        }
    },
    "require": {}
}

Select the library I want to use

As with Node.js and the Go ecosystem, the second step is to find the libraries we need. Usually our requirement is to write an API service, so we need an HTTP Server library and a DB library to start working.

Since this is a modern PHP development, I chose the PHP CLI mode resident high-performance library, here I chose:

  • Mix/Vega Vega is a CLI schema HTTP networking framework written in PHP that supports Swoole and Workerman
  • Mix/Database Lightweight database for use in a variety of environments, support FPM, Swoole, Workerman, optional connection pooling (coroutine)

Both of these are core components of MixPHP V3+.

Mix Vega & Mix Database installation

Vega also supports Swoole, Workerman, and, in the future, Swow. The simplest principle is that Workerman can be implemented without installing an extension. Development uses Workerman to drive Vega first, and the Upline can be switched as needed.

Install the Workerman

composer require workerman/workerman

Install the Mix of Vega

composer require mix/vega

Install the Mix Database

composer require mix/database

Create an entry file

The entry to Vue is usually SRC /main.js. Since JS is usually a single-entry project, we will follow binary conventions and create a bin/start.php entry file

<? php require __DIR__ . '/.. /vendor/autoload.php'; $vega = new Mix\Vega\Engine(); $vega->handleF('/hello', function (Mix\Vega\Context $ctx) { $ctx->string(200, 'hello, world! '); })->methods('GET'); $http_worker = new Workerman \ Worker (" http://0.0.0.0:2345 "); $http_worker->onMessage = $vega->handler(); $http_worker->count = 4; Workerman\Worker::runAll();

Then we added the composer to the NPM set-up:

"scripts": {
      "server": "php bin/start.php start"
},

NPM does not require __DIR__. ‘/ / NPM does not require __DIR__. /vendor/autoload.php’; NPM Run Server runs a script that can find the corresponding dependencies by itself, but even if the script is executed by the composer run server, the composer still has to handle the autoload in the code and give a bad rating.

Now let’s try starting the service in Composer Run Server:

% composer run server > php8 bin/start.php start Workerman[bin/start.php] start in DEBUG mode -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- WORKERMAN -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- WORKERMAN version: 4.0.19 PHP version: 8.0.7 -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- WORKERS -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- the proto User worker listen processes status TCP liujian none http://0.0.0.0:2345 4 [OK] --------------------------------------------------------------------------------------------- Press Ctrl+C to stop. Start success.

Write an API interface

We will change the above entry file and write a user query interface. Vega is very easy to use.

<? php require __DIR__ . '/.. /vendor/autoload.php'; Const DSN = 'mysql: host = 127.0.0.1; port=3306; charset=utf8; dbname=test'; const USERNAME = 'root'; const PASSWORD = '123456'; $db = new \Mix\Database\Database(DSN, USERNAME, PASSWORD); $vega = new Mix\Vega\Engine(); $vega->handleF('/users/{id}', function (Mix\Vega\Context $ctx) use ($db) { $row = $db->table('users')->where('id = ? ', $ctx->param('id'))->first(); if (! $row) { throw new \Exception('User not found'); } $ctx->JSON(200, [ 'code' => 0, 'message' => 'ok', 'data' => $row ]); })->methods('GET'); $http_worker = new Workerman \ Worker (" http://0.0.0.0:2345 "); $http_worker->onMessage = $vega->handler(); $http_worker->count = 4; Workerman\Worker::runAll();

Curl Test this with:

% curl http://127.0.0.1:2345/users/1
{"code":0,"message":"ok","data":{"id":"1","name":"foo2","balance":"102","add_time":"2021-07-06 08:40:20"}}

Use the PSR to adjust the directory structure

Earlier we defined the PSR

"autoload": {
    "psr-4": {
        "App\\": "src/"
    }
},

Next we use automatic loading to reasonably split the above entry file code, the split directory structure is as follows:

─ bin │ sigma ── sigma ── sigma ── composer.json ─ SRC │ ├─ Controller │ sigma ── users.php │ sigma ── Database │ │ │ └ ─ ─ the PHP └ ─ ─ the Router │ └ ─ ─ Vega. PHP └ ─ ─ vendor
  • bin/start.php
<? php require __DIR__ . '/.. /vendor/autoload.php'; $vega = \App\Router\Vega::new(); $http_worker = new Workerman \ Worker (" http://0.0.0.0:2345 "); $http_worker->onMessage = $vega->handler(); $http_worker->count = 8; Workerman\Worker::runAll();
  • src/Router/Vega.php
<?php

namespace App\Router;

use App\Controller\Users;
use Mix\Vega\Engine;

class Vega
{

    /**
     * @return Engine
     */
    public static function new()
    {
        $vega = new Engine();

        $vega->handleC('/users/{id}', [new Users(), 'index'])->methods('GET');

        return $vega;
    }

}
  • src/Controller/Users.php
<? php namespace App\Controller; use App\Database\DB; use Mix\Vega\Context; class Users { public function index(Context $ctx) { $row = DB::instance()->table('users')->where('id = ? ', $ctx->param('id'))->first(); if (! $row) { throw new \Exception('User not found'); } $ctx->JSON(200, [ 'code' => 0, 'message' => 'ok', 'data' => $row ]); }}
  • src/Database/DB.php
<? php namespace App\Database; use Mix\Database\Database; Const DSN = 'mysql: host = 127.0.0.1; port=3306; charset=utf8; dbname=test'; const USERNAME = 'root'; const PASSWORD = '123456'; class DB extends Database { static private $instance; public static function instance() { if (! isset(self::$instance)) { self::$instance = new self(DSN, USERNAME, PASSWORD); } return self::$instance; }}

After the adjustment, you have basically completed the prototype of a formal project, and then you can play freely.

Pressure measure

Mysql: docker mysql8 native CPU: macOS M1 8 kernel mem: 16G wokerman (no libevent installed): 8 processes, equivalent to 8 MySQL connections

% WRK - 1000 - c d 1 m http://127.0.0.1:2345/users/1 Running 1 m test @ http://127.0.0.1:2345/users/1 2 threads and 1000 Connections Thread Stats AVG STDEV Max +/- STDEV Latency 36.08ms 8.11ms 428.09ms 95.38% REQ/SEC 3.49k 211.80 4.00k 71.00% 416817 requests in 1.00m, 109.31MB read Socket errors: connect 749, read 295, write 1, timeout 0 requests/SEC: 71.00% 416817 requests in 1.00m, 109.31MB read Socket errors: connect 749, read 295, write 1, timeout 0 requests/SEC: 6943.38 Transfer/SEC: 1.82 MB