国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

PHP回顧之流

gself / 2395人閱讀

摘要:本文先簡(jiǎn)要跟蹤底層流的原理,再回到用戶態(tài)中流的使用。底層流我們知道中的函數(shù)可以打開本地文件等并返回一個(gè)句柄,函數(shù)能對(duì)資源句柄進(jìn)行讀寫,用于關(guān)閉資源。更多關(guān)于底層流的操作可參考官方文檔中開發(fā)者的流章節(jié),本文不再深入。

轉(zhuǎn)載請(qǐng)注明文章出處: https://tlanyan.me/php-review...
PHP回顧系列目錄

PHP基礎(chǔ)

web請(qǐng)求

cookie

web響應(yīng)

session

數(shù)據(jù)庫(kù)操作

加解密

Composer

創(chuàng)建自己的Composer包

發(fā)送郵件

IO

上篇 “PHP回顧之IO” 提到讀取文件、網(wǎng)絡(luò)通信等操作,本質(zhì)上是與 “流(stream)” 打交道。流機(jī)制是許多編程語(yǔ)言的重要機(jī)制,程序通過(guò)流可自由操作文件、內(nèi)存、網(wǎng)絡(luò)等設(shè)備的數(shù)據(jù)。

本文先簡(jiǎn)要跟蹤PHP底層流的原理,再回到用戶態(tài)中流的使用。

底層流

我們知道PHP中的fopen函數(shù)可以打開本地文件、URL等并返回一個(gè)句柄,freadfwrite函數(shù)能對(duì)資源句柄進(jìn)行讀寫,fclose用于關(guān)閉資源。PHP如何做到使用一致API對(duì)不同數(shù)據(jù)源進(jìn)行操作?答案是PHP引入了“流”的概念,在底層對(duì)操作進(jìn)行抽象,帶來(lái)的好處是上層可用同一套API。

為了理解PHP中的流,我們先追蹤PHP中fopen函數(shù)調(diào)用過(guò)程。PHP的底層用C實(shí)現(xiàn),閱讀文中的代碼需要一定的C語(yǔ)言基礎(chǔ)。如果不熟悉C語(yǔ)言,關(guān)注其思路即可。

用戶態(tài)的fopen函數(shù)定義在ext/standard/file.c文件中,函數(shù)體如下:

PHP_NAMED_FUNCTION(php_if_fopen)
{
    // 一些初始化代碼
    context = php_stream_context_from_zval(zcontext, 0);
    stream = php_stream_open_wrapper_ex(filename, mode, (use_include_path ? USE_PATH : 0) | REPORT_ERRORS, NULL, context);

    if (stream == NULL) {
        RETURN_FALSE;
    }
    php_stream_to_zval(stream, return_value);
}

PHP_NAMED_FUNCTION(php_if_fopen)定義PHP中的fopen函數(shù)(區(qū)別C中的fopen),有拓展開發(fā)基礎(chǔ)的應(yīng)當(dāng)對(duì)這種寫法熟悉。略過(guò)初始化等無(wú)關(guān)緊要的代碼,fopen主要工作是獲取流對(duì)象(stream)并轉(zhuǎn)成PHP值類型(zval)返回。

流對(duì)象由php_stream_open_wrapper_ex函數(shù)返回,該函數(shù)位于main/php_streams.h中,是定義在main/streams/streams.c_php_stream_open_wrapper_ex的別名:

PHPAPI php_stream *_php_stream_open_wrapper_ex(const char *path, const char *mode, int options,
        zend_string **opened_path, php_stream_context *context STREAMS_DC)
{
    // 初始化代碼
    wrapper = php_stream_locate_url_wrapper(path, &path_to_open, options);
    if (options & STREAM_USE_URL && (!wrapper || !wrapper->is_url)) {
        php_error_docref(NULL, E_WARNING, "This function may only be used against URLs");
        if (resolved_path) {
            zend_string_release(resolved_path);
        }
        return NULL;
    }

    if (wrapper) {
        if (!wrapper->wops->stream_opener) {
            php_stream_wrapper_log_error(wrapper, options ^ REPORT_ERRORS,
                    "wrapper does not support stream open");
        } else {
            stream = wrapper->wops->stream_opener(wrapper,
                path_to_open, mode, options ^ REPORT_ERRORS,
                opened_path, context STREAMS_REL_CC);
        }

    }
    // stream檢測(cè)等代碼
}

_php_stream_open_wrapper_ex函數(shù)的工作主要有兩點(diǎn):1. 調(diào)用php_stream_locate_url_wrapper函數(shù)獲取協(xié)議包裝器(wrapper);2. 調(diào)用包裝器打開資源并返回流對(duì)象。

接著看同一文件內(nèi)獲取包裝器的函數(shù)php_stream_locate_url_wrapper

PHPAPI php_stream_wrapper *php_stream_locate_url_wrapper(const char *path, const char **path_for_open, int options)
{
    // 一些初始化代碼
    for (p = path; isalnum((int)*p) || *p == "+" || *p == "-" || *p == "."; p++) {
        n++;
    }

    if ((*p == ":") && (n > 1) && (!strncmp("http://", p+1, 2) || (n == 4 && !memcmp("data:", path, 5)))) {
        protocol = path;
    }

    if (protocol) {
        if (NULL == (wrapper = zend_hash_str_find_ptr(wrapper_hash, protocol, n))) {
            char *tmp = estrndup(protocol, n);

            php_strtolower(tmp, n);
            if (NULL == (wrapper = zend_hash_str_find_ptr(wrapper_hash, tmp, n))) {
                char wrapper_name[32];

                if (n >= sizeof(wrapper_name)) {
                    n = sizeof(wrapper_name) - 1;
                }
                PHP_STRLCPY(wrapper_name, protocol, sizeof(wrapper_name), n);

                php_error_docref(NULL, E_WARNING, "Unable to find the wrapper "%s" - did you forget to enable it when you configured PHP?", wrapper_name);

                wrapper = NULL;
                protocol = NULL;
            }
            efree(tmp);
        }
    }
    /* TODO: curl based streams probably support file:// properly */
    if (!protocol || !strncasecmp(protocol, "file", n)) {
        /* fall back on regular file access */
        php_stream_wrapper *plain_files_wrapper = (php_stream_wrapper*)&php_plain_files_wrapper;

        // 檢測(cè)代碼

        return plain_files_wrapper;
    }
    // 檢測(cè)遠(yuǎn)程文件等
    return wrapper;
}

php_stream_locate_url_wrapper中,我們終于知道fopen支持本地文件、HTTP/FTP、php://等多種數(shù)據(jù)源的奧秘:函數(shù)先查找路徑是否以“http://”、"ftp://"類似協(xié)議開頭,有則從注冊(cè)的包裝器列表中查找對(duì)應(yīng)包裝器;不以協(xié)議開頭則回退到本地文件模式(php_plain_files_wrapper);fopen返回的流對(duì)象由包裝器打開。

追蹤以上代碼,fopen的奧秘已經(jīng)暴露無(wú)遺,但有兩個(gè)關(guān)鍵點(diǎn):1. 流對(duì)象(php_stream)是什么?2. 包裝器(php_stream_wrapper)是什么?

內(nèi)核開發(fā)者在源碼的README.STREAMS文件中解釋使用流的原因:讓拓展開發(fā)者能像普通文件一樣操作數(shù)據(jù)。為達(dá)到這個(gè)目的,流操作的資源都是php_stream對(duì)象。統(tǒng)一好資源接口后,PHP還定義了與文件操作對(duì)應(yīng)的一套流函數(shù):

流函數(shù)的第一個(gè)參數(shù)總是php_stream對(duì)象,例如與fread對(duì)應(yīng)的php_stream_read函數(shù)定義為:PHPAPI size_t php_stream_read(php_stream * stream, char * buf, size_t count)

流操作的支持和具體操作由包裝器決定(流包裝器實(shí)際會(huì)調(diào)用php_stream中ops成員的具體函數(shù),這些函數(shù)在包裝器打開流時(shí)被正確的賦值)。同樣是讀取數(shù)據(jù)(fread),從文件中讀和從內(nèi)存中讀做法不同。另外有些操作對(duì)某些流不適用。例如http協(xié)議支持fread,但不支持fwrite;普通文件可以其大小,但ssh2://協(xié)議的數(shù)據(jù)大小不可知(stat函數(shù)不可用)。內(nèi)置的協(xié)議包裝器列表和可用操作可參考官方文檔中的“支持的協(xié)議和包裝器”。

更多關(guān)于底層流的操作可參考官方文檔中開發(fā)者的“流”章節(jié),本文不再深入。

用戶態(tài)流

讓我們回到PHP應(yīng)用層面,即用戶態(tài)中的流。PHP的官方手冊(cè)有專門講解用戶態(tài)流的章節(jié),并提供一系列以stream開頭的函數(shù)。由于fread/fputs等函數(shù)已經(jīng)包含常見(jiàn)的流操作,stream開頭的函數(shù)主要分為三類:輔助的過(guò)濾器filter和上下文context,包裝器以及socket編程。網(wǎng)絡(luò)編程將在后續(xù)的文章中講解,我們先關(guān)注包裝器。

開發(fā)者可以注冊(cè)流包裝器實(shí)現(xiàn)自定義協(xié)議,通過(guò)協(xié)議才能正常解析流的數(shù)據(jù)。比如我們?yōu)橄旅娴男〗憬銓?shí)現(xiàn)一個(gè)專屬的協(xié)議secret://

class SecretStream {
    private $position;
    private $file;
    private $cipher = "aes-256-cbc";
    private $key = "little-sister";

    function stream_open($path, $mode, $options, &$opened_path)
    {
        $info = parse_url($path);
        $this->file = fopen($info["host"], $mode);
        $this->position = 0;
        return true;
    }

    function stream_read($count)
    {
        $line = fgets($this->file);
        $text = openssl_decrypt(base64_decode($line), $this->cipher, $this->key);
        $this->position += strlen($text);
        return $text;
    }

    function stream_write($data)
    {
        $raw = @openssl_encrypt($data, $this->cipher, $this->key);
        $base64 = base64_encode($raw);
        fwrite($this->file, $base64 . PHP_EOL);
        $this->position += strlen($data);
        return strlen($data);
    }

    function stream_tell()
    {
        return $this->position;
    }

    function stream_eof()
    {
        return feof($this->file);
    }

    function stream_close()
    {
        fclose($this->file);
    }
}

使用自定義協(xié)議先要注冊(cè),然后就可以正常使用了:

// 先注冊(cè)自定義協(xié)議
stream_wrapper_register("secret", "SecretStream")
    or die("Failed to register protocol");

// 寫數(shù)據(jù)
$fp = fopen("secret://Akari", "w+");
fwrite($fp, "IPZ-985
");
fwrite($fp, "IPX-021
");
fwrite($fp, "IPZ-933
");
fclose($fp);

// 由于協(xié)議未實(shí)現(xiàn)seek功能,不能通過(guò)rewind讓文件指針到頭部,需要重新打開
$fp = fopen("secret://Akari", "r");
while (!feof($fp)) {
    echo fgets($fp);
}
fclose($fp);

通過(guò)簡(jiǎn)單的代碼,我們安全的存儲(chǔ)了小姐姐的數(shù)據(jù),也守護(hù)了小姐姐的秘密。其他人即使獲取到文件內(nèi)容,不通過(guò)我們的協(xié)議打開也很難知道具體內(nèi)容。有沒(méi)有感覺(jué)很不錯(cuò)?小姐姐和你比心哦~

總結(jié)

本文先回顧了PHP流底層的細(xì)節(jié),再回到應(yīng)用層中流的使用,并給出了一個(gè)簡(jiǎn)單的流包裝器示例(例子簡(jiǎn)單,可用流章節(jié)中的php_user_filter來(lái)實(shí)現(xiàn))。有興趣的讀者可以為下面的小姐姐創(chuàng)建自定義的協(xié)議,示例內(nèi)容可以是:SSNI-056、SSNI-014、SNIS-662等。

本文感謝“微通廣州”的贊助。

感謝閱讀,歡迎指正!

參考

http://php.net/manual/en/book...

http://php.net/manual/en/inte...

https://blog.csdn.net/lgg201/...

https://post.zz173.com/course...

感謝閱讀,敬請(qǐng)指正!

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/28708.html

相關(guān)文章

  • PHP回顧之socket編程

    摘要:如果你想體驗(yàn)原味編程,用開頭的比較適合否則建議使用流函數(shù)。有關(guān)流的知識(shí),請(qǐng)參考本人之前的博文回顧之流。接下來(lái)我們用流函數(shù)實(shí)現(xiàn)一個(gè)簡(jiǎn)單的客戶端和服務(wù)端。流函數(shù)中的和兩個(gè)函數(shù)是我們想要的。本文目的是簡(jiǎn)要介紹中的編程,行文到此已經(jīng)達(dá)到目的。 轉(zhuǎn)載請(qǐng)注明文章出處: https://tlanyan.me/php-review... PHP回顧系列目錄 PHP基礎(chǔ) web請(qǐng)求 cookie w...

    tomorrowwu 評(píng)論0 收藏0
  • 機(jī)器學(xué)習(xí) 刀光劍影 之屠龍刀

    摘要:在下以為,集成學(xué)習(xí)就是這把屠龍刀。集成學(xué)習(xí)在眾多的機(jī)器學(xué)習(xí)數(shù)據(jù)挖掘競(jìng)賽中往往探囊取物,屢試不爽,像屠龍刀一樣當(dāng)之無(wú)愧排行兵器譜第一。這是機(jī)器學(xué)習(xí)研究中少有的理論指導(dǎo)的創(chuàng)新案列。歷史的年輪不知不覺(jué)來(lái)到了年,統(tǒng)計(jì)學(xué)家已開始在機(jī)器學(xué)習(xí)界站穩(wěn)腳跟。 機(jī)器學(xué)習(xí)是一個(gè)大武林,這里面江湖人士頗多,發(fā)明出來(lái)的算法兵器也是五花八門,浩瀚如海,足夠你數(shù)上三天兩夜了。然而,這些兵器行走江湖能用的不多,真正無(wú)敵的更...

    CloudwiseAPM 評(píng)論0 收藏0
  • Node中間層實(shí)踐(一)——基于NodeJS的全棧式開發(fā)

    摘要:總結(jié)我覺(jué)得,以后基于的全棧式開發(fā)的模式將會(huì)越來(lái)越流行,這也會(huì)引領(lǐng)前端步入工程化時(shí)代。歡迎繼續(xù)關(guān)注本博的更新中間層實(shí)踐一基于的全棧式開發(fā)中間層實(shí)踐二搭建項(xiàng)目框架中間層實(shí)踐三配置中間層實(shí)踐四模板引擎中間層實(shí)踐五中間層的邏輯處理 版權(quán)聲明:更多文章請(qǐng)?jiān)L問(wèn)我的個(gè)人站Keyon Y,轉(zhuǎn)載請(qǐng)注明出處。 前言 近期公司有個(gè)新項(xiàng)目,由于后端人手不足,我果斷的提議用node中間層的方案,得到了老大的支持...

    warkiz 評(píng)論0 收藏0
  • PHP回顧之執(zhí)行流程及相關(guān)概念

    摘要:通過(guò),腳本層無(wú)需過(guò)多考慮執(zhí)行的具體環(huán)境,而本身則可以讓針對(duì)自己的特點(diǎn)給出特有實(shí)現(xiàn)。模式下,也只執(zhí)行一次。這幾個(gè)概念的關(guān)系如下網(wǎng)關(guān)協(xié)議,與語(yǔ)言無(wú)關(guān),所以與關(guān)系也不大。總結(jié)本文簡(jiǎn)要回顧了程序的架構(gòu)和執(zhí)行流程,并對(duì)幾個(gè)容易混淆概念做了介紹。 轉(zhuǎn)載請(qǐng)注明文章出處:https://tlanyan.me/php-review... PHP回顧系列目錄 PHP基礎(chǔ) web請(qǐng)求 cookie we...

    jsdt 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

gself

|高級(jí)講師

TA的文章

閱讀更多
最新活動(dòng)
閱讀需要支付1元查看
<