PHP FFI: Callback - Part 3

Phản Hồi

Trong quá trình chuẩn bị bài viết này, tôi nhận thấy rằng nhiều tài nguyên trên internet trực tiếp sử dụng thư viện libc cho các ví dụ của họ. 
Bạn có thể dễ dàng tìm thấy những ví dụ đơn giản và trực quan. Nhưng để làm điều gì đó thú vị và độc đáo, tôi đã nghĩ "hãy tìm một cái gì đó để thể hiện với libc này". 
Dù sao đi nữa… nếu chỉ là truyền một cấu trúc như hầu hết các ví dụ, nó sẽ không hữu ích. Và sau đó, tôi tình cờ thấy một chức năng trong một diễn đàn nơi mọi người phàn nàn rằng nó được thực hiện không tốt, rằng không thể sử dụng dễ dàng, v.v. 
Vì vậy, tôi đã tiến hành nghiên cứu, và quả thực các hàm “nftw” và “ftw” có phần không tiện lợi. Chúng yêu cầu một hàm làm tham số.

Như bạn có thể thấy trên trang hướng dẫn http://manpages.ubuntu.com/manpages/bionic/en/man3/ftw.3posix.html

#include <ftw.h>

       int ftw(const char *path, int (*fn)(const char *, const struct stat *ptr, int flag), int ndirs);

Vì vậy, chúng ta có một con trỏ hàm làm tham số thứ hai. Được rồi… 
Vậy bây giờ chúng ta làm gì? FFI::new cái gì đó như thế ??
Hãy để tôi giải thích trong vài phút cách xử lý điều này.

#!/usr/bin/php8.1
<?php
    /**
     * quelques références:
     * https://www.ibm.com/docs/en/zos/2.3.0?topic=functions-nftw-nftw64-traverse-file-tree
     * http://www.sde.cs.titech.ac.jp/~gondow/dwarf2-xml/HTML-rxref/app/gcc-3.3.2/lib/gcc-lib/sparc-sun-solaris2.8/3.3.2/include/sys/types.h.html
     * http://www.doc.ic.ac.uk/~svb/oslab/Minix/usr/include/sys/types.h
     * http://manpages.ubuntu.com/manpages/trusty/fr/man2/stat.2.html
     */
opcache_reset();

$ffi = FFI::cdef(
    "
            typedef long		blkcnt_t;	
            typedef	long		time_t;
            /* Types used in disk, inode, etc. data structures. */
            typedef short          dev_t;	   /* holds (major|minor) device pair */
            typedef char           gid_t;	   /* group id */
            typedef unsigned short ino_t; 	   /* i-node number */
            typedef unsigned short mode_t;	   /* file type and permissions bits */
            typedef char         nlink_t;	   /* number of links to a file */
            typedef unsigned long  off_t;	   /* offset within a file */
            typedef int            pid_t;	   /* process id (must be signed) */
            typedef short          uid_t;	   /* user id */
            typedef unsigned long zone_t;	   /* zone number */
            typedef unsigned long block_t;	   /* block number */
            typedef unsigned long  bit_t;	   /* bit number in a bit map */
            typedef unsigned short zone1_t;	   /* zone number for V1 file systems */
            typedef unsigned short bitchunk_t; /* collection of bits in a bitmap */
            
            typedef unsigned char   u8_t;	   /* 8 bit type */
            typedef unsigned short u16_t;	   /* 16 bit type */
            typedef unsigned long  u32_t;	   /* 32 bit type */
    

    
            typedef	int		blksize_t;	/* used for block sizes */
    
            struct timespec {
                         time_t          tv_sec;
                         long            tv_nsec;
            };
            
            /** 
             * http://manpages.ubuntu.com/manpages/trusty/fr/man2/stat.2.html
             * /usr/include/x86_64-linux-gnu/sys/types.h 
             */
            struct stat {
               dev_t     st_dev;         /* Périphérique                     */
               ino_t     st_ino;         /* Numéro d’inœud                   */
               mode_t    st_mode;        /* Protection                       */
               nlink_t   st_nlink;       /* Nombre de liens physiques        */
               uid_t     st_uid;         /* UID du propriétaire              */
               gid_t     st_gid;         /* GID du propriétaire              */
               dev_t     st_rdev;        /* Type de périphérique             */
               off_t     st_size;        /* Taille totale en octets          */
               blksize_t st_blksize;     /* Taille de bloc pour E/S          */
               blkcnt_t  st_blocks;      /* Nombre de blocs de 512 o alloués */
            
               /* Depuis Linux 2.6, le noyau permet une précision à la
                  nanoseconde pour les champs temporels suivants. Pour
                  plus de précisions avant Linux 2.6, consultez les NOTES. */
            
               struct timespec st_atim;  /* Heure dernier accès              */
               struct timespec st_mtim;  /* Heure dernière modification      */
               struct timespec st_ctim;  /* Heure dernier changement état    */
            
               #define st_atime st_atim.tv_sec      /* Rétrocompatibilité        */
               #define st_mtime st_mtim.tv_sec
               #define st_ctime st_ctim.tv_sec
            };

            typedef struct FTW{
                int base;
                int level;
            };

            typedef int (*fn)(const char *, const struct stat *, int, struct FTW *);
            int nftw(const char *path, int (*fn)(const char *, const struct stat *, int, struct FTW *),  int fd_limit, int flags);
            int ftw(const char *path, int (*fn)(const char *, const struct stat *, int, struct FTW *),  int fd_limit);
        ",
    "libc.so.6"
);

$values = [];
$fn = function ($fpath, $stat, $tflag, $ftwbuf) {
    global $values;

    /**
     * https://sites.uclouvain.be/SystInfo/usr/include/ftw.h.html
     * FTW_CONTINUE = 0,     Continue with next sibling or for FTW_D with the  first child.
     * FTW_STOP = 1,         Return from `ftw' or `nftw' with FTW_STOP as return value.
     * FTW_SKIP_SUBTREE = 2  Only meaningful for FTW_D: Don't walk through the  subtree, instead just continue with its next sibling.
     * FTW_SKIP_SIBLINGS = 3 Continue with FTW_DP callback for current directory (if FTW_DEPTH) and then its siblings.
     */
    $values[] = sprintf(
        "level: %s, Path: %-40s",
        $ftwbuf->level,
        $fpath
    );
    return 0;
};

$ffi->nftw('/var/lib', $fn, 1, 0);
print_r(array_slice($values, 0, 5));
echo PHP_EOL;


$ffi->ftw('/var/lib', $fn, 1);
print_r(array_slice($values, 0, 5));
echo PHP_EOL;

Tôi trình bày mã nguồn như nó vốn có, với các nguồn tham khảo giúp tôi thiết lập ví dụ. Nói ngắn gọn,  tôi bắt đầu từ trang hướng dẫn, nơi cung cấp thông tin đầu tiên cho tôi. Chúng ta có thể bắt đầu bằng cách xác định định nghĩa với 2 hàm.


int nftw(const char *path, int (*fn)(const char *, const struct stat *, int, struct FTW *),  int fd_limit, int flags);
int ftw(const char *path, int (*fn)(const char *, const struct stat *, int, struct FTW *),  int fd_limit);

Chúng ta nhanh chóng nhận ra rằng chúng ta có một con trỏ hàm nên chúng ta thêm nó vào định nghĩa của mình:


typedef int (*fn)(const char *, const struct stat *, int, struct FTW *);

Tại đây chúng ta nhận thấy rằng cần phải xác định 2 cấu trúc : FTW nhưng cũng là stat

typedef struct FTW{
                int base;
                int level;
};
            struct stat {
               dev_t     st_dev;         /* Périphérique                     */
               ino_t     st_ino;         /* Numéro d’inœud                   */
               mode_t    st_mode;        /* Protection                       */
               nlink_t   st_nlink;       /* Nombre de liens physiques        */
               uid_t     st_uid;         /* UID du propriétaire              */
               gid_t     st_gid;         /* GID du propriétaire              */
               dev_t     st_rdev;        /* Type de périphérique             */
               off_t     st_size;        /* Taille totale en octets          */
               blksize_t st_blksize;     /* Taille de bloc pour E/S          */
               blkcnt_t  st_blocks;      /* Nombre de blocs de 512 o alloués */
            
               /* Depuis Linux 2.6, le noyau permet une précision à la
                  nanoseconde pour les champs temporels suivants. Pour
                  plus de précisions avant Linux 2.6, consultez les NOTES. */
            
               struct timespec st_atim;  /* Heure dernier accès              */
               struct timespec st_mtim;  /* Heure dernière modification      */
               struct timespec st_ctim;  /* Heure dernier changement état    */
            
               #define st_atime st_atim.tv_sec      /* Rétrocompatibilité        */
               #define st_mtime st_mtim.tv_sec
               #define st_ctime st_ctim.tv_sec
            };

            typedef struct FTW{
                int base;
                int level;
            };

Một tìm kiếm nhanh chóng trên internet và chúng ta dễ dàng tìm thấy nơi để sao chép và dán thông tin :

http://manpages.ubuntu.com/manpages/trusty/fr/man2/stat.2.html nhưng đặc biệt là /usr/include/x86_64-linux-gnu/sys/types.h.

Thuận tiện, tôi đã có sẵn thông tin 🙂 Chúng ta tiếp tục khám phá và thêm tất cả các loại. Việc tìm kiếm chúng hơi khó khăn…

http://www.sde.cs.titech.ac.jp/~gondow/dwarf2-xml/HTML-rxref/app/gcc-3.3.2/lib/gcc-lib/sparc-sun-solaris2.8/3.3.2/include/sys/types.h.html 
http://www.doc.ic.ac.uk/~svb/oslab/Minix/usr/include/sys/types.h

Đó, định nghĩa của chúng ta đã hoàn tất! Cuối cùng, chúng ta đến với chức năng tuyệt vời mà tôi đang cố gắng thể hiện ở đây: Phản Hồi!
Vâng, bạn nghe đúng rồi. 
Vì chúng ta không có giới hạn, chúng ta sẽ trực tiếp truyền một hàm PHP ẩn danh vào hàm ftw của chúng ta... wow wow WOOOOW  WOOOOW!!!

Tôi đang hứng thú, nhưng thực sự rất tuyệt vời, hãy nhìn vào mã nguồn :

$values = [];
$fn = function ($fpath, $stat, $tflag, $ftwbuf) {
    global $values;

    /**
     * https://sites.uclouvain.be/SystInfo/usr/include/ftw.h.html
     * FTW_CONTINUE = 0,     Continue with next sibling or for FTW_D with the  first child.
     * FTW_STOP = 1,         Return from `ftw' or `nftw' with FTW_STOP as return value.
     * FTW_SKIP_SUBTREE = 2  Only meaningful for FTW_D: Don't walk through the  subtree, instead just continue with its next sibling.
     * FTW_SKIP_SIBLINGS = 3 Continue with FTW_DP callback for current directory (if FTW_DEPTH) and then its siblings.
     */
    $values[] = sprintf(
        "level: %s, Path: %-40s",
        $ftwbuf->level,
        $fpath
    );
    return 0;
};

$ffi->nftw('/var/lib', $fn, 1, 0);

Được rồi, tôi không nghĩ rằng mình sẽ viết lại một biến toàn cục trong đời, lần cuối cùng có lẽ là 15 năm trước, và thậm chí lúc đó… Nhưng vì mục đích của bản trình diễn, thôi thì , YOLO (tùy bạn nghĩ ra một từ viết tắt khác…). 
Như bạn thấy, không cần phải truyền FFI::addr, hàm ẩn danh đơn độc đã đủ.

Kết quả :

./nftw.php 
Array
(
    [0] => level: 0, Path: /var/lib                                
    [1] => level: 1, Path: /var/lib/php                            
    [2] => level: 2, Path: /var/lib/php/sessions                   
    [3] => level: 2, Path: /var/lib/php/modules                    
    [4] => level: 3, Path: /var/lib/php/modules/7.4                
)

Array
(
    [0] => level: 0, Path: /var/lib                                
    [1] => level: 1, Path: /var/lib/php                            
    [2] => level: 2, Path: /var/lib/php/sessions                   
    [3] => level: 2, Path: /var/lib/php/modules                    
    [4] => level: 3, Path: /var/lib/php/modules/7.4                
)

Tài liệu hướng dẫn cũng cảnh báo chúng ta về phản hồi. Không nên lạm dụng chúng.

Trong phần tiếp theo của tệp, chúng ta sẽ đề cập đến cách chơi với tính tương tác giữa các ngôn ngữ được thiết kế cho di động. KMP (Kotlin Multiplatform).

PHP FFI: sử dụng thư viện Kotlin Multiplatform - phần 4

Cảm ơn Thomas Bourdin, Cédric Le Jallé, Stéphane Péchard vì đã giúp đỡ, tư vấn và đọc lại.