国产av日韩一区二区三区精品,成人性爱视频在线观看,国产,欧美,日韩,一区,www.成色av久久成人,2222eeee成人天堂

Maison développement back-end tutoriel php [Fran?ais] [développement d'extensions php et embarquées] Chapitre 15 - Implémentation des flux en php

[Fran?ais] [développement d'extensions php et embarquées] Chapitre 15 - Implémentation des flux en php

Feb 10, 2017 am 10:21 AM
php


Implémentation de flux

L'une des fonctionnalités les plus puissantes des flux PHP est qu'il peut accéder à de nombreux Sources de données?: fichiers ordinaires, fichiers compressés, canaux réseau transparents, réseaux cryptés, canaux nommés et sockets de domaine, qui sont des API unifiées pour l'espace utilisateur et en interne.

Sous la surface de flux PHP

Pour une instance de flux donnée, telle qu'un flux de fichier et un flux réseau, la différence est que la fonction de création de flux que vous avez utilisée dans le chapitre précédent renvoie Le membre ops dans la structure php_stream.

typedef struct _php_stream {
    ...
    php_stream_ops *ops;
    ...
} php_stream;

La structure php_stream_ops définit une collection de pointeurs de fonction et une balise de description.

typedef struct _php_stream_ops {
    size_t (*write)(php_stream *stream, const char *buf,
                            size_t count TSRMLS_DC);
    size_t (*read)(php_stream *stream, char *buf,
                            size_t count TSRMLS_DC);
    int    (*close)(php_stream *stream, int close_handle
                            TSRMLS_DC);
    int    (*flush)(php_stream *stream TSRMLS_DC);

    const char *label;

    int (*seek)(php_stream *stream, off_t offset, int whence,
                            off_t *newoffset TSRMLS_DC);
    int (*cast)(php_stream *stream, int castas, void **ret
                            TSRMLS_DC);
    int (*stat)(php_stream *stream, php_stream_statbuf *ssb
                            TSRMLS_DC);
    int (*set_option)(php_stream *stream, int option,int value,
                            void *ptrparam TSRMLS_DC);
} php_stream_ops;

Lorsqu'une fonction d'accès au flux telle que php_stream_read() est appelée, la couche d'empaquetage du flux résout et appelle la fonction correspondante dans la fonction stream->ops , de sorte que ce qui est réellement appelé est l'implémentation de lecture spécifique au type de flux actuel. Par exemple, la fonction de lecture dans la structure stream ops d'un fichier ordinaire est implémentée comme suit (l'implémentation réelle est un peu plus compliquée que l'exemple ci-dessous. ):

size_t php_stdio_read(php_stream *stream, char *buf,
                                size_t count TSRMLS_DC)
{
    php_stdio_stream_data *data =
                (php_stdio_stream_data*)stream->abstract;
    return read(data->fd, buf, count);
}

Dans la structure ops utilisée par le flux compress.zlib, read pointe vers la fonction suivante?:

size_t php_zlib_read(php_stream *stream, char *buf,
                                size_t count TSRMLS_DC)
{
    struct php_gz_stream_data_t *data =
            (struct php_gz_stream_data_t *) stream->abstract;

    return gzread(data->gz_file, buf, count);
}

La première chose à noter ici est que le pointeur de fonction pointait vers par la structure ops est souvent la source de données. Un proxy léger pour la fonction de lecture réelle. Dans les deux exemples ci-dessus, le flux d'E/S standard utilise la fonction read() de posix, tandis que le flux zlib utilise la fonction gzread() de libz.

Vous remarquerez peut-être également que l'élément stream->abstract est utilisé ici. Il s'agit d'un pointeur pratique vers l'implémentation du flux, qui peut être utilisé pour obtenir diverses informations sur le bundle associées dans ce qui précède. Par exemple, pointez sur Un pointeur vers une structure personnalisée utilisée pour stocker le descripteur de fichier à utiliser par la fonction de lecture sous-jacente

Une autre chose que vous remarquerez peut-être est que chaque fonction de la structure php_stream_ops. Les deux attendent une instance de flux existante, mais comment en obtenir une ? Comment les membres abstraits sont-ils définis et quand la directive stream utilise-t-elle quelle structure opérationnelle ? La réponse réside dans la première fonction que vous avez utilisée pour ouvrir un flux dans le chapitre précédent ( php_stream_open_wrapper()).

Lorsque cette fonction est appelée, la couche wrapper de flux de PHP tente de déterminer quel protocole est demandé en fonction de la partie schéma:// de l'URL transmise de cette fa?on. il peut trouver l'élément php_stream_wrapper correspondant dans le wrapper php enregistré. Chaque structure php_stream_wrapper peut obtenir son propre élément ops, qui pointe vers une structure php_stream_wrapper_ops?:

typedef struct _php_stream_wrapper_ops {
    php_stream *(*stream_opener)(php_stream_wrapper *wrapper,
                        char *filename, char *mode,
                        int options, char **opened_path,
                        php_stream_context *context
                        STREAMS_DC TSRMLS_DC);
    int (*stream_closer)(php_stream_wrapper *wrapper,
                        php_stream *stream TSRMLS_DC);
    int (*stream_stat)(php_stream_wrapper *wrapper,
                        php_stream *stream,
                        php_stream_statbuf *ssb
                        TSRMLS_DC);
    int (*url_stat)(php_stream_wrapper *wrapper,
                        char *url, int flags,
                        php_stream_statbuf *ssb,
                        php_stream_context *context
                        TSRMLS_DC);
    php_stream *(*dir_opener)(php_stream_wrapper *wrapper,
                        char *filename, char *mode,
                        int options, char **opened_path,
                        php_stream_context *context
                        STREAMS_DC TSRMLS_DC);

    const char *label;

    int (*unlink)(php_stream_wrapper *wrapper, char *url,
                        int options,
                        php_stream_context *context
                        TSRMLS_DC);

    int (*rename)(php_stream_wrapper *wrapper,
                        char *url_from, char *url_to,
                        int options,
                        php_stream_context *context
                        TSRMLS_DC);


    int (*stream_mkdir)(php_stream_wrapper *wrapper,
                        char *url, int mode, int options,
                        php_stream_context *context
                        TSRMLS_DC);
    int (*stream_rmdir)(php_stream_wrapper *wrapper, char *url,
                        int options,
                        php_stream_context *context
                        TSRMLS_DC);
} php_stream_wrapper_ops;

Ici, l'empaquetage du flux. la couche appelle wrapper->ops->stream_opener(), qui effectuera des opérations spécifiques au wrapper pour créer une instance de flux, attribuer la structure php_stream_ops appropriée et lier les données abstraites associées.

La fonction dir_opener() fournit le même service de base que stream_opener(); cependant, c'est une réponse à l'appel d'API php_stream_opendir() et lie généralement une structure php_stream_ops différente à l'instance renvoyée stat() et close(). les fonctions sont dupliquées à ce niveau. Ceci est fait pour ajouter une logique spécifique au protocole à ces opérations du wrapper.

Autres Les fonctions vous permettent d'effectuer des opérations de flux statiques sans réellement créer de flux. Par exemple. En regardant ces appels d'API de flux, ils ne renvoient pas réellement d'objet php_stream, comme vous le verrez en détail sous peu

.

Bien que url_stat existait en interne en tant que fonction d'opérations wrapper lorsque la couche flow wrapper a été introduite dans PHP 4.3, elle n'a pas été utilisée avant PHP 5.0. De plus, les 3 dernières fonctions, rename(), stream_mkdir(. ) et stream_rmdir() n'ont été introduits qu'avec PHP 5.0. Avant cette version, ils n'étaient pas dans la structure opérationnelle du wrapper.

Opération Wrapper.

à l'exception de la fonction url_stat(), chaque opération précédant l'élément const char *label dans l'opération wrapper peut être utilisée sur l'instance de flux activée. La signification de chaque fonction est la suivante?:

stream_opener() instancie une instance de flux Lorsqu'une fonction fopen() de l'espace utilisateur est appelée, cette fonction Le

numéro sera appelé. L'instance php_stream renvoyée par cette fonction est celle renvoyée par la fonction fopen()

Représentation interne. de gestion des ressources de fichiers. Fonctions intégrées telles que file(), file_get_contents(),

file_put_contents(), readfile(), etc., lors de la demande de ressources d'empaquetage. , utilisez ce package

loader ops.

stream_closer() Cette fonction est appelée lorsqu'une instance de flux termine son cycle de vie. Toutes les ressources allouées par stream_opener()

doivent être appelées dans cette fonction Release.

stream_stat()Semblable à la fonction fstat() dans l'espace utilisateur, cette fonction devrait remplir la structure ssb (en fait

ne contient qu'un seul membre de la structure struct statbuf sb),

dir_opener() se comporte de la même manière que stream_opener(), Cependant, il est appelé lors de l'appel de l'espace utilisateur

fonction de la famille opendir(). L'implémentation du flux sous-jacent utilisée par le flux de répertoire suit les mêmes règles que le flux de fichiers

.

Cependant, un flux de répertoire n'a besoin que de renvoyer un enregistrement contenant le nom du fichier trouvé dans le répertoire ouvert, il

La taille de est la taille de la structure struct dirent.

Opération de wrapper statique

url_stat()stat() est utilisée pour renvoyer les métadonnées du fichier, telles que l'autorisation d'accès, taille, type?; et

accès, modification, heure de création Bien que cette fonction soit apparue lorsque PHP 4.3 a introduit la couche d'empaquetage de flux

在php_stream_wrapper_ops結(jié)構(gòu)體中的, 但直到php 5.0才被用戶空

間的stat()函數(shù)使用.

unlink()和posix文件系統(tǒng)的同名函數(shù)語義相同, 它執(zhí)行文件刪除. 如果對于當(dāng)

前的包裝器刪除沒有意義, 比如內(nèi)建的http://包裝器, 這個函數(shù)應(yīng)該被

定義為NULL, 以便內(nèi)核去引發(fā)適當(dāng)?shù)腻e誤消息.

rename()當(dāng)用戶空間的rename()函數(shù)的參數(shù)$from和$to參數(shù)指向的是相同的

底層包裝器實現(xiàn), php則將這個重命名請求分發(fā)到包裝器的rename函

數(shù).

mkdir() & rmdir()這兩個函數(shù)直接映射到對應(yīng)的用戶空間函數(shù).

實現(xiàn)一個包裝器

為了演示包裝器和流操作的內(nèi)部工作原理, 我們需要重新實現(xiàn)php手冊的stream_wrapper_register()一頁示例中的var://包裝器.

此刻, 首先從下面功能完整的變量流包裝實現(xiàn)開始. 構(gòu)建他, 并開始檢查每一塊的工作原理.

譯注: 為了方便大家閱讀, 對代碼的注釋進(jìn)行了適量補充調(diào)整, 此外, 由于phpapi的調(diào)整, 原著中的代碼不能直接在譯者使用的php-5.4.10中運行, 進(jìn)行了適當(dāng)?shù)男薷? 因此下面代碼結(jié)構(gòu)可能和原著略有不同, 請參考閱讀.(下面opendir的例子也進(jìn)行了相應(yīng)的修改)

config.m4

PHP_ARG_ENABLE(varstream,whether to enable varstream support,
[  enable-varstream      Enable varstream support])

if test "$PHP_VARSTREAM" = "yes"; then
  AC_DEFINE(HAVE_VARSTREAM,1,[Whether you want varstream])
  PHP_NEW_EXTENSION(varstream, varstream.c, $ext_shared)
fi

php_varstream.h

#ifndef PHP_VARSTREAM_H
#define PHP_VARSTREAM_H

extern zend_module_entry varstream_module_entry;
#define phpext_varstream_ptr &varstream_module_entry

#ifdef PHP_WIN32
#   define PHP_VARSTREAM_API __declspec(dllexport)
#elif defined(__GNUC__) && __GNUC__ >= 4
#   define PHP_VARSTREAM_API __attribute__ ((visibility("default")))
#else
#   define PHP_VARSTREAM_API
#endif

#ifdef ZTS
#include "TSRM.h"
#endif

PHP_MINIT_FUNCTION(varstream);
PHP_MSHUTDOWN_FUNCTION(varstream);

#define PHP_VARSTREAM_WRAPPER       "var"
#define PHP_VARSTREAM_STREAMTYPE    "varstream"

/* 變量流的抽象數(shù)據(jù)結(jié)構(gòu) */
typedef struct _php_varstream_data {
    off_t   position;
    char    *varname;
    int     varname_len;
} php_varstream_data;

#ifdef ZTS
#define VARSTREAM_G(v) TSRMG(varstream_globals_id, zend_varstream_globals *, v)
#else
#define VARSTREAM_G(v) (varstream_globals.v)
#endif

#endif

varstream.c

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "php.h"
#include "php_ini.h"
#include "ext/standard/info.h"
#include "ext/standard/url.h"
#include "php_varstream.h"

static size_t php_varstream_write(php_stream *stream,
                const char *buf, size_t count TSRMLS_DC)
{
    php_varstream_data *data = stream->abstract;
    zval **var;
    size_t newlen;

    /* 查找變量 */
    if (zend_hash_find(&EG(symbol_table), data->varname,
            data->varname_len + 1,(void**)&var) == FAILURE) {
        /* 變量不存在, 直接創(chuàng)建一個字符串類型的變量, 并保存新傳遞進(jìn)來的內(nèi)容 */
       zval *newval;
       MAKE_STD_ZVAL(newval);
       ZVAL_STRINGL(newval, buf, count, 1); 
       /* 將新的zval *放到變量中 */
       zend_hash_add(&EG(symbol_table), data->varname,
           data->varname_len + 1, (void*)&newval,
           sizeof(zval*), NULL);
       return count;
    }   
    /* 如果需要, 讓變量可寫. 這里實際上處理的是寫時復(fù)制 */
    SEPARATE_ZVAL_IF_NOT_REF(var);
    /* 轉(zhuǎn)換為字符串類型 */
    convert_to_string_ex(var);
    /* 重置偏移量(譯注: 相比于正常的文件系統(tǒng), 這里的處理實際上不支持文件末尾的空洞創(chuàng)建, 讀者如果熟悉*nix文件系統(tǒng), 應(yīng)該了解譯者所說, 否則請略過) */
    if (data->position > Z_STRLEN_PP(var)) {
        data->position = Z_STRLEN_PP(var);
    }   
    /* 計算新的字符串長度 */
    newlen = data->position + count;
    if (newlen < Z_STRLEN_PP(var)) {
        /* 總長度不變 */
        newlen = Z_STRLEN_PP(var);
    } else if (newlen > Z_STRLEN_PP(var)) {
        /* 重新調(diào)整緩沖區(qū)大小以保存新內(nèi)容 */
        Z_STRVAL_PP(var) =erealloc(Z_STRVAL_PP(var),newlen+1);
        /* 更新字符串長度 */
        Z_STRLEN_PP(var) = newlen;
        /* 確保字符串NULL終止 */
        Z_STRVAL_PP(var)[newlen] = 0;
    }   
    /* 將數(shù)據(jù)寫入到變量中 */
    memcpy(Z_STRVAL_PP(var) + data->position, buf, count);
    data->position += count;

    return count;
}

static size_t php_varstream_read(php_stream *stream,
                char *buf, size_t count TSRMLS_DC)
{
    php_varstream_data *data = stream->abstract;
    zval **var, copyval;
    int got_copied = 0;
    size_t toread = count;

    if (zend_hash_find(&EG(symbol_table), data->varname,
        data->varname_len + 1, (void**)&var) == FAILURE) {
        /* 變量不存在, 讀不到數(shù)據(jù), 返回0字節(jié)長度 */
        return 0;
    }   
    copyval = **var;
    if (Z_TYPE(copyval) != IS_STRING) {
        /* 對于非字符串類型變量, 創(chuàng)建一個副本進(jìn)行讀, 這樣對于只讀的變量, 就不會改變其原始類型 */
        zval_copy_ctor(?val);
        INIT_PZVAL(?val);
        got_copied = 1;
    }   
    if (data->position > Z_STRLEN(copyval)) {
        data->position = Z_STRLEN(copyval);
    }   
    if ((Z_STRLEN(copyval) - data->position) < toread) {
        /* 防止讀取到變量可用緩沖區(qū)外的內(nèi)容 */
        toread = Z_STRLEN(copyval) - data->position;
    }   
    /* 設(shè)置緩沖區(qū) */
    memcpy(buf, Z_STRVAL(copyval) + data->position, toread);
    data->position += toread;

    /* 如果創(chuàng)建了副本, 則釋放副本 */
    if (got_copied) {
        zval_dtor(?val);
    }   

    /* 返回設(shè)置到緩沖區(qū)的字節(jié)數(shù) */
    return toread;
}

static int php_varstream_closer(php_stream *stream,
                            int close_handle TSRMLS_DC)
{
    php_varstream_data *data = stream->abstract;

    /* 釋放內(nèi)部結(jié)構(gòu)避免泄露 */
    efree(data->varname);
    efree(data);

    return 0;
}

static int php_varstream_flush(php_stream *stream TSRMLS_DC)
{
    php_varstream_data *data = stream->abstract;
    zval **var;

    /* 根據(jù)不同情況, 重置偏移量 */
    if (zend_hash_find(&EG(symbol_table), data->varname,
                    data->varname_len + 1, (void**)&var)
                    == SUCCESS) {
        if (Z_TYPE_PP(var) == IS_STRING) {
            data->position = Z_STRLEN_PP(var);
        } else {
            zval copyval = **var;
            zval_copy_ctor(?val);
            convert_to_string(?val);
            data->position = Z_STRLEN(copyval);
            zval_dtor(?val);
        }
    } else {
        data->position = 0;
    }

    return 0;
}

static int php_varstream_seek(php_stream *stream, off_t offset,
                    int whence, off_t *newoffset TSRMLS_DC)
{
    php_varstream_data *data = stream->abstract;

    switch (whence) {
        case SEEK_SET:
            data->position = offset;
            break;
        case SEEK_CUR:
            data->position += offset;
            break;
        case SEEK_END:
        {
            zval **var;
           size_t curlen = 0;

           if (zend_hash_find(&EG(symbol_table),
                   data->varname,    data->varname_len + 1,
                   (void**)&var) == SUCCESS) {
              if (Z_TYPE_PP(var) == IS_STRING) {
                  curlen = Z_STRLEN_PP(var);
              } else {
                  zval copyval = **var;
                  zval_copy_ctor(?val);
                  convert_to_string(?val);
                  curlen = Z_STRLEN(copyval);
                  zval_dtor(?val);
              }
           }

           data->position = curlen + offset;
           break;
       }
    }

    /* 防止隨機訪問指針移動到緩沖區(qū)開始位置之前 */
    if (data->position < 0) {
        data->position = 0;
    }

    if (newoffset) {
        *newoffset = data->position;
    }

    return 0;
}

static php_stream_ops php_varstream_ops = {
    php_varstream_write,
    php_varstream_read,
    php_varstream_closer,
    php_varstream_flush,
    PHP_VARSTREAM_STREAMTYPE,
    php_varstream_seek,
    NULL, /* cast */
    NULL, /* stat */
    NULL, /* set_option */
};

/* Define the wrapper operations */
static php_stream *php_varstream_opener(
            php_stream_wrapper *wrapper,
            char *filename, char *mode, int options,
            char **opened_path, php_stream_context *context
            STREAMS_DC TSRMLS_DC)
{
    php_varstream_data *data;
    php_url *url;

    if (options & STREAM_OPEN_PERSISTENT) {
        /* 按照變量流的定義, 是不能持久化的
         * 因為變量在請求結(jié)束后將被釋放
         */
        php_stream_wrapper_log_error(wrapper, options
            TSRMLS_CC, "Unable to open %s persistently",
                                        filename);
        return NULL;
    }

    /* 標(biāo)準(zhǔn)URL解析: scheme://user:pass@host:port/path?query#fragment */
    url = php_url_parse(filename);
    if (!url) {
        php_stream_wrapper_log_error(wrapper, options
            TSRMLS_CC, "Unexpected error parsing URL");
        return NULL;
    }
    /* 檢查是否有變量流URL必須的元素host, 以及scheme是否是var */
    if (!url->host || (url->host[0] == 0) ||
        strcasecmp("var", url->scheme) != 0) {
        /* Bad URL or wrong wrapper */
        php_stream_wrapper_log_error(wrapper, options
            TSRMLS_CC, "Invalid URL, must be in the form: "
                     "var://variablename");
        php_url_free(url);
        return NULL;
    }

    /* 創(chuàng)建一個數(shù)據(jù)結(jié)構(gòu)保存協(xié)議信息(變量流協(xié)議重要是變量名, 變量名長度, 當(dāng)前偏移量) */
    data = emalloc(sizeof(php_varstream_data));
    data->position = 0;
    data->varname_len = strlen(url->host);
    data->varname = estrndup(url->host, data->varname_len + 1);
    /* 釋放前面解析出來的url占用的內(nèi)存 */
    php_url_free(url);

    /* 實例化一個流, 為其賦予恰當(dāng)?shù)牧鱫ps, 綁定抽象數(shù)據(jù) */
    return php_stream_alloc(&php_varstream_ops, data, 0, mode);
}

static php_stream_wrapper_ops php_varstream_wrapper_ops = {
    php_varstream_opener, /* 調(diào)用php_stream_open_wrapper(sprintf("%s://xxx", PHP_VARSTREAM_WRAPPER))時執(zhí)行 */
    NULL, /* stream_close */
    NULL, /* stream_stat */
    NULL, /* url_stat */
    NULL, /* dir_opener */
    PHP_VARSTREAM_WRAPPER,
    NULL, /* unlink */
#if PHP_MAJOR_VERSION >= 5
    /* PHP >= 5.0 only */
    NULL, /* rename */
    NULL, /* mkdir */
    NULL, /* rmdir */
#endif
};

static php_stream_wrapper php_varstream_wrapper = {
    &php_varstream_wrapper_ops,
    NULL, /* abstract */
    0, /* is_url */
};

PHP_MINIT_FUNCTION(varstream)
{
    /* 注冊流包裝器:
     * 1. 檢查流包裝器名字是否正確(符合這個正則: /^[a-zA-Z0-9+.-]+$/)
     * 2. 將傳入的php_varstream_wrapper增加到url_stream_wrappers_hash這個HashTable中, key為PHP_VARSTREAM_WRAPPER
     */
    if (php_register_url_stream_wrapper(PHP_VARSTREAM_WRAPPER,
            &php_varstream_wrapper TSRMLS_CC)==FAILURE) {
        return FAILURE;
    }
    return SUCCESS;
}

PHP_MSHUTDOWN_FUNCTION(varstream)
{
    /* 卸載流包裝器: 從url_stream_wrappers_hash中刪除 */
    if (php_unregister_url_stream_wrapper(PHP_VARSTREAM_WRAPPER
                                TSRMLS_CC) == FAILURE) {
        return FAILURE;
    }
    return SUCCESS;
}

zend_module_entry varstream_module_entry = {
#if ZEND_MODULE_API_NO >= 20010901
    STANDARD_MODULE_HEADER,
#endif
    "varstream",
    NULL,
    PHP_MINIT(varstream),
    PHP_MSHUTDOWN(varstream),
    NULL,
    NULL,
    NULL,
#if ZEND_MODULE_API_NO >= 20010901
    "0.1",
#endif
    STANDARD_MODULE_PROPERTIES
};

#ifdef COMPILE_DL_VARSTREAM
ZEND_GET_MODULE(varstream)
#endif

在構(gòu)建加載擴展后, php就可以處理以var://開始的URL的請求, 它的行為和手冊中用戶空間實現(xiàn)的行為一致.

內(nèi)部實現(xiàn)

首先你注意到的可能是這個擴展完全沒有暴露用戶空間函數(shù). 它所做的只是在MINIT函數(shù)中調(diào)用了一個核心PHPAPI的鉤子, 將var協(xié)議和我們定義的包裝器關(guān)聯(lián)起來:

static php_stream_wrapper php_varstream_wrapper = {
    &php_varstream_wrapper_ops,
    NULL, /* abstract */
    0, /* is_url */
}

很明顯, 最重要的元素就是ops, 它提供了訪問特定流包裝器的創(chuàng)建以及檢查函數(shù). 你可以安全的忽略abstract屬性, 它僅在運行時使用, 在初始化定義時, 它只是作為一個占位符. 第三個元素is_url, 它告訴php在使用這個包裝器時是否考慮php.ini中的allow_url_fopen選項. 如果這個值非0, 并且將allow_url_fopen設(shè)置為false, 則這個包裝器不能被腳本使用.

在本章前面你已經(jīng)知道, 調(diào)用用戶空間函數(shù)比如fopen將通過這個包裝器的ops元素得到php_varstream_wrapper_ops, 這樣去調(diào)用流的打開函數(shù)php_varstream_opener.

這個函數(shù)的第一塊代碼檢查是否請求持久化的流:

if (options & STREAM_OPEN_PERSISTENT) {

對于很多包裝器這樣的請求是合法的. 然而目前的情況這個行為沒有意義. 一方面用戶空間變量的定義就是臨時的, 另一方面, varstream的實例化代價很低, 這就使得持久化的優(yōu)勢很小.

像流包裝層報告錯誤很簡單, 只需要返回一個NULL值而不是流實例即可. 流包裝層透出到用戶空間的失敗消息并不會說明具體的錯誤, 只是說明不能打開URL. 要想給開發(fā)者暴露更多的錯誤信息, 可以在返回之前使用php_stream_wrapper_log_error()函數(shù).

php_stream_wrapper_log_error(wrapper, options
    TSRMLS_CC, "Unable to open %s persistently",
                                filename);
return NULL;

URL解析

實例化varstream的下一步需要一個人類可讀的URL, 將它分塊放入到一個易管理的結(jié)構(gòu)體中. 幸運的是它使用了和用戶空間url_parse()函數(shù)相同的機制. 如果URL成功解析, 將會分配一個php_url結(jié)構(gòu)體并設(shè)置合適的值. 如果在URL中沒有某些值, 在返回的php_url中對應(yīng)的將被設(shè)置為NULL. 這個結(jié)構(gòu)體必須在離開php_varstream_opener函數(shù)之前被顯式釋放, 否則它的內(nèi)存將會泄露:

typedef struct php_url {
    /* scheme://user:pass@host:port/path?query#fragment */
    char *scheme;
    char *user;
    char *pass;
    char *host;
    unsigned short port;
    char *path;
    char *query;
    char *fragment;
} php_url;

最后, varstream包裝器創(chuàng)建了一個數(shù)據(jù)結(jié)構(gòu), 保存了流指向的變量名, 讀取時的當(dāng)前位置. 這個結(jié)構(gòu)體將在流的讀取和寫入函數(shù)中用于獲取變量, 并且將在流結(jié)束使用時由php_varstream_close函數(shù)釋放.

opendir()

讀寫變量內(nèi)容的實現(xiàn)可以再次進(jìn)行擴展. 這里可以加入一個新的特性, 允許使用目錄函數(shù)讀取數(shù)組中的key. 在你的php_varstream_wrapper_ops結(jié)構(gòu)體之前增加下面的代碼:

static size_t php_varstream_readdir(php_stream *stream,
                char *buf, size_t count TSRMLS_DC)
{
    php_stream_dirent *ent = (php_stream_dirent*)buf;
    php_varstream_dirdata *data = stream->abstract;
    char *key;
    int type, key_len;
    long idx;

    /* 查找數(shù)組中的key */
    type = zend_hash_get_current_key_ex(Z_ARRVAL_P(data->arr),
                    &key, &key_len, &idx, 0, &(data->pos));

    /* 字符串key */
    if (type == HASH_KEY_IS_STRING) {
        if (key_len >= sizeof(ent->d_name)) {
            /* truncate long keys to maximum length */
            key_len = sizeof(ent->d_name) - 1;
        }
        /* 設(shè)置到目錄結(jié)構(gòu)上 */
        memcpy(ent->d_name, key, key_len);
        ent->d_name[key_len] = 0;
    /* 數(shù)值key */
    } else if (type == HASH_KEY_IS_LONG) {
        /* 設(shè)置到目錄結(jié)構(gòu)上 */
        snprintf(ent->d_name, sizeof(ent->d_name), "%ld",idx);
    } else {
        /* 迭代結(jié)束 */
        return 0;
    }
    /* 移動數(shù)組指針(位置記錄到流的抽象結(jié)構(gòu)中) */
    zend_hash_move_forward_ex(Z_ARRVAL_P(data->arr),
                                        &data->pos);
    return sizeof(php_stream_dirent);
}

static int php_varstream_closedir(php_stream *stream,
                            int close_handle TSRMLS_DC)
{
    php_varstream_dirdata *data = stream->abstract;

    zval_ptr_dtor(&(data->arr));
    efree(data);
    return 0;
}

static int php_varstream_dirseek(php_stream *stream,
                    off_t offset, int whence,
                    off_t *newoffset TSRMLS_DC)
{
    php_varstream_dirdata *data = stream->abstract;

    if (whence == SEEK_SET && offset == 0) {
        /* 重置數(shù)組指針 */
        zend_hash_internal_pointer_reset_ex(
                    Z_ARRVAL_P(data->arr), &(data->pos));
        if (newoffset) {
            *newoffset = 0;
        }
        return 0;
    }
    /* 不支持其他類型的隨機訪問 */
    return -1;
}

static php_stream_ops php_varstream_dirops = {
    NULL, /* write */
    php_varstream_readdir,
    php_varstream_closedir,
    NULL, /* flush */
    PHP_VARSTREAM_DIRSTREAMTYPE,
    php_varstream_dirseek,
    NULL, /* cast */
    NULL, /* stat */
    NULL, /* set_option */
};

static php_stream *php_varstream_opendir(
            php_stream_wrapper *wrapper,
            char *filename, char *mode, int options,
            char **opened_path, php_stream_context *context
            STREAMS_DC TSRMLS_DC)
{
    php_varstream_dirdata *data;
    php_url *url;
    zval **var;

    /* 不支持持久化流 */
    if (options & STREAM_OPEN_PERSISTENT) {
        php_stream_wrapper_log_error(wrapper, options
                TSRMLS_CC, "Unable to open %s persistently",
                filename);
        return NULL;
    }

    /* 解析URL */
    url = php_url_parse(filename);
    if (!url) {
        php_stream_wrapper_log_error(wrapper, options
                TSRMLS_CC, "Unexpected error parsing URL");
        return NULL;
    }
    /* 檢查請求URL的正確性 */
    if (!url->host || (url->host[0] == 0) ||
            strcasecmp("var", url->scheme) != 0) {
        /* Bad URL or wrong wrapper */
        php_stream_wrapper_log_error(wrapper, options
                TSRMLS_CC, "Invalid URL, must be in the form: "
                "var://variablename");
        php_url_free(url);
        return NULL;
    }

    /* 查找變量 */
    if (zend_hash_find(&EG(symbol_table), url->host,
                strlen(url->host) + 1, (void**)&var) == FAILURE) {
        php_stream_wrapper_log_error(wrapper, options
                TSRMLS_CC, "Variable $%s not found", url->host);
        php_url_free(url);
        return NULL;
    }

    /* 檢查變量類型 */
    if (Z_TYPE_PP(var) != IS_ARRAY) {
        php_stream_wrapper_log_error(wrapper, options
                TSRMLS_CC, "$%s is not an array", url->host);
        php_url_free(url);
        return NULL;
    }
    /* 釋放前面分配的URL結(jié)構(gòu) */
    php_url_free(url);

    /* 分配抽象數(shù)據(jù)結(jié)構(gòu) */
    data = emalloc(sizeof(php_varstream_dirdata));
    if ( Z_ISREF_PP(var) && Z_REFCOUNT_PP(var) > 1) {
        /* 全拷貝 */
        MAKE_STD_ZVAL(data->arr);
        *(data->arr) = **var;
        zval_copy_ctor(data->arr);
        INIT_PZVAL(data->arr);
    } else {
        /* 寫時拷貝 */
        data->arr = *var;
        Z_SET_REFCOUNT_P(data->arr, Z_REFCOUNT_P(data->arr) + 1);
    }
    /* 重置數(shù)組指針 */
    zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(data->arr),
            &data->pos);
    return php_stream_alloc(&php_varstream_dirops,data,0,mode);
}

現(xiàn)在, 將你的php_varstream_wrapper_ops結(jié)構(gòu)體中的dir_opener的NULL替換成你的php_varstream_opendir函數(shù). 最后, 將下面新定義的類型放入到你的php_varstream.h文件的php_varstream_data定義下面:

#define PHP_VARSTREAM_DIRSTREAMTYPE    "varstream directory"
typedef struct _php_varstream_dirdata {
    zval *arr;
    HashPosition pos;
} php_varstream_dirdata;

在你基于fopen()實現(xiàn)的varstream包裝器中, 你直接使用持久變量名, 每次執(zhí)行讀寫操作時從符號表中獲取變量. 而這里, opendir()的實現(xiàn)中獲取變量時處理了變量不存在或者類型錯誤的異常. 你還有一個數(shù)組變量的拷貝, 這就說明原數(shù)組的改變并不會影響后續(xù)的readdir()調(diào)用的結(jié)果. 原來存儲變量名的方式也可以正常工作, 這里只是給出另外一種選擇作為演示示例.

由于目錄訪問是基于成塊的目錄條目, 而不是字符, 因此這里需要一套獨立的流操作. 這個版本中, write沒有意義, 因此保持它為NULL. read的實現(xiàn)使用zend_hash_get_current_key_ex()函數(shù)將數(shù)組映射到目錄名. 而隨機訪問也只是對SEEK_SET有效, 用來響應(yīng)rewinddir()跳轉(zhuǎn)到數(shù)組開始位置.

實際上, 目錄流并沒有使用SEEK_CUR, SEEK_END, 或者除了0之外的偏移量. 在實現(xiàn)目錄流操作時, 最好還是涉及你的函數(shù)能以某種方式處理這些情況, 以使得在流包裝層變化時能夠適應(yīng)其目錄隨機訪問.

操縱

5個靜態(tài)包裝器操作中的4個用來處理不是基于I/O的流資源操作. 你已經(jīng)看到過它們并了解它們的原型; 現(xiàn)在我們看看varstream包裝器框架中它們的實現(xiàn):

unlink

在你的wrapper_ops結(jié)構(gòu)體中增加下面的函數(shù), 它可以讓unlink()通過varstream包裝器, 擁有和unset()一樣的行為:

static int php_varstream_unlink(php_stream_wrapper *wrapper,
                        char *filename, int options,
                        php_stream_context *context
                        TSRMLS_DC)
{               
    php_url *url;   
                        
    url = php_url_parse(filename);
    if (!url) {         
        php_stream_wrapper_log_error(wrapper, options
            TSRMLS_CC, "Unexpected error parsing URL");
        return -1;   
    }       
    if (!url->host || (url->host[0] == 0) ||
        strcasecmp("var", url->scheme) != 0) {
        /* URL不合法 */
        php_stream_wrapper_log_error(wrapper, options
            TSRMLS_CC, "Invalid URL, must be in the form: "
                     "var://variablename");
        php_url_free(url);
        return -1;
    }
    
    /* 從符號表刪除變量 */
    //zend_hash_del(&EG(symbol_table), url->host, strlen(url->host) + 1);
    zend_delete_global_variable(url->host, strlen(url->host) + 1 TSRMLS_CC);
    
    php_url_free(url);                                      
    return 0;
}

這個函數(shù)的編碼量和php_varstream_opener差不多. 唯一的不同在于這里你需要傳遞變量名給zend_hash_del()去刪除變量.

譯注: 譯者的php-5.4.10環(huán)境中, 使用unlink()刪除變量后, 在用戶空間再次讀取該變量名的值會導(dǎo)致core dump. 因此上面代碼中譯者進(jìn)行了修正, 刪除變量時使用了zend_delete_global_variable(), 請讀者參考閱讀zend_delete_global_variable()函數(shù)源代碼, 考慮為什么直接用zend_hash_del()刪除, 會導(dǎo)致core dump. 下面是譯者測試用的用戶空間代碼:

<?php
$fp = fopen(&#39;var://hello&#39;, &#39;r&#39;);
fwrite($fp, &#39;world&#39;);
var_dump($hello);
unlink(&#39;var://hello&#39;);
$a  = $hello;

這個函數(shù)的代碼量應(yīng)該和php_varstream_opener差不多. 唯一的不同是這里是傳遞變量名給zend_hash_del()去刪除變量.

rename, mkdir, rmdir

為了一致性, 下面給出rename, mkdir, rmdir函數(shù)的實現(xiàn):

static int php_varstream_rename(php_stream_wrapper *wrapper,
        char *url_from, char *url_to, int options,
        php_stream_context *context TSRMLS_DC)
{
    php_url *from, *to;
    zval **var;

    /* 來源URL解析 */
    from = php_url_parse(url_from);
    if (!from) {
        php_stream_wrapper_log_error(wrapper, options
            TSRMLS_CC, "Unexpected error parsing source");
        return -1;
    }
    /* 查找變量 */
    if (zend_hash_find(&EG(symbol_table), from->host,
                strlen(from->host) + 1,
                (void**)&var) == FAILURE) {
        php_stream_wrapper_log_error(wrapper, options
            TSRMLS_CC, "$%s does not exist", from->host);
        php_url_free(from);
        return -1;
    }
    /* 目標(biāo)URL解析 */
    to = php_url_parse(url_to);
    if (!to) {
        php_stream_wrapper_log_error(wrapper, options
            TSRMLS_CC, "Unexpected error parsing dest");
        php_url_free(from);
        return -1;
    }
    /* 變量的改名 */
    Z_SET_REFCOUNT_PP(var, Z_REFCOUNT_PP(var) + 1);
    zend_hash_update(&EG(symbol_table), to->host,
                strlen(to->host) + 1, (void*)var,
                sizeof(zval*), NULL);
    zend_hash_del(&EG(symbol_table), from->host,
                strlen(from->host) + 1);
    php_url_free(from);
    php_url_free(to);
    return 0;
}

static int php_varstream_mkdir(php_stream_wrapper *wrapper,
                char *url_from, int mode, int options,
                php_stream_context *context TSRMLS_DC)
{
    php_url *url;

    /* URL解析 */
    url = php_url_parse(url_from);
    if (!url) {
       php_stream_wrapper_log_error(wrapper, options
           TSRMLS_CC, "Unexpected error parsing URL");
       return -1;
    }

    /* 檢查變量是否存在 */
    if (zend_hash_exists(&EG(symbol_table), url->host,
                    strlen(url->host) + 1)) {
        php_stream_wrapper_log_error(wrapper, options
            TSRMLS_CC, "$%s already exists", url->host);
        php_url_free(url);
        return -1;
    }
    /* EG(uninitialized_zval_ptr)通常是IS_NULL的zval *, 引用計數(shù)無限大 */
    zend_hash_add(&EG(symbol_table), url->host,
            strlen(url->host) + 1,
            (void*)&EG(uninitialized_zval_ptr),
            sizeof(zval*), NULL);
    php_url_free(url);
    return 0;
}

static int php_varstream_rmdir(php_stream_wrapper *wrapper,
                char *url, int options,
                php_stream_context *context TSRMLS_DC)
{
    /* 行為等價于unlink() */
    wrapper->wops->unlink(wrapper, url, options,
                                context TSRMLS_CC);
}

檢查

并不是所有的流操作都涉及到資源的操縱. 有時候也需要查看活動的流在某個時刻的狀態(tài), 或檢查潛在可打開的資源的狀態(tài).

這一節(jié)流和包裝器的ops函數(shù)都是在相同的數(shù)據(jù)結(jié)構(gòu)php_stream_statbuf上工作的, 它只有一個元素: posix標(biāo)準(zhǔn)的struct statbuf. 當(dāng)本節(jié)的某個函數(shù)被調(diào)用時, 將嘗試填充盡可能多的statbuf元素的成員.

stat

如果設(shè)置, 當(dāng)請求激活流實例的信息時, 將會調(diào)用wrapper->ops->stream_stat(). 如果沒有設(shè)置, 則對應(yīng)的stream->ops->stat()將會被調(diào)用. 無論哪個函數(shù)被調(diào)用, 都應(yīng)該盡可能多的向返回的statbuf結(jié)構(gòu)體ssb->sb中填充盡可能多流實例的有用信息. 在普通文件I/O的用法中, 它對應(yīng)fstat()的標(biāo)準(zhǔn)I/O調(diào)用.

url_stat

在流實例外部調(diào)用wrapper->ops->url_stat()取到流資源的元數(shù)據(jù). 通常來說, 符號鏈接和重定向都應(yīng)該被解析, 直到找到一個真正的資源, 對其通過stat()系統(tǒng)調(diào)用這樣的機制讀取統(tǒng)計信息. url_stat的flags參數(shù)允許是下面PHP_STREAM_URL_STAT_*系列的常量值(省略PHP_STREAM_URL_STAT_前綴):

LINK不解析符號鏈接和重定向. 而是報告它碰到的第一個節(jié)點的信息, 無論是連

接還是真正的資源.

QUIET ne signale pas les erreurs. Notez que c'est exactement le contraire de la logique REPORT_ERRORS dans de nombreuses autres fonctions de flux

<.>.

Résumé

Qu'il s'agisse d'une ressource de streaming qui expose les E/S du réseau distant ou d'un local source de données, il vous permet de connecter l'extension aux fonctions de manipulation de Core Data, évitant ainsi d'avoir à réimplémenter la gestion monotone des descripteurs et le travail du tampon d'E/S. Cela la rend plus utile et puissante dans les environnements d'espace utilisateur.

Le chapitre suivant terminera l'étude de la couche d'empaquetage de flux en découvrant les filtres et les contextes. Les filtres et les contextes peuvent être utilisés pour sélectionner le comportement de flux par défaut et même modifier les données dans le processus.

Ce qui précède est le contenu du [Traduction] [développement d'extensions php et intégré] Chapitre 15 - Implémentation de PHP à mi-parcours Pour plus de contenu connexe, veuillez faire attention au site Web PHP chinois (m.miracleart.cn)?!


Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefa?on, veuillez contacter admin@php.cn

Outils d'IA chauds

Undress AI Tool

Undress AI Tool

Images de déshabillage gratuites

Undresser.AI Undress

Undresser.AI Undress

Application basée sur l'IA pour créer des photos de nu réalistes

AI Clothes Remover

AI Clothes Remover

Outil d'IA en ligne pour supprimer les vêtements des photos.

Clothoff.io

Clothoff.io

Dissolvant de vêtements AI

Video Face Swap

Video Face Swap

échangez les visages dans n'importe quelle vidéo sans effort grace à notre outil d'échange de visage AI entièrement gratuit?!

Article chaud

Outils chauds

Bloc-notes++7.3.1

Bloc-notes++7.3.1

éditeur de code facile à utiliser et gratuit

SublimeText3 version chinoise

SublimeText3 version chinoise

Version chinoise, très simple à utiliser

Envoyer Studio 13.0.1

Envoyer Studio 13.0.1

Puissant environnement de développement intégré PHP

Dreamweaver CS6

Dreamweaver CS6

Outils de développement Web visuel

SublimeText3 version Mac

SublimeText3 version Mac

Logiciel d'édition de code au niveau de Dieu (SublimeText3)

Sujets chauds

Tutoriel PHP
1502
276
PHP appelle AI Intelligent Voice Assistant Assistant PHP Interaction System Construction PHP appelle AI Intelligent Voice Assistant Assistant PHP Interaction System Construction Jul 25, 2025 pm 08:45 PM

L'entrée vocale de l'utilisateur est capturée et envoyée au backend PHP via l'API MediaRecorder du JavaScript frontal; 2. PHP enregistre l'audio en tant que fichier temporaire et appelle STTAPI (tel que Google ou Baidu Voice Recognition) pour le convertir en texte; 3. PHP envoie le texte à un service d'IA (comme Openaigpt) pour obtenir une réponse intelligente; 4. PHP appelle ensuite TTSAPI (comme Baidu ou Google Voice Synthesis) pour convertir la réponse en fichier vocal; 5. PHP diffuse le fichier vocal vers l'avant pour jouer, terminant l'interaction. L'ensemble du processus est dominé par PHP pour assurer une connexion transparente entre toutes les liens.

Comment utiliser PHP pour créer des fonctions de partage social PHP Partage d'interface Pratique Comment utiliser PHP pour créer des fonctions de partage social PHP Partage d'interface Pratique Jul 25, 2025 pm 08:51 PM

La méthode principale de création de fonctions de partage social dans PHP est de générer dynamiquement des liens de partage qui répondent aux exigences de chaque plate-forme. 1. Obtenez d'abord la page actuelle ou les informations d'URL et d'article spécifiées; 2. Utilisez UrLencode pour coder les paramètres; 3. épisser et générer des liens de partage en fonction des protocoles de chaque plate-forme; 4. Afficher les liens sur l'avant pour que les utilisateurs puissent cliquer et partager; 5. Générez dynamiquement des balises OG sur la page pour optimiser l'affichage du contenu du partage; 6. Assurez-vous d'échapper à la saisie des utilisateurs pour empêcher les attaques XSS. Cette méthode ne nécessite pas d'authentification complexe, a de faibles co?ts de maintenance et convient à la plupart des besoins de partage de contenu.

Comment utiliser PHP combiné avec l'IA pour obtenir la correction de texte de la syntaxe PHP détection et l'optimisation Comment utiliser PHP combiné avec l'IA pour obtenir la correction de texte de la syntaxe PHP détection et l'optimisation Jul 25, 2025 pm 08:57 PM

Pour réaliser la correction d'erreur de texte et l'optimisation de la syntaxe avec l'IA, vous devez suivre les étapes suivantes: 1. Sélectionnez un modèle ou une API d'IA appropriée, tels que Baidu, Tencent API ou bibliothèque NLP open source; 2. Appelez l'API via Curl ou Guzzle de PHP et traitez les résultats de retour; 3. Afficher les informations de correction d'erreur dans l'application et permettre aux utilisateurs de choisir d'adopter l'adoption; 4. Utilisez PHP-L et PHP_CODESNIFFER pour la détection de syntaxe et l'optimisation du code; 5. Collectez en continu les commentaires et mettez à jour le modèle ou les règles pour améliorer l'effet. Lorsque vous choisissez AIAPI, concentrez-vous sur l'évaluation de la précision, de la vitesse de réponse, du prix et du support pour PHP. L'optimisation du code doit suivre les spécifications du PSR, utiliser le cache raisonnablement, éviter les requêtes circulaires, revoir le code régulièrement et utiliser x

PHP crée un système de commentaires de blog pour monétiser la revue des commentaires PHP et la stratégie anti-brosse PHP crée un système de commentaires de blog pour monétiser la revue des commentaires PHP et la stratégie anti-brosse Jul 25, 2025 pm 08:27 PM

1. La maximisation de la valeur commerciale du système de commentaires nécessite de combiner la livraison précise de la publicité native, les services à valeur ajoutée par l'utilisateur (tels que le téléchargement d'images, les commentaires de recharge), d'influencer le mécanisme d'incitation basé sur la qualité des commentaires et la conformité de la monétisation anonyme des données de données; 2. La stratégie d'audit doit adopter une combinaison de mécanismes de filtrage des mots clés dynamiques pré-audit et de signalement des utilisateurs, complétés par une note de qualité des commentaires pour réaliser une exposition hiérarchique de contenu; 3. Anti-brosses nécessite la construction d'une défense multicouche: la vérification sans capteur RecaptChav3, le robot de reconnaissance de champ de miel, IP et la limite de fréquence d'horodatage empêchent l'arrosage, et la reconnaissance du modèle de contenu marque les commentaires suspects et itéra en continu pour traiter les attaques.

Comment utiliser PHP pour combiner l'IA pour générer une image. PHP génère automatiquement des ?uvres d'art Comment utiliser PHP pour combiner l'IA pour générer une image. PHP génère automatiquement des ?uvres d'art Jul 25, 2025 pm 07:21 PM

PHP n'effectue pas directement un traitement d'image AI, mais s'intègre via les API, car il est bon dans le développement Web plut?t que dans les taches à forte intensité informatique. L'intégration de l'API peut atteindre une division professionnelle du travail, réduire les co?ts et améliorer l'efficacité; 2. Intégration des technologies clés incluez l'utilisation de Guzzle ou Curl pour envoyer des demandes HTTP, le codage et le décodage des données JSON, l'authentification de la sécurité des clés de l'API, les taches de traitement de la file d'attente asynchrones, les taches prenant du temps, la gestion des erreurs robuste et le mécanisme de retrait, le stockage et l'affichage d'images; 3. Les défis courants incluent le co?t des API incontr?lable, les résultats de génération incontr?lables, la mauvaise expérience utilisateur, les risques de sécurité et la gestion difficile des données. Les stratégies de réponse consistent à définir des quotas et des caches utilisateur, en fournissant des conseils ProTT et une sélection multi-images, des notifications asynchrones et des invites de progrès, un stockage et un audit de contenu de la variable d'environnement clé et un stockage cloud.

PHP réalise la gestion des stocks de produits de base et la monétisation de la synchronisation et du mécanisme d'alarme de l'inventaire PHP PHP réalise la gestion des stocks de produits de base et la monétisation de la synchronisation et du mécanisme d'alarme de l'inventaire PHP Jul 25, 2025 pm 08:30 PM

PHP assure l'atomicité de la déduction des stocks via les transactions de base de données et les verrous en ligne de Forupdate pour empêcher la survente élevée élevée en simultation; 2. La cohérence de l'inventaire multiplateforme dépend de la gestion centralisée et de la synchronisation axée sur les événements, combinant des notifications API / WebHook et des files d'attente de messages pour assurer une transmission fiable de données; 3. Le mécanisme d'alarme doit définir un faible inventaire, un inventaire zéro / négatif, des ventes invidables, des cycles de réapprovisionnement et des stratégies de fluctuations anormales dans différents scénarios, et sélectionner Dingtalk, SMS ou les personnes responsables par e-mail en fonction de l'urgence, et les informations d'alarme doivent être complètes et claires pour réaliser l'adaptation et la réponse rapide.

Au-delà de la pile de lampe: le r?le de PHP dans l'architecture d'entreprise moderne Au-delà de la pile de lampe: le r?le de PHP dans l'architecture d'entreprise moderne Jul 27, 2025 am 04:31 AM

PhpisstillRelevantinmodernerterpriseenvironments.1.modernPhp (7.xand8.x) offre des performances, des stricts, un jitcompilation, et modernsyntax, rendant la main

Comment créer un environnement PHP Nginx avec MacOS pour configurer la combinaison des services Nginx et PHP Comment créer un environnement PHP Nginx avec MacOS pour configurer la combinaison des services Nginx et PHP Jul 25, 2025 pm 08:24 PM

Le r?le principal de Homebrew dans la construction de l'environnement Mac est de simplifier l'installation et la gestion des logiciels. 1. Homebrew gère automatiquement les dépendances et encapsule les processus de compilation et d'installation complexes en commandes simples; 2. Fournit un écosystème de package logiciel unifié pour assurer la normalisation de l'emplacement et de la configuration de l'installation logicielle; 3. Intègre les fonctions de gestion des services et peut facilement démarrer et arrêter les services via BrewServices; 4. Mise à niveau et maintenance des logiciels pratiques et améliore la sécurité et les fonctionnalités du système.

See all articles