Hướng dẫn create php extension

1. Zephir là gì?

Chắc hẳn chúng ta đã từng nghe qua framework Phalcon PHP. Phalcon là một PHP Framework được viết bằng ngôn ngữ lập trình C, sau đó được biên dịch thành một PHP extension(thành phần mở rộng cho PHP). Thực tế thì Phalcon Framework không phải là framework đầu tiên xây dựng theo phương pháp này, trước đó Yaf Framework cũng đã được xây dựng bằng phương pháp tương tự. Mục tiêu của Phalcon là trở thành Framework có tốc độ nhanh nhất cho PHP. Mặc dù đạt được tốc độ cao, tuy nhiên với cách làm trên, Phalcon cũng gây khó cho các lập trình viên PHP vì toàn bộ mã nguồn được viết bằng C. Vì vậy, đội phát triển Phalcon đã quyết định viết lại framework này bằng ngôn ngữ lập trình Zephir. Từ đó, nhóm lập trình Phalcon Framework đã xây dựng nên Phalcon bằng ngôn ngữ Zephir.

Zephir – Zend Engine PHP Intermediate

Zephir, một ngôn ngữ cấp cao, mã nguồn mở được thiết kế để dễ dàng tạo và duy trì các phần mở rộng cho PHP với trọng tâm vào sự an toàn của kiểu và bộ nhớ, là một ngôn ngữ giải quyết các nhu cầu chính của một nhà phát triển PHP cố gắng để viết và biên dịch mã có thể được thực hiện bởi PHP. Zephir là một ngôn ngữ khá đơn giản, cú pháp của Zephir là sự kết hợp giữa PHP và C. Khi biên dịch, toàn bộ mã nguồn Zephir sẽ được chuyển đổi sang ngôn ngữ lập trình C, kết quả biên dịch sau đó sẽ được dùng để tạo nên PHP extension.

Vậy tóm gọn zephir là cái gì vậy? Vâng, ngắn gọn thì Zephir là ngôn ngữ giúp chúng ta code một PHP extension dễ dàng hơn......

2. Cài đặt Zephir như thế nào?

2.1 Yêu cầu hệ thống:

Cũng không quá khó để cài Zephir. Chúng ta có thể tham khảo hướng dẫn cài đặt tại trang Installation của Zephir. Về cơ bản chúng ta có một số thứ cần thiết như sau:

  • gcc >= 4.x/clang >= 3.x
  • re2c 0.13 or later
  • gnu make 3.81 or later
  • autoconf 2.31 or later
  • automake 1.14 or later
  • libpcre3
  • php

Ví dụ mình sử dụng hệ điều hành Ubuntu để cài đặt Zephir:

$ sudo apt-get update
$ sudo apt-get install git gcc make re2c php7.0 php7.0-json php7.0-dev libpcre3-dev

Kiểm tra xem PHP đã được cài thành công chưa?

$ php -v
PHP 7.0.8 (cli) (built: Jun 26 2016 00:59:31) ( NTS )
Copyright (c) 1997-2016 The PHP Group
Zend Engine v3.0.0, Copyright (c) 1998-2016 Zend Technologies
        with Zend OPcache v7.0.8, Copyright (c) 1999-2016, by Zend Technologies

Kiểm tra thư viện phát triển PHP đi kèm:

$ phpize -v
Configuring for:
PHP Api Version:         20151012
Zend Module Api No:      20151012
Zend Extension Api No:   320151012

2.2 Cài đặt Zephir.

Tải source của Zephir trên Github về máyvà chạy cài đặt:

git clone https://github.com/phalcon/zephir
cd zephir && ./install -c

Kiểm tra xem zephir đã cài thành công chưa nào:

$ zephir help

Để chủ động, chúng ta nên set biến môi trường zephir để dễ dàng sử dụng mọi nơi:

vi $HOME/.bash_profile
export PATH=$PATH:~/app/zephir/bin

3. Cơ bản về Zephir.

3.1 Khởi tạo project.

Để tạo mới một project chúng ta gõ lệnh:

zephir init myapp

Cấu trúc thư mục của project có dạng như sau:

myapp/
 |-----ext/
 |-----myapp/
 |-----config.json

Chúng ta có thể đọc và tìm hiểu thêm về cú pháp, cách chạy, sử dụng các hàm, thư viện tại document của Zephir: Zephir Documentation

4. Bắt tay xậy dựng một extension đơn giản!

Chúng ta sẽ thử tạo một extension Route đơn giản bằng zephir:

4.1 Khởi tạo thư mục chứa source:

mkdir -p ~/simpleRoute/SimpleRoute
cd ~/simpleRoute/SimpleRoute
zephir init

Cấu trúc thư mục của extension của chúng ta khá đơn giản:

  • config.json - thông tin của extension như namespace, các package sử dụng....
  • ext/ - Thư mục generate ra extension
  • SimpleRoute/ - thư mục chứa các file .zep source

4.2 Code file Route

Chúng ta tạo một file route.zep:

namespace SimpleRoute;

class Router {

  /*
  * Trong zephir, 
  * để khai báo: sử dụng từ khóa var.
  * để gán giá trị, kiểu: sử dụng từ khóa let.
  * để gán ký tự: sử dụng ' '
  * để gán một chuỗi: sử dụng " "
  */  

  protected basePath = "";
  protected currentPath = "";
  protected defaultMethod = "";
  public notFound = null;

  public function __construct(string basePath = null) {
    let this->basePath = basePath;
    let this->defaultMethod = "index";
  }

  /**
   * Dispatch
   *
   * Khởi tạo đối tượng và gọi tới phương thức xử lý.
   */
  public function dispatch()
  {
    var parseURI; // Khai báo biến có kiểu động, có thể gán bằng bất kì kiểu giá trị nào
    let parseURI = []; // Quy định biến kiểu mảng
    let parseURI = this->parseURI(); // Gọi đến hàm xử lý parseURI
    if !empty(this->basePath) && this->basePath == parseURI[0] {
      let parseURI = array_slice(parseURI, 1); // Chúng ta có thể gọi trực tiếp các hàm PHP.
    }

    /*
    * Khai báo một số biến dùng chung.
    */
    var className;
    var method;
    var value;

    /*
    * Khởi tạo giá trị null cho biến.
    */
    let className = null;
    let method = null;

    /*
    * Check xem có dữ liệu hay không.
    * Sử dụng tương tự hàm empty, isset trong PHP.
    * https://github.com/phalcon/zephir-lexer/blob/master/zephir/zephir.py#L22
    */
    if fetch value, parseURI[0] { 
      let className = value;
      let className = ucwords(className);
    }

    if fetch value, parseURI[1] {
      let method = parseURI[1];
    }

    if is_null(className) || !class_exists(className) {
      this->error(404);
      return;
    }

    /**
     * PHP có thể tạo mới một instant từ tên class có thể truyền động vào.
     * Trong Zephir thì không, class name trong Zephir là cố định
     * Chúng ta sẽ đặt tên alias cho tham số class truyền vào cho dễ xử lý.
     */
    var classInstance;
    var classInstance = create_instance(className);
    
    if method == null {
      this->callMethod(classInstance, this->defaultMethod);
      return;
    }

    this->callMethod(classInstance, method);

  }

  /**
   * Call Method
   *
   * @param object $class
   * @param string $method
   */

  private function callMethod(var instance, string method) {
    if is_callable([instance, method]) { // check xem có phương thức này trong instant khởi tạo hay không.
      call_user_func([instance, method]);
    } else {
      this->error(404);
    }
  }

  /**
   * Error
   *
   * @param int $code
   */

  public function error(int code = 500) {
    if(code == 404) {
      if(is_callable(this->notFound)) {
        call_user_func(this->notFound);
      } else {
        echo "404 Not Found";
      }
    } else {
      echo "Error {code}";
    }
  }

  /**
   * Parse URI
   *
   * Phân tích URI để chuyển chúng thành class và method
   *
   * return array [$class, $method]
   */

  protected function parseURI()
  {
    var currentURI;
    let currentURI = _SERVER["REQUEST_URI"];
    var pattern = "@/([a-z][a-z0-9-]*)@i"; // sử dụng regex để xử lý

    var matches;
    let matches = [];
    preg_match_all(pattern, currentURI, matches);

    var schema;
    let schema = array_slice(matches, 1);
    return schema[0];
  }
}

Để đơn giản hơn, chúng ta có thể tìm một thư viện route đơn giản bằng code php, sau đó chúng ta convert sang code zephir sẽ dễ dàng hơn. hay chúng ta có thể sử dụng thư viện PHP to Zephir này để convert sang code zephir.

4.3 Biên dịch và cài đặt.

Để biên dịch và cài đặt extension vào hệ thống, chúng ta có thể sử dụng cách:

zephir build

Sau khi gõ lệnh trên, nếu màn hình hiển thị như thế này thì chúng ta đã biên dịch và cài đặt thành công:

Compiling… 
Installing… 
Extension installed! 
Add extension=simpleroute.so to your php.ini 
Don’t forget to restart your web server

Trong một số trường hợp lỗi, chúng ta có thể sử dụng cách thủ công:

Trước tiên chúng ta cần biên dịch ra file extension:

zephir compile

Tiếp theo để tích hợp extension vào hệ thống:

sudo cp ext/modules/simpleroute.so ~/etc/php/extensions/

Mở file php.ini lên, thêm dòng sau để kích hoạt extension lên:

extension=simpleroute.so

4.4 Tận hưởng thành quả

Chúng ta có thể tạo một file php để sử dụng extension vừa code:

<?php

class App {
    public function index() {
    echo "Hello, this index";
  }
  
  public function home() {
    echo "Hello, you should see this when open /app/home";
  }
}

$router = new SimpleRoute\Router();
$router->dispatch();

Như vậy là chúng ta đã hoàn thành một extension php đơn giản với zephir rồi đấy.

5. Kết luận

Zephir không phải là ngôn ngữ đơn giản về mặt cú pháp, đôi khi nhìn vào cú pháp của Zephir, bạn sẽ cảm thấy khó hiểu. Bản thân đoạn code C được biên dịch ra từ Zephir cũng phức tạp hơn việc cài đặt trực tiếp bằng C. Mặc đù vậy, Zephir là một dự án hay, nó vừa đơn giản hóa việc xây dựng các PHP extension, vừa mang lại hiệu năng cao cho ứng dụng. Chúng ta có thể tạo ra nhiều thứ khác hay ho với zephir như team Phalcon PHP đã làm được. Hi vọng rằng bài viết sẽ có ích với các bạn.

6. Một số tài liệu liên quan:

  • Zephir Lang Official
  • Phalcon Zephir Source
  • Convert PHP to Zephir
  • HHVM, Zephir, PHP: Cuộc chiến giữa những nền tảng chạy PHP
  • Getting Started with PHP Extension Development via Zephi
  • A quick introduction to Zephir language

Tải thêm tài liệu liên quan đến bài viết Hướng dẫn create php extension