Skip to content
scil edited this page Oct 1, 2019 · 33 revisions

Requirement by swoole

1. php functions useless in LaravelFly

name replacement
header Laravel API: $response->header
setcookie Laravel API: $response->cookie
http_response_code Laravel API: setStatusCode(404); or abort(404);

And, do not use flush()/ob_flush()/ob_end_flush()/ob_implicit_flush() for Laravel response.

2. readline()

With fpm, readline() returns false; with cli or swoole, it returns user input, or wait all day long...

a code sample from psysh

// vendor/psy/psysh/src/Shell.php
    protected function readline()
    {
        if (!empty($this->inputBuffer)) {
            $line = \array_shift($this->inputBuffer);
            ...
            return $line;
        }

        // added in a third-party Shell of laravel-web-tinker v1.5.1
        return false;

        $line = $this->readline->readline($this->getPrompt());  // readline() is called when $this->readline is instance of GNUReadline.

    }

laravel-web-tinker v1.5.0 uses psysh offical Shell, get user input from this function using $this->inputBuffer, finish execution when this function returns false which readline should supply. But with php cli, readline never return false unless user press Ctrl-D.

3. include/require and (include/require)_once

According to Laruence's blog Do NOT USE (include/require)_once

  • To include the files about class/interface/trait/function, sugguest to use (include/require)_once. In other cases, use include/require.

  • In the multi-process mode, the child process inherits the parent process resource. Once the parent process includes a file that needs to be executed, the child process will directly return true when it uses require_once(), causing the file to fail to execute. Now, you need to use include/require.

4. cookie size

The max size of GET request's header is 8KB.

5. Memory Control Requirements

Infinitely appending element into static/global variable will lead to memory leak.

This rule also works for variables in worker objects( objects or laravel services that made before any requests ).

// Some class
class Test
{
    public static $array = [];
    public static $string = '';
}

// Controller
public function test(Request $req)
{
    // Memory leak
    Test::$array[] = $req->input('param1');
    Test::$string .= $req->input('param2');
}

Requirement by LaravelFly

1.1 Consistency Requirements

  1. If coroutine is used, php configuration should keep same in any requests. Any changes of php configuration must be made before any requests (that is, made on worker ).

  2. constants should keep same in all requests.

  3. Super global vars like $_GET, $_POST should not be used if coroutine is used in your code.

  4. Localization. The same key for a lang should always be found in the same file.
    Give __('hello',[],'fr'), if 'hello' is found in 'lang-1/fr.json' in a request, found in 'lang-2/fr.json' in another request, some changed should be made in src/LaravelFly/Map/Illuminate/Translation and then, the cache $loaded in app('translator') would be not so much useful.

1.2 Consistency Requirements about static props

static props should keep consistent.

  1. Illuminate\Pagination\Paginator: currentPathResolver,currentPageResolver,viewFactoryResolver,defaultView,defaultSimpleView .

  2. Illuminate\Database\Eloquent\Model: globalScopes ( Global Scopes ) is an associated array, its values on the same key should always be same. For example, follwing code should not coexist in a project, because global scope 'age' means different closure.They should be merged into single closure.

    static::addGlobalScope('age', function (Builder $builder) {
            $builder->where('age', '>', 200);
    });
    
    static::addGlobalScope('age', function (Builder $builder) {
            $builder->where('age', '>', 200000000000000000000000000);
    });
    
    // The two addGlobalScope above should be merged into one, like this:
    
    static::addGlobalScope('age', function (Builder $builder) {
        if(Request::has('age'))
            $builder->where('age', '>', 200);
        else
            $builder->where('age', '>', 200000000000000000000000000);
    });
    
  3. If you use Laravel Macros, an object's Macros with same name should always be same if the object is made on worker, like addGlobalScope.

2. Coroutine Coding Rules

Mode Map uses coroutine, so different requests can be handled by server concurrently. Suppose the server is handling a request, meet co::sleep(3) , then it goes to handle another request, later go back to the first request.

  • Coroutine can not be used before requests. That is, coroutine can not used in a service if it booted on WorkerStart. LaravelFly uses \Co::getUid() as a key of arrays to store services infomation, \Co::getUid() should always returns 1 on WorkerStart.

  • Coroutine can not be used with route definition in route files, as I've moved coroutine refactor for 'groupStack' in vendor/scil/laravel-fly-files/src/Router.php.

     Route::get('/', 'Controller@home');
    
     // ❌ this line is not valid, as it will be executed by Router.php which has no coroutine support for groupStack
     co::sleep(100); 
    
     Route::get('/test', function () {
        co::sleep(100); // ✔️ this line is valid. 
    });
    

3. controller and Stale Reference

Like worker service, Controllers are not Singleton, but only instanced once for each route.

namespace Illuminate\Routing;
class Route
{
    ...
    public $controller;

    public function getController()
    {
        if (! $this->controller) {
            $class = $this->parseControllerCallback()[0];

            $this->controller = $this->container->make(ltrim($class, '\\'));
        }

        return $this->controller;
    }
}

So it is important to avoid Stale Reference

1.Incorrect usage.

namespace App\Http\Controllers;
class TestController extends Controller
{
    protected $userId;
    public function __construct()
    {
        // Incorrect usage: TestController is singleton instance, subsequent requests will misread the userId generated by the first request.
        $this->userId = session('userId');
    }
    public function testAction()
    {
        // read $this->userId;
    }
}

2.Correct usage.

namespace App\Http\Controllers;
class TestController extends Controller
{
    protected function getUserId()
    {
        return session('userId');
    }
    public function testAction()
    {
        // call $this->getUserId() to get $userId
    }
}
Clone this wiki locally