Xây dựng ứng dụng console với PHP CLI

    0

    Rất nhiều người học PHP thường nghĩ rằng PHP luôn phải gắn chặt với phát triển ứng dụng web.

    Thực tế không phải như vậy. Mặc dù được sử dụng chính trong web, PHP là một ngôn ngữ lập trình script đa năng tương tự như Python. Bạn có thể sử dụng PHP để viết chương trình chạy trên hệ điều hành giống như với các ngôn ngữ lập trình khác.

    Cách thức đơn giản và phổ biến nhất là sử dụng PHP CLI để chạy các script dưới dạng ứng dụng console (đa nền tảng).

    Chúng ta đã sử dụng cách thức này để dạy/học ngôn lập trình PHP cơ bản. Cách học này giúp tránh những phức tạp của ứng dụng web để tập trung vào cú pháp của chính ngôn ngữ trong giai đoạn đầu.

    Trong bài học này chúng ta sẽ tổng hợp tất cả các kiến thức cơ bản đã học để xây dựng một ứng dụng quản lý sách điện tử ở dạng console đơn giản.

    Đây sẽ là bài học cuối vận dụng PHP CLI trước khi chuyển sang ứng dụng web.

    Chuẩn bị project

    Để thực hiện bài học này chúng ta sẽ sử dụng IDE PhpStorm (bản 2020.2). Theme sử dụng là Visual Studio Dark (Rider UI Them Pack plugin). Nếu bạn chưa cài đặt PHP và PhpStorm, hãy đọc lại bài học về cấu hình PHP CLI và IDE.

    Tạo một project mới theo các bước sau:

    Bước 1. Ấn nút New Project để tạo một dự án mới

    Bước 2. Chọn mục PHP Empty Project trong danh sách bên trái. Mục Location chỉ định đường dẫn đến nơi sẽ chứa các file của dự án.

    Bước 3. Trong thư mục CLI tạo 4 file php mới:

    • BookMan.php – file script chính của chương trình.
    • Config.php – file chứa một số thông tin cấu hình.
    • Console.php – file chứa các lệnh để xuất nhập dữ liệu với giao diện console.
    • DataService.php – file chứa các lệnh làm việc với dữ liệu.

    Tất cả các file script tạm thời để trống. Chúng ta sẽ lần lượt viết code cho chúng trong các phần tiếp theo của bài học.

    Chuẩn bị ứng dụng

    Bước 1. Cấu hình ứng dụng

    Để tiện lợi khi viết code về sau, chúng ta trước hết định nghĩa một số hằng trong file Config.php như sau:

    <?php
    
    // Một số mã màu sử dụng trong console
    define('RED', "\e[31m");
    define('GREEN', "\e[32m");
    define('BLUE', "\e[34m");
    define('RESET', "\e[0m");
    
    // Cặp escape sequence \r\n
    define('RN', "\r\n");
    
    // Tên file dữ liệu
    define('DATAFILE', 'DATA.JSON');

    Bước 2. Chỉ định tải các script ở đầu file BookMan.php như sau:

    <?php
    
    require_once "Config.php";
    require_once "Console.php";
    require_once "DataService.php";

    Bước 3. Xây dựng hàm hỗ trợ main_error và main_help cho BookMan.php

    Chúng ta xây dựng hàm hỗ trợ thông báo lỗi main_error và hàm trợ giúp main_help trong BookMain.php như sau:

    /**
     * Báo lỗi
     * @param string $cmd lệnh lỗi
     */
    function main_error(string $cmd) {
        echo RED . "Command '$cmd' not found! Type 'help' for supported commands." . RESET . RN;
    }
    
    /**
     * Hiển thị trợ giúp
     */
    function main_help() {
        echo BLUE . 'BOOK MANAGER V1.0 By TuHocICT.com' . RESET . RN;
        echo <<<MENU
    help     Show this menu
    quit     Exit program
    
    MENU;
    }

    Bước 4. Tạo mảng chứa thông tin ánh xạ chuỗi => hàm

    Mảng $actions chứa thông tin để ánh xạ chuỗi lệnh người dùng nhập sang một hàm.

    // mảng actions chứa thông tin để ánh xạ chuỗi lệnh người dùng nhập sang một hàm
    $actions = [
        'help' => function() { main_help();},
        'quit' => function() { exit();},
    ];

    Trong mảng $actions chúng ta sử dụng hàm lambda làm giá trị cho mỗi phần tử, còn chuỗi lệnh làm khóa.

    Hiện tại $actions chỉ chứa hai phần tử: lệnh ‘help’ sẽ ánh xạ sang chạy hàm main_help(), lệnh ‘quit’ sẽ ánh xạ sang chạy hàm exit(). Trong các phần sau của bài học chúng ta sẽ lần lượt bổ sung thêm các lệnh mới vào $actions.

    Bước 5. Xây dựng hàm entry point cho ứng dụng

    Mỗi ứng dụng đều cần một điểm khởi đầu (entry point) để chạy các tính năng. Trong script này hàm entry point sẽ được đặt tên là main(). Hàm main() chứa vòng lặp chính của chương trình.

    /**
     * Entry point
     */
    function main() {
        global $actions;
        while (true) {
            echo BLUE . "Command > " . RESET;
            $cmd = strtolower(readline());
            if(array_key_exists($cmd, $actions))
                $actions[$cmd]();
            else
                main_error($cmd);
        }
    }
    
    main();

    Bước 6. Chạy thử ứng dụng

    Giờ nếu chạy ứng dụng từ PHP CLI bạn thu được kết quả như sau:

    Ứng dụng console PHP CLI

    Bạn thu được một ứng dụng console hoàn toàn bình thường!

    Tạm thời chúng ta chỉ thực hiện được hai lệnh help và quit. Nếu sử dụng lệnh khác sẽ gặp báo lỗi.

    Cấu trúc dữ liệu

    Mở file DataService.php và viết code như sau:

    <?php
    
    namespace DataService;
    
    require_once "Config.php";
    
    // mảng dữ liệu ban đầu
    $books = array(
        [
            'title' => 'PHP programming for dummy',
            'authors' => 'Trump D.',
            'publisher' => 'The White house',
            'year' => 2017
        ],
        [
            'title' => 'PHP programming for expert',
            'authors' => 'Obama B.',
            'publisher' => 'The White house',
            'year' => 2013
        ],
        [
            'title' => 'PHP programming for professional',
            'authors' => 'Bush G.',
            'publisher' => 'The White house',
            'year' => 2009
        ],
        [
            'title' => 'PHP programming for beginner',
            'authors' => 'Clinton B.',
            'publisher' => 'The White house',
            'year' => 2005
        ],
    );

    Đây là cấu trúc dữ liệu cho ứng dụng quản lý sách.

    Trong chương trình này, để lưu trữ dữ liệu chúng ta sử dụng một mảng kết hợp với tên biến $books. Như bạn đã biết, mảng trong PHP là một cấu trúc dữ liệu mạnh và đa năng. Chúng ta hoàn toàn có thể sử dụng mảng để biểu diễn những cấu trúc dữ liệu quan hệ phức tạp.

    Ở đây $books là một mảng của mảng.

    Mỗi phần tử của $books là một mảng kết hợp và có thể xem là dữ liệu của một cuốn sách cụ thể. Do vậy, chúng đều chứa các cặp khóa => giá trị tương ứng với thông tin về tiêu đề (title), tác giả (authors), nhà xuất bản (publisher), năm xuất bản (year).

    Với cấu trúc dữ liệu như trên bạn có thể:

    • Truy xuất từng cuốn sách qua biến $books và chỉ số như sau: $b = $books[0];.
    • Truy xuất thông tin của thể của từng cuốn sách như sau: $title = $books[0]['title'];.
    • Có thể duyệt danh sách với lệnh foreach như sau: foreach($books as $book) hoặc foreach($books as $index => $book) nếu bạn cần cả giá trị chỉ số.

    Sau đây chúng ta sẽ lần lượt thực hiện các chức năng chính của một ứng dụng quản lý.

    Hiển thị danh sách dữ liệu

    Bước 1. Thêm phương thức book_info vào DataService.php:

    /**
     * Thông tin sách
     * @param array $b
     * @return string
     */
    function book_info(array $b) {
        $title = $b['title'];
        $authors = $b['authors'];
        $publisher = $b['publisher'];
        $year = $b['year'];
        return "$title [$authors] -$publisher, $year";
    }

    Phương thức này chuyển đổi dữ liệu từ một mảng phần tử của $books (tức là dữ liệu 1 cuốn sách) về một chuỗi ký tự để dễ dàng in ra.

    Bước 2. Viết code cho file Console.php như sau:

    <?php
    
    namespace Console;
    
    require_once "DataService.php";
    
    use DataService as ds;
    
    /**
     * chức năng hiển thị danh sách
     */
    function ui_list() {
        global $books;
        echo GREEN . 'The book collection:' . RESET . RN;
        foreach ($books as $key => $b) {
            print "[$key] " . ds\book_info($b) . RN;
        }
    }
    

    Phương thức này duyệt qua mảng $books và lần lượt in ra từng phần tử. Lưu ý cách chúng ta sử dụng biệt danh (alias) của namespace DataService: use DataService as ds;. Từ giờ về sau bạn có thể gọi các hàm trong DataService qua lối viết ds\tên_hàm(), ví dụ như ds\book_info() ở trên.

    Bước 3. Cập nhật biến $actions và hàm main_help() trong BookMan.php:

    function main_help() {
        echo BLUE . 'BOOK MANAGER V1.0 By TuHocICT.com' . RESET . RN;
        echo <<<MENU
    help     Show this menu
    quit     Exit program
    list     Show all books
    
    MENU;
    
    }
    
    $actions = [
        'help' => function() { main_help();},
        'quit' => function() { exit();},
        'list' => function () { Console\ui_list(); },
    ];

    Ở bước này chúng ta bổ sung một phần tử mới cho $actions và một dòng thông báo mới cho main_help().

    Bước 4. Chạy thử với lệnh list

    Thêm dữ liệu mới

    Tiếp theo đây chúng ta sẽ xây dựng chức năng thêm dữ liệu.

    Bước 1. Thêm hai hàm sau vào file DataService.php:

    /**
     * Thêm sách vào kho
     * @param $b
     */
    function book_add($b) {
        global $books;
        array_push($books, $b);
    }
    
    /**
     * Tạo dữ liệu sách mới
     * @param string $title
     * @param string $authors
     * @param string $publisher
     * @param int $year
     * @return array
     */
    function book_create(string $title, string $authors, string $publisher, int $year) {
        return [
            'title' => $title,
            'authors' => $authors,
            'publisher' => $publisher,
            'year' => $year
        ];
    }

    Bước 2. Thêm hàm sau vào Console.php:

    /**
     * chức năng thêm sách
     */
    function ui_new() {
        $title = readline('Title: ');
        $authors = readline('Authors: ');
        $publisher = readline('Publisher: ');
        $year = (int)readline('Year: ');
        $b = ds\book_create($title, $authors, $publisher, $year);
        ds\book_add($b);
        echo BLUE . ds\book_info($b) . RESET . RN;
    }

    Bước 3. Cập nhật main_help() và $actions:

    function main_help() {
        echo BLUE . 'BOOK MANAGER V1.0 By TuHocICT.com' . RESET . RN;
        echo <<<MENU
    help     Show this menu
    quit     Exit program
    list     Show all books
    new      Add new book
    
    MENU;
    
    }
    
    $actions = [
        'help' => function() { main_help();},
        'quit' => function() { exit();},
        'list' => function () { Console\ui_list(); },
        'new' => function() {Console\ui_new();},
    ];

    * Từ giờ về sau chúng ta sẽ chỉ trình bày cập nhật cho mảng $actions. Hàm mail_help() bạn có thể tùy ý điều chỉnh.

    Bước 4. Chạy thử với lệnh new:

    Lưu trữ dữ liệu trong file JSON

    JSON là định dạng dữ liệu văn bản phổ biến hàng đầu trong ứng dụng web. Dữ liệu dạng JSON có thể dùng để lưu trữ (trong file) và trao đổi (giữa web client và server).

    Trong ví dụ này chúng ta sẽ sử dụng định dạng JSON cho việc lưu trữ dữ liệu của ứng dụng.

    PHP hỗ trợ làm việc với JSON qua hai hàm json_encode() – chuyển object thành chuỗi json, và json_decode() – chuyển chuỗi json thành object.

    Để nhanh chóng lưu dữ liệu vào file có thể sử dụng hàm file_put_contents(). Để đọc dữ liệu từ file có thể dùng hàm file_get_contents().

    Giờ hãy cùng thực thi chức năng lưu và tải dữ liệu.

    Bước 1. Thêm hai hàm sau vào DataService.php:

    /**
     * Lưu dữ liệu vào file json
     */
    function data_save() {
        global $books;
        $json = json_encode($books);
        file_put_contents(DATAFILE, $json);
    }
    
    /**
     * Tải dữ liệu từ file json
     */
    function data_load() {
        global $books;
        $json = file_get_contents(DATAFILE);
        $books = json_decode($json, true);
    }

    Bước 2. Thêm hai hàm sau vào Console.php:

    /**
     * chức năng tải dữ liệu từ file
     */
    function ui_load() {
        ds\data_load();
        echo GREEN . 'Data loaded!' . RESET . RN;
    }
    
    /**
     * chức năng lưu dữ liệu vào file
     */
    function ui_save() {
        ds\data_save();
        echo GREEN . 'Data saved!' . RESET . RN;
    }

    Bước 3. Cập nhật $actions:

    $actions = [
        'help' => function() { main_help();},
        'quit' => function() { exit();},
        'list' => function () { Console\ui_list(); },
        'new' => function() {Console\ui_new();},
        'load' => function () { Console\ui_load(); },
        'save' => function () { Console\ui_save(); },
    ];

    Bạn tự cập nhật main_help() theo ý muốn.

    Bước 4. Chạy thử ứng dụng với hai lệnh mới load và save.

    Chức năng tìm kiếm

    Bước 1. Thêm hàm book_search() vào DataService.php:

    /**
     * Tìm kiếm
     * @param string $term từ khóa cần tìm
     * @return array mảng sách tìm thấy
     */
    function book_search(string $term) {
        global $books;
        $result = array();
        foreach ($books as $key => $b) {
            if (stripos($b['title'], $term) !== false
                || stripos($b['authors'], $term) !== false
                || stripos($b['publisher'], $term) !== false) {
                array_push($result, $b);
            }
    
        }
        return $result;
    }

    Bước 2. Thêm hàm ui_search() vào Console.php

    /**
     * chức năng tìm kiếm
     */
    function ui_search() {
        $term = readline('What do you search for? ');
        $found = ds\book_search($term);
        $s = sizeof($found);
        if ($s > 0) {
            echo GREEN . "$s books found:" . RESET . RN;
            foreach ($found as $b) {
                print ds\book_info($b) . RN;
            }
        } else
            echo RED . 'No book found!' . RESET . RN;
    }

    Bước 3. Cập nhật $actions:

    $actions = [
        'help' => function() { main_help();},
        'quit' => function() { exit();},
        'list' => function () { Console\ui_list(); },
        'new' => function() {Console\ui_new();},
        'load' => function () { Console\ui_load(); },
        'save' => function () { Console\ui_save(); },
        'search' => function () { Console\ui_search(); },
    ];

    Bước 4. Chạy thử chương trình với lệnh search:

    Chức năng tìm và xóa

    Bước 1. Thêm hàm book_delete vào DataService.php

    /**
     * Tìm và xóa
     * @param string $term từ khóa cần tìm
     * @return int số bản ghi bị xóa
     */
    function book_delete(string $term) {
        global $books;
        $count = 0;
        foreach ($books as $key => $b) {
            if (stripos($b['title'], $term) !== false
                || stripos($b['authors'], $term) !== false
                || stripos($b['publisher'], $term) !== false) {
                unset($books[$key]);
                $count++;
            }
        }
        return $count;
    }

    Bước 2. Thêm hàm ui_del() vào Console.php:

    /**
     * Chức năng xóa dữ liệu
     */
    function ui_del() {
        $term = readline('What do you want to delete? ');
        $count = ds\book_delete($term);
        echo BLUE . "$count books have been removed." . RESET . RN;
    }

    Bước 3. Cập nhật $actions:

    $actions = [
        'help' => function() { main_help();},
        'quit' => function() { exit();},
        'list' => function () { Console\ui_list(); },
        'new' => function() {Console\ui_new();},
        'load' => function () { Console\ui_load(); },
        'save' => function () { Console\ui_save(); },
        'search' => function () { Console\ui_search(); },
        'del' => function () { Console\ui_del(); }
    ];

    Bước 4. Chạy thử với lệnh del:

    Mã nguồn

    Dưới đây là đầy đủ mã nguồn của chương trình để bạn tham khảo.

    <?php
    
    require_once "Config.php";
    require_once "Console.php";
    require_once "DataService.php";
    
    /**
     * Báo lỗi
     * @param string $cmd lệnh lỗi
     */
    function main_error(string $cmd) {
        echo RED . "Command '$cmd' not found! Type 'help' for supported commands." . RESET . RN;
    }
    
    /**
     * Hiển thị trợ giúp
     */
    function main_help() {
        echo BLUE . 'BOOK MANAGER V1.0 By TuHocICT.com' . RESET . RN;
        echo <<<MENU
    help     Show this menu
    quit     Exit program
    list     Show all books
    new      Add new book
    
    MENU;
    
    }
    
    $actions = [
        'help' => function() { main_help();},
        'quit' => function() { exit();},
        'list' => function () { Console\ui_list(); },
        'new' => function() {Console\ui_new();},
        'load' => function () { Console\ui_load(); },
        'save' => function () { Console\ui_save(); },
        'search' => function () { Console\ui_search(); },
        'del' => function () { Console\ui_del(); }
    ];
    
    /**
     * Entry point
     */
    function main() {
        global $actions;
        while (true) {
            echo BLUE . "Command > " . RESET;
            $cmd = strtolower(readline());
            if(array_key_exists($cmd, $actions))
                $actions[$cmd]();
            else
                main_error($cmd);
        }
    }
    
    main();
    <?php
    
    namespace Console;
    
    require_once "DataService.php";
    
    use DataService as ds;
    
    /**
     * chức năng hiển thị danh sách
     */
    function ui_list() {
        global $books;
        echo GREEN . 'The book collection:' . RESET . RN;
        foreach ($books as $key => $b) {
            print "[$key] " . ds\book_info($b) . RN;
        }
    }
    
    /**
     * chức năng thêm sách
     */
    function ui_new() {
        $title = readline('Title: ');
        $authors = readline('Authors: ');
        $publisher = readline('Publisher: ');
        $year = (int)readline('Year: ');
        $b = ds\book_create($title, $authors, $publisher, $year);
        ds\book_add($b);
        echo BLUE . ds\book_info($b) . RESET . RN;
    }
    
    /**
     * chức năng tải dữ liệu từ file
     */
    function ui_load() {
        ds\data_load();
        echo GREEN . 'Data loaded!' . RESET . RN;
    }
    
    /**
     * chức năng lưu dữ liệu vào file
     */
    function ui_save() {
        ds\data_save();
        echo GREEN . 'Data saved!' . RESET . RN;
    }
    
    /**
     * chức năng tìm kiếm
     */
    function ui_search() {
        $term = readline('What do you search for? ');
        $found = ds\book_search($term);
        $s = sizeof($found);
        if ($s > 0) {
            echo GREEN . "$s books found:" . RESET . RN;
            foreach ($found as $b) {
                print ds\book_info($b) . RN;
            }
        } else
            echo RED . 'No book found!' . RESET . RN;
    }
    
    /**
     * Chức năng xóa dữ liệu
     */
    function ui_del() {
        $term = readline('What do you want to delete? ');
        $count = ds\book_delete($term);
        echo BLUE . "$count books have been removed." . RESET . RN;
    }
    <?php
    
    namespace DataService;
    
    require_once "Config.php";
    
    // mảng dữ liệu ban đầu
    $books = array(
        [
            'title' => 'PHP programming for dummy',
            'authors' => 'Trump D.',
            'publisher' => 'The White house',
            'year' => 2017
        ],
        [
            'title' => 'PHP programming for expert',
            'authors' => 'Obama B.',
            'publisher' => 'The White house',
            'year' => 2013
        ],
        [
            'title' => 'PHP programming for professional',
            'authors' => 'Bush G.',
            'publisher' => 'The White house',
            'year' => 2009
        ],
        [
            'title' => 'PHP programming for beginner',
            'authors' => 'Clinton B.',
            'publisher' => 'The White house',
            'year' => 2005
        ],
    );
    
    /**
     * Thông tin sách
     * @param array $b
     * @return string
     */
    function book_info(array $b) {
        $title = $b['title'];
        $authors = $b['authors'];
        $publisher = $b['publisher'];
        $year = $b['year'];
        return "$title [$authors] -$publisher, $year";
    }
    
    /**
     * Thêm sách vào kho
     * @param $b
     */
    function book_add($b) {
        global $books;
        array_push($books, $b);
    }
    
    /**
     * Tạo dữ liệu sách mới
     * @param string $title
     * @param string $authors
     * @param string $publisher
     * @param int $year
     * @return array
     */
    function book_create(string $title, string $authors, string $publisher, int $year) {
        return [
            'title' => $title,
            'authors' => $authors,
            'publisher' => $publisher,
            'year' => $year
        ];
    }
    
    /**
     * Lưu dữ liệu vào file json
     */
    function data_save() {
        global $books;
        $json = json_encode($books);
        file_put_contents(DATAFILE, $json);
    }
    
    /**
     * Tải dữ liệu từ file json
     */
    function data_load() {
        global $books;
        $json = file_get_contents(DATAFILE);
        $books = json_decode($json, true);
    }
    
    /**
     * Tìm kiếm
     * @param string $term từ khóa cần tìm
     * @return array mảng sách tìm thấy
     */
    function book_search(string $term) {
        global $books;
        $result = array();
        foreach ($books as $key => $b) {
            if (stripos($b['title'], $term) !== false
                || stripos($b['authors'], $term) !== false
                || stripos($b['publisher'], $term) !== false) {
                array_push($result, $b);
            }
    
        }
        return $result;
    }
    
    /**
     * Tìm và xóa
     * @param string $term từ khóa cần tìm
     * @return int số bản ghi bị xóa
     */
    function book_delete(string $term) {
        global $books;
        $count = 0;
        foreach ($books as $key => $b) {
            if (stripos($b['title'], $term) !== false
                || stripos($b['authors'], $term) !== false
                || stripos($b['publisher'], $term) !== false) {
                unset($books[$key]);
                $count++;
            }
        }
        return $count;
    }
    <?php
    
    // Một số mã màu sử dụng trong console
    define('RED', "\e[31m");
    define('GREEN', "\e[32m");
    define('BLUE', "\e[34m");
    define('RESET', "\e[0m");
    
    // Cặp escape sequence \r\n
    define('RN', "\r\n");
    
    // Tên file dữ liệu
    define('DATAFILE', 'DATA.JSON');

    Kết luận

    Trong bài học này chúng ta đã vận dụng hầu hết các kỹ thuật lập trình PHP cơ bản đã học để xây dựng một ứng dụng console đơn giản.

    Đây cũng là bài học kết thúc của phần lập trình PHP cơ bản.

    Từ bài sau chúng ta sẽ bắt đầu học cách sử dụng PHP trong phát triển ứng dụng web.

    + Nếu bạn thấy site hữu ích, trước khi rời đi hãy giúp đỡ site bằng một hành động nhỏ để site có thể phát triển và phục vụ bạn tốt hơn.
    + Nếu bạn thấy bài viết hữu ích, hãy giúp chia sẻ tới mọi người.
    + Nếu có thắc mắc hoặc cần trao đổi thêm, mời bạn viết trong phần thảo luận cuối trang.
    Cảm ơn bạn!

    Subscribe
    Notify of
    guest
    0 Thảo luận
    Inline Feedbacks
    View all comments