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.