PHP
Article

Can PHP Be Even Faster? Light-Speed with the Blink Framework

By Younes Rafie

Lumen, Silex, Slim, etc. you’ve heard of all of them. In this article, we’ll take a look at a newcomer called Blink.

Abstract image representing speed

Blink was built to improve high performance applications that consume a lot of server resources, and it uses the Swoole PHP extension to achieve this goal. As an example, we will build a Blink powered notejam for our demo. Let’s get started.

Installing the Swoole PHP Extension

We mentioned earlier that Blink relies on the Swoole extension for better performance.

You can install the Swoole extension from source on Github. Follow this article if you don’t know how to do it.

The alternative way to install it is by using the PEAR package manager, which is the approach I’ll take. I will be using an Ubuntu 14.04 image with the LAMP setup for this article, configured via Vaprobash – to get this exact setup, refer to the Vagrantfile in this project’s final repo. We could’ve used Homestead improved to easily set up the environment, but the Swoole extension is only compatible with PHP 5 for now, and Homestead Improved uses PHP 7 by default.

# update repositories
sudo apt-get update 

# Install pear manager
sudo apt-get install php-pear

# PHP source to compile the extension
sudo apt-get install php5-dev

# Used to install package dependencies
sudo apt-get install libcurl3-openssl-dev

# Installing Swoole
sudo pecl install swoole

The current Swoole version is 1.7.22-alpha and it’s not compatible with PHP 7, but there is an ongoing effort to make this happen in the next minor version.

After the commands above execute, we’re asked to add the extension=swoole.so line to php.ini. You can make sure that the extension is loaded by listing the available PHP modules command.

php -m | grep 'swoole'

Blink is available on Packagist, so we can use Composer to create a new project.

composer create-project --prefer-dist blink/seed

After all the dependencies have been loaded, you can run the server from the command line.

php blink server serve

This command will run an interactive shell for logs. However, you may use the php blink server start/stop/restart commands to manage the server.

We can make sure that everything is working as expected by visiting port 7788 of our local server:

Hello world

Configuration

While Blink doesn’t provide an option from the command line to specify the port, we can use the config/server.php file to change it.

// src/config/server.php

<?php
return [
    'class' => '\blink\server\SwServer',
    'bootstrap' => require __DIR__ . '/../bootstrap.php',
    'host' => '0.0.0.0',
    'port' => 8080,
];

Now, restart your server and check the browser using the new port. The config folder also holds configuration for our app and services.

Building the Notejam App

The Notejam application doesn’t contain a lot of functionality. You can check out the final app on Github. It offers:

  • A sign in page with the forgot password option.
  • A sign up page.
  • Account settings. (change user password)
  • Managing notes.
  • Managing pads.

Installing a Template Engine

Blink doesn’t provide a templating engine by default, we need to choose and inject our own. I chose to work with Twig, but you can easily switch to another engine like Blade, Smarty, etc. The src/bootstrap.php file is responsible for registering our routes, services and executing our application, and we will use it to bind the Twig engine to the application container.

// src/bootstrap.php

// ...
$app = new blink\core\Application($config);

require __DIR__.'/bindings.php';

return $app;
// src/bindings.php

<?php

/*
    Registering Twig
 */

$app->bind('twig.loader', function () {
    $view_paths = __DIR__.'/views';
    $loader = new \Twig_Loader_Filesystem($view_paths);

    return $loader;
});

$app->bind('twig', function () use($app) {
    $options = [
        'debug' => false,
        'cache' => __DIR__.'/../cache/views/compiled'
    ];

    $twig = new \Twig_Environment($app->get('twig.loader'), $options);

    // register Twig Extensions
    $twig->addExtension(new \Twig_Extension_Debug());

    // register Twig globals
    $twig->addGlobal('app', $app);

    return $twig;
});

Blink uses the Yii container for dependency injection. It has constructor and method injection, auto resolving for dependencies, etc. Check the documentation for more details.

Installing a Database Manager

The Notejam application requires database access for managing users, notes and pads. We’re going to use the Eloquent package for that, so we need to require it using Composer.

composer require illuminate/database 5.1.*
composer require illuminate/console 5.1.*
composer require illuminate/filesystem 5.1.*

We also required the console and filesystem packages to use database migrations to build our database.

// src/bindings.php

// ...

/*
    Registering Eloquent DB Manager
*/

use Illuminate\Database\Capsule\Manager as Capsule;

$capsule = new Capsule;
$capsule->addConnection(require __DIR__.'/config/database.php');

$app->bind('capsule', $capsule);
$capsule->setAsGlobal();
$capsule->bootEloquent();
// src/console/MigrateCommand.php

class MigrateCommand extends Command
{

    public $name = 'migrate';

    public $description = 'Migrate Database.';

    protected function execute(InputInterface $input, OutputInterface $output)
    {
        $capsule = app('capsule');
        $connectionResolver = $capsule->getDatabaseManager();
        $repository = new DatabaseMigrationRepository($connectionResolver, 'migrations');
        if( !$repository->repositoryExists() )
        {
            $repository->createRepository();
        }

        $migrator = new Migrator($repository, $connectionResolver, new Filesystem);
        
        return $migrator->run(__DIR__.'/../database/migrations');
    }
}
// src/config/app.php

<?php
return [
    // ...
    
    'commands' => [
        'App\Console\MigrateCommand',
    ]
];

After registering our command, we can run php blink migrate to create our database.

// src/database/migrations/2015_11_14_00_create_users_table.php

class CreateUsersTable extends Migration {
    public function up()
    {
       Capsule::schema()->create('users', function(Blueprint $table) {
            $table->increments('id');
            $table->string('email');
            $table->string('password', 60);
            $table->string('remember_token', 100)->nullable();
            $table->timestamps();
        });
    }

    public function down()
    {
        Capsule::schema()->drop('users');
    }
}
// src/database/migrations/2015_11_14_00_create_notes_table.php

class CreateNotesTable extends Migration {
    public function up()
    {
        Capsule::schema()->create('notes', function(Blueprint $table) {
            $table->increments('id');
            $table->string('name', 255);
            $table->text('text');
            $table->integer('user_id');
            $table->integer('pad_id')->nullable();
            $table->timestamps();
        });
    }

    public function down()
    {
        Capsule::schema()->drop('notes');
    }
}
// src/database/migrations/2015_11_14_00_create_pads_table.php

class CreatePadsTable extends Migration {
    public function up()
    {
        Capsule::schema()->create('pads', function(Blueprint $table) {
            $table->increments('id');
            $table->string('name', 255);
            $table->integer('user_id');
            $table->timestamps();
        });
    }

    public function down()
    {
        Capsule::schema()->drop('pads');
    }
}

Routes

Application routes are registered inside http/routes.php, and should return an array containing our routes definitions.

// src/http/routes.php

return [
    ['GET', '/signin', '\\App\\Http\\Controllers\\UserController@signin']
];

The first item in the array should define the request method. This could be a single item or an array to handle multiple request methods. The second item defines the request URL, and can contain rules for parameters.

// src/http/routes.php

return [
    ['GET', '/notes/{id}', '\\App\\Http\\Controllers\\NoteController@show']
];

// You can also use regex to filter parameters.

return [
    ['GET', '/notes/{id:\d+}', '\\App\\Http\\Controllers\\NoteController@show']
];

The third item specifies the request handler. This could be a Closure or a class name like the example above. Check the routes documentation for more details. Our full routes file looks like this.

// src/http/routes.php

return [
    ['GET', '/', function(blink\http\Response $response){
        return $response->redirect('/signin');
    }],

    // authentication
    ['GET', '/signin', 'UserController@signin'],
    ['POST', '/signin', 'UserController@processSignin'],

    ['GET', '/signup', 'UserController@signup'],
    ['POST', '/signup', 'UserController@store'],

    ['GET', '/logout', 'UserController@logout'],

    ['GET', '/settings', 'UserController@settings'],
    ['POST', '/settings', 'UserController@updateSettings'],

    //pads
    ['GET', '/pads', 'PadController@index'],
    ['GET', '/pads/{id:\d+}', 'PadController@show'],

    ['GET', '/pads/create', 'PadController@create'],
    ['POST', '/pads/create', 'PadController@store'],

    ['GET', '/pads/{id:\d+}/update', 'PadController@edit'],
    ['POST', '/pads/{id:\d+}/update', 'PadController@update'],

    ['GET', '/pads/{id:\d+}/delete', 'PadController@delete'],

    // Notes
    ['GET', '/notes', 'NoteController@index'],
    ['GET', '/notes/{id:\d+}', 'NoteController@show'],

    ['GET', '/notes/create', 'NoteController@create'],
    ['POST', '/notes/create', 'NoteController@store'],

    ['GET', '/notes/{id:\d+}/update', 'NoteController@edit'],
    ['POST', '/notes/{id:\d+}/update', 'NoteController@update'],

    ['GET', '/notes/{id:\d+}/delete', 'NoteController@delete'],
];

Controllers

Controllers are stored inside src/http/controllers, and you can avoid using the fully qualified namespace of your class if you store them there. However, you can use another path and change it inside the src/config/app.php file.

// src/config/app.php

<?php
return [
    // ...
    'controllerNamespace' => '\app\http\controllers',
];
// src/http/routes.php

return [
    ['GET', '/notes/{id}', 'NoteController@show'] // without a namespace
];

Sign Up

The Notejam application provides the HTML templates to be used, and we’re just going to convert them to Twig templates.

// src/views/user/signup.htm

{% extends 'layouts/layout.htm' %}

{% block page_title %}Sign Up{% endblock %}

{% block content %}
    <form action="/signup" method="POST" class="offset-by-six">
        <label for="email">E-mail</label>
        <input type="email" id="email" name="email" value="{{ oldInputs.email }}" />

        <label for="password">Password</label>
        <input type="password" id="password" name="password" value="" />

        <label for="password_confirmation">Password Confirmation</label>
        <input type="password" id="password_confirmation" name="password_confirmation" value="" />

        <input type="submit" value="Sign up" /> or <a href="/signin">Sign in</a>
    </form>
{% endblock %}

We’re extending the master layout that defines the common page structure including our page header and the errors partial.

// src/views/partials/errors.htm

{% set session = app.get('request').session %}

{% if session.get('success') or session.get('errors') %}
    <div class="alert-area">
        {% if session.get('success') %}
            <div class="alert alert-success">{{ session.get('success') }}</div>
        {% endif %}
        {% if session.get('errors') %}
            <div class="alert alert-error">
                {% for error in session.get('errors') %}
                    <ul class="errorlist">
                        <li>{{ error }}</li>
                    </ul>
                {% endfor %}
            </div>
        {% endif %}
    </div>
{% endif %}

We have access to the app container instance because we registered it as a global variable inside the Twig instance.

Our sign up form contains an email, password and a password confirmation field. Now we need to render our page.

// src/http/controllers/UserController.php

class UserController extends Object
{
    // ...
    
    public function signup()
    {
        return app()->twig->render('user/signup.htm');
    }
}

Sign up page

We handle the form submission inside our UserController@store method, and because we’ll need some validation logic, we can use the Illuminate/Validation component. So, let’s bind it to the container using our src/bindings.php file.

// src/bindings.php

// ...
/*
    Registering Validator
 */

use Illuminate\Container\Container;
use Illuminate\Filesystem\Filesystem;
use Illuminate\Translation\FileLoader;
use Illuminate\Translation\Translator;
use Illuminate\Validation\Factory;
use Illuminate\Validation\DatabasePresenceVerifier;

$loader = new FileLoader(new Filesystem, __DIR__.'/lang');
$translator = new Translator($loader, 'en');
$validation = new Factory($translator, new Container);
$validation->setPresenceVerifier(new DatabasePresenceVerifier(app('capsule')->getDatabaseManager()));
$app->bind('validation', $validation);

The setPresenceVerifier method is useful in this case because we’ll be using the unique validation rule, which tests for the existence of the element in the database.

// src/http/controllers/UserController.php

class UserController extends Object
{
    // ...
    public function store(Request $request, Hash $hash)
    {
        $rules = [
            'email'     => 'required|email|unique:users',
            'password'  => 'required|confirmed|min:8'
        ];

        $validator = app('validation')->make($request->all(), $rules);
        if ( $validator->fails() )
        {
            $request->session->add([
                'errors' => $validator->errors()->all(),
            ]);
            
            return app('twig')->render('user/signup.htm', [
                'oldInputs'     => $request->all()
            ]);
        }

        $user = new \App\Models\User;
        $user->email = $request->body->get('email');
        $user->password = $hash->make($request->body->get('password'));
        $user->save();
        
        $request->session->add(['success' => "Welcome to Notejam, you can now log in."]);

        return $response->redirect('/signin');
    }
}

Blink supports method injection, so we can just pass our classes as a parameter and they’ll be automatically resolved for us. After building the validation rules, if anything fails we return with old inputs. The $request->session object holds our current session instance, and this is where we store our success and error messages to be displayed by the errors partial.

Sessions &Amp; Cookies

Using PHP sessions, cookies and header functions won’t work, but we can access them from the request object ($request->getSession()). Note that this could be a problem for other packages you may want to install into your project.

Sign In

Before logging users in, we need to configure our authorization component.

// src/config/services.php

// ...
'auth' => [
        'class' => 'blink\auth\Auth',
        'model' => 'App\Models\User',
    ],
// ...

The next step is to update our User model and fill the Authenticatable interface methods.

// src/models/User.php

class User extends Model implements Authenticatable
{
    protected $table = 'users';
    
    public static function findIdentity($id)
    {
        if (is_numeric($id) || (is_array($id) && isset($id['id']))) {
            return static::where('id', $id)->first();
        } else if (is_string($id) || (is_array($id) && isset($id['email'])) ) {
            return static::where('email', $id)->first();
        } else {
            throw new \InvalidParamException("The param: id is invalid.");
        }
    }

    public function getAuthId()
    {
        return $this->id;
    }

    public function validatePassword($password)
    {
        return (new Hash)->check($password, $this->password);
    }
}

The findIdentity method will be used to authenticate users using the primary key or email, and validatePassword will test the login password against the current user instance.

// src/views/user/signin.htm

{% extends 'layouts/layout.htm' %}

{% block page_title %}Sign In{% endblock %}

{% block content %}
    <form action="/signin" method="POST" class="offset-by-six">
        <label for="email">E-mail</label>
        <input type="email" value="{{ oldInputs.email }}" name="email" id="email" />

        <label for="email">Password</label>
        <input type="password" value="" name="password" id="password" />

        <input type="submit" value="Sign in" /> or <a href="/signup">Sign up</a>
        <hr />
        <p><a class="small-red" href="/forgot_password">Forgot Password?</a></p>
    </form>
{% endblock %}

Sign in page

// src/controllers/UserController.php

class UserController extends Object
{
    // ...
    public function processSignin(Request $request, Hash $hash)
    {
        $rules = [
            'email'     => 'required|email',
            'password'  => 'required|min:8'
        ];
        $validator = app('validation')->make($request->all(), $rules);
        if ( $validator->fails() )
        {
            $request->session->add([
                'errors' => $validator->errors()->all(),
            ]);

            return app('twig')->render('user/signin.htm', [
                'oldInputs'     => $request->all()
            ]);
        }

        $user = auth()->attempt($request->only(['email', 'password']));

        if ( !$user )
        {
            $request->session->add([
                'errors' => ['Login error.'],
            ]);

            return app('twig')->render('user/signin.htm', [
                'oldInputs'     => $request->all()
            ]);
        }
        
        $cookies = $response->getCookies()->add( new \blink\http\Cookie([
            'name' => 'SESSIONID', 
            'value' => $request->session->id
        ]) );

        return $response->redirect('/settings');
    }
}

We validate inputs the same way and display errors in case of failure. The auth() helper will resolve the blink/auth/Auth instance from the container, and we attempt logging the user in with the request credentials. If the login credentials are correct, we redirect the user to the settings page. Blink doesn’t manage our session automatically, so we need to set the session cookie on login and set it on the request on page load. Blink allow us to bind our services to the container using a configuration array.

// src/config/services.php

return [
    'request' => [
        'class' => \blink\http\Request::class,
        'middleware' => [\App\Http\Middleware\AuthMiddleware::class],
        'sessionKey' => function (\blink\http\Request $request) {
                $cookie = $request->cookies->get('SESSIONID');
                if ($cookie) {
                    return $cookie->value;
                }
        }
    ],
    // ...
];

Blink uses Yii’s configuration concept to initialize class attributes. We use a closure function to initialize the sessionKey using the cookie variable if it exists.

After a successful login attempt, we can access the logged in user from the request object using $request->user() and $request->guest(). Check the documentation for more details about authentication.

Account Settings

The logged in user has the ability to change his password. He must enter the old password, the new password and a confirmation. We follow the same steps as before.

// src/http/controllers/UserController.php

class UserController extends Object
{
    public function updateSettings(Request $request, Hash $hash)
    {
        $user = $request->user();
        $rules = [
            'old_password'   => 'required|min:8',
            'password'      => 'required|confirmed|min:8'
        ];

        $validator = app('validation')->make($request->all(), $rules);
        if ( $validator->fails() )
        {
            $request->session->add([
                'errors' => $validator->errors()->all(),
            ]);

            return app('twig')->render('user/settings.htm', [
                'oldInputs'     => $request->all()
            ]);
        }

        if( !$hash->check($request->input('old_password'), $user->password) )
        {
            $request->session->add([
                'errors' => ['Old password incorrect.'],
            ]);

            return app('twig')->render('user/settings.htm', [
                'oldInputs'     => $request->all()
            ]);
        }

        $user->password = $hash->make($request->input('old_password'));
        $user->save();

        $request->session->add([
                'success' => 'settings updated successfuly.',
        ]);

        return app('twig')->render('user/settings.htm');
    }
}

The $hash->check method compares a value with a hashed version. After updating the user settings we return with a success message. The problem here is that we are accessing the $request->user() method directly which returns the currently logged in user, but what if the user is not logged in yet and tries to access the settings page?

Account settings page

Middleware

To guard our routes we can define a list of middleware to our request object. We can do that inside our services.php config file.

// config/services.php

return [
    'request' => [
        'class' => \blink\http\Request::class,
        'middleware' => [\App\Http\Middleware\AuthMiddleware::class],
        'sessionKey' => function (\blink\http\Request $request) {
                $cookie = $request->cookies->get('SESSIONID');
                if ($cookie) {
                    return $cookie->value;
                }
        }
    ],
    // ...
];

Now we need to create our AuthMiddleware inside the src/http/middleware folder. The middleware will intercept all the requests, and we should compare the current request with our guarded routes.

// src/http/middleware/AuthMiddleware.php

class AuthMiddleWare implements MiddlewareContract
{
    public function handle($request)
    {
        $guardedRoutes = [
            '/\/settings/',
            '/\/logout/',
            '/\/notes?\/*.*/',
            '/\/pads?\/*.*/',
        ];

        if ( !$request->user() )
        {
            foreach ($guardedRoutes as $route)
            {
                if ( $request->match($route) )
                {
                    return response()->redirect('/signin');
                }
            }
        }

        if ( $request->user() && in_array($request->path, ['/signin', '/signup']))
        {
            return response()->redirect('/settings');
        }

    }
}

The Request@match method tests the current request path against a regular expression. It uses the PHP preg_match function, so we need to define our guarded routes as regular expressions.
If the user is not logged in and tries to access one of the guarded routes, we redirect them to the sign in page. We also want to prevent the user from accessing the signin and signup pages when they are logged in.

You can also use the before and after methods inside the controller to achieve the same result.

// src/http/controllers/UserController.php

class UserController extends Object
{
    public function before($action, $request)
    {
        // do something
    }

    public function after($action, $request, $response)
    {
        // do something
    }
}

If you noticed, in the services file, the response object also has middleware. It can be used to intercept the response object and add some information to the response, or act as a response formatter.

List Notes

The NoteController class is responsible for handling our notes’ CRUD operations.

// src/http/controllers/NoteController.php

class NoteController extends Object
{
    public function index(Request $request, Note $noteModel)
    {
        $notes = $noteModel
                    ->where('user_id', $request->user()->id)
                    ->with('pad')
                    ->get();

        return app('twig')->render('notes/index.htm', [
            'notes' => $notes
        ]);
    }
}

Because Blink supports method injection, we pass the Request and Note objects and query the user notes.

// src/views/notes/index.htm

{% extends 'layouts/layout.htm' %}

{% block page_title %}My Notes{% endblock %}

{% block content %}
    {% if notes|length %}
        <table class="notes">
            <tr>
                <th class="note">Note</th>
                <th>Pad</th>
                <th class="date">Last modified</th>
            </tr>
            {% for note in notes %}
                <tr>
                    <td><a href="/notes/{{ note.id }}">{{ note.name }}</a></td>
                    <td class="pad">
                        {% if note.pad is defined %}
                            <a href="/pads/{{ note.pad.id }}">{{ note.pad.name }}</a>
                        {% else %}
                            No pad
                        {% endif %}
                    </td>
                    <td class="hidden-text date">{{ note.updated_at|date("F jS \\a\\t g:ia") }}</td>
                </tr>
            {% endfor %}
        </table>
    {% else %}
        <p class="empty">Create your first note.</p>
    {% endif %}
    <a href="/notes/create" class="button">New note</a>
{% endblock %}

List notes page

View Note

A logged in user can only view his own notes and manage them. And because we’ll need to often check for this condition, we’re going to put it in a separate method.

// src/http/controllers/NoteController.php

class NoteController extends Object
{

    protected function noteExists( $noteId )
    {
        $request = request();
        $note = Note::with('pad')
                    ->where('user_id', $request->user()->id)
                    ->where('id', $noteId)
                    ->first();
        if( !$note )
        {
            $request->session->add([
                'errors' => ["Note not found."],
            ]);

            return false;
        }

        return $note;
    }
    // ...
}
// src/http/controllers/NoteController.php

class NoteController extends Object
{
    public function show($id, Request $request, Response $response, Note $noteModel)
    {
        $note = $this->noteExists($id);

        if( !$note )
        {
            return $response->redirect('/notes');
        }

        return app()->twig->render('notes/view.htm', [
            'note' => $note
        ]);
    }
}
// src/views/notes/view.htm

{% extends 'layouts/layout.htm' %}

{% block page_title %}{{ note.name }}{% endblock %}

{% block content %}
    <p class="hidden-text">Last edited at {{ note.updated_at|date("F jS \\a\\t g:ia") }}</p>
    <div class="note">
        <p>
            {{ note.text }}
        </p>
    </div>
    <a href="/notes/{{ note.id }}/update" class="button">Edit</a>
    <a href="/notes/{{ note.id }}/delete" class="delete-note">Delete it</a>
{% endblock %}

View note page

New Note

The new note view has a name, a text, and a pad input.

// src/views/notes/create.htm

{% extends 'layouts/layout.htm' %}

{% block page_title %}New Note{% endblock %}

{% block content %}
    <form action="/notes/create" method="POST">
        <label for="name">Name</label>
        <input type="text" id="name" name="name" value="{{ oldInputs.name }}"/>
        
        <label for="text">Text</label>
        <textarea id="text" name="text" value="{{ oldInputs.text }}"></textarea>

        <label for="pad">Pad</label>
        <select id="pad" name="pad">
            {% for pad in pads %}
                <option value="{{ pad.id }}">{{ pad.name }}</option>
            {% endfor %}
        </select>

        <input type="submit" value="Create" />
    </form>
{% endblock %}
// src/http/controllers/NoteController.php

class NoteController extends Object
{
    public function create(Request $request, Pad $padModel)
    {
        $pads = $padModel->where('user_id', $request->user()->id)->get();

        return app('twig')->render('notes/create.htm', [
            'pads' => $pads
        ]);
    }
}

New note page

Because we already set up our model relations, we can also do something like $request->user()->pads. The store method handles the form submission.

// src/http/controllers/NoteController.php

class NoteController extends Object
{
    public function store(Request $request, Response $response)
    {
        $rules = [
            'name'  => 'required',
            'text'  => 'required',
            'pad'   => 'required|exists:pads,id'
        ];
        $validator = app('validation')->make($request->all(), $rules);
        
        if ( $validator->fails() )
        {
            $request->session->add([
                'errors' => $validator->errors()->all(),
            ]);
            
            return app('twig')->render('notes/create.htm', [
                'oldInputs' => $request->all()
            ]);
        }

        $note = new Note;
        $note->name = $request->input('name');
        $note->text = $request->input('text');
        $note->user_id = $request->user()->id;
        $note->pad_id = $request->input('pad');
        $note->save();

        $request->session->add([
            'success' => 'Note saved successfully.',
        ]);

        return $response->redirect("/notes/{$note->id}/update");
    }
}

We follow the same process of validating data and returning errors in case of messages. To avoid pasting repetitive tasks for updating, deleting, and viewing notes you can check the final result on Github. The repository also contains the installation steps.

Final Notes

Although Swoole and Blink try to economize server resources by keeping your application resources alive through the application’s lifetime, the request and response objects are always refreshed on every request. Blink provides a ShouldBeRefreshed interface that you can implement to indicate that the class instance should be refreshed on every request.

Conclusion

Blink is a newcomer and still in its early development stages. You can collaborate by writing documentation for different components and send pull requests for new features. If you have any questions or comments you can post them below and let us know what you think about this new framework!

  • Radek Dvořák

    Hi Younes,

    thanks for the article. I think the blink framework itself is not as interesting as the swoole extension it uses. Furthermore there is also an official Swoole framework :-) From the information at the extension’s website it utilizes multiprocessing to handle concurent requests. Unfortunately I have not found any explanation how it manages data sharing & synchronization between processes. Perhaps it forks the main process so that all the data are available in children. In such a case any changed information would be lost when children exit. I can imagine such an approach could be also implemented with reactphp. How would it compare performance-wise?

    Once again thanks for the article.

    • younesrafie

      I definitely agree that the Swoole extension is interesting. In fact, the doc says that a lot of high traffic websites are using it, and it proved to be an efficient solution for performance problems.

      I haven’t tried ReactPHP yet to do the comparison :)

  • Alan

    Thanks for the article, but how is this related to your title? I don’t see how it makes PHP faster.

    • younesrafie

      It makes PHP faster by using the Swoole extension.

  • http://devrr.p.ht/ Lewis Josś Yuburi Medina

    I love how they can include the best of laravel / Lumen in Blink! I’ll try!

Recommended
Sponsors
Because We Like You
Free Ebooks!

Grab SitePoint's top 10 web dev and design ebooks, completely free!

Get the latest in PHP, once a week, for free.