Mac環境PHP踩過的「坑」 (一)函數重載

零、問題¶php

  在Mac下開發原本很高興,但是有一個地方把人噁心到了,Apache+Mod_PHP方式,出現了一個strtoupper('漢字')亂碼的問題。。結果一個html

  strtoupper('法克'); 輸出亂碼,把我搞了一個下午。。。。c++

 1、問題定位(源碼分析)¶

 上代碼(摘自ZF1):程序員

 

# \Zend_Locale_Format::_parseDate line 866-871
# 這裏有這麼一段精彩絕倫的轉換(斷定時間格式"2015-05-07 a18:33:32"裏面是否含有一個a,若是有則表示上午,不然下午:Zend大哥,爲嘛要那麼多strtoupper?代碼還不夠長?)
// get daytime
if (iconv_strpos($format, 'a') !== false) {
    /*對,就是這裏,掛掉*/  
    if (iconv_strpos(strtoupper($number), strtoupper(Zend_Locale_Data::getContent($options['locale'], 'am'))) !== false) {
        $am = true;
    } else if (iconv_strpos(strtoupper($number), strtoupper(Zend_Locale_Data::getContent($options['locale'], 'pm'))) !== false) {
        $am = false;
    }
}

  百思不得其解,因而bing/google/baddu....stackoverflow。。。依然無解,而後繼續xdebug單步調試仍然無所收穫,毫無進展,無奈祭出翻源碼大法:正則表達式

PHPAPI char *php_strtoupper(char *s, size_t len)
{
        unsigned char *c, *e;

        c = (unsigned char *)s;
        e = (unsigned char *)c+len;

        while (c < e) {
                *c = toupper(*c);
                c++;
        }
        return s;
}

  看到這麼簡潔的代碼,我竟無言以對!!把這段代碼給深諳C的G大哥,可這總不能改源碼修復這個bug吧?是PHP源碼的問題嗎?就在G大哥搞出一個辦法時,小夥伴H兄duang的一下找到問題方案了,果真思路正確結果OK。過後總結,不要試圖找太多系統代碼因素(Zend怎麼會不清楚呢?),任何PHP (Linux)的問題,總歸要先看下配置,純找strtoupper的問題或者locale的問題一律搜不到接近真相的可能 。數組

2、解決方法(配置php.ini) ¶

Mac下面須要顯式的設置兩個參數: 安全

; internal/script encoding.
; Some encoding cannot work as internal encoding.
; (e.g. SJIS, BIG5, ISO-2022-*)
; http://php.net/mbstring.internal-encoding
;mbstring.internal_encoding = UTF-8
; 不顯式指定的話,Mac下會默認 ISO-8859-1 (好差別,說好的默認值呢。。)
mbstring.internal_encoding = UTF-8 

; overload(replace) single byte functions by mbstring functions.
; mail(), ereg(), etc are overloaded by mb_send_mail(), mb_ereg(),
; etc. Possible values are 0,1,2,4 or combination of them.
; For example, 7 for overload everything.
; 0: No overload
; 1: Overload mail() function
; 2: Overload str*() functions
; 4: Overload ereg*() functions
; http://php.net/mbstring.func-overload
;mbstring.func_overload = 0
mbstring.func_overload = 2


 再次謝過身邊的小夥伴,you are the best!!網絡

 PS:感謝官方關於string的解釋 http://php.net/manual/zh/language.types.string.php 函數

       以及函數重載的故事(對,這是我從業8年第一次認真瞭解這個事情!!) http://php.net/manual/zh/mbstring.overload.php 源碼分析

       其實他們有說過,不要試圖。。。。因此啊,這些個「坑」都真的是「坑」麼?

  附上摘錄,懶惰的哥們能夠不用點鏈接了:

PS1:字符串類型詳解 ¶

PHP 中的 string 的實現方式是一個由字節組成的數組再加上一個整數指明緩衝區長度。並沒有如何將字節轉換成字符的信息,由程序員來決定。字符串由什麼值來組成並沒有限制;特別的,其值爲 0(「NUL bytes」)的字節能夠處於字符串任何位置(不過有幾個函數,在本手冊中被稱爲非「二進制安全」的,也許會把 NUL 字節以後的數據全都忽略)。

字符串類型的此特性解釋了爲何 PHP 中沒有單獨的「byte」類型 - 已經用字符串來代替了。返回非文本值的函數 - 例如從網絡套接字讀取的任意數據 - 仍會返回字符串。

因爲 PHP 並不特別指明字符串的編碼,那字符串究竟是怎樣編碼的呢?例如字符串 "á" 究竟是等於 "\xE1"(ISO-8859-1),"\xC3\xA1"(UTF-8,C form),"\x61\xCC\x81"(UTF-8,D form)仍是任何其它可能的表達呢?答案是字符串會被按照該腳本文件相同的編碼方式來編碼。所以若是一個腳本的編碼是 ISO-8859-1,則其中的字符串也會被編碼爲 ISO-8859-1,以此類推。不過這並不適用於激活了 Zend Multibyte 時;此時腳本能夠是以任何方式編碼的(明確指定或被自動檢測)而後被轉換爲某種內部編碼,而後字符串將被用此方式編碼。注意腳本的編碼有一些約束(若是激活了 Zend Multibyte 則是其內部編碼)- 這意味着此編碼應該是 ASCII 的兼容超集,例如 UTF-8 或 ISO-8859-1。不過要注意,依賴狀態的編碼其中相同的字節值能夠用於首字母和非首字母而轉換狀態,這可能會形成問題。

固然了,要作到有用,操做文本的函數必須假定字符串是如何編碼的。不幸的是,PHP 關於此的函數有不少變種:

  • 某些函數假定字符串是以單字節編碼的,但並不須要將字節解釋爲特定的字符。例如 substr()strpos()strlen()和 strcmp()。理解這些函數的另外一種方法是它們做用於內存緩衝區,即按照字節和字節下標操做。

  • 某些函數被傳遞入了字符串的編碼方式,也可能會假定默認無此信息。例如 htmlentities() 和 mbstring 擴展中的大部分函數。

  • 其它函數使用了當前區域(見 setlocale()),可是逐字節操做。例如 strcasecmp()strtoupper() 和 ucfirst()。這意味着這些函數只能用於單字節編碼,並且編碼要與區域匹配。例如 strtoupper("á") 在區域設定正確而且 á 是單字節編碼時會返回 "Á"。若是是用 UTF-8 編碼則不會返回正確結果,其結果根據當前區域有可能返回損壞的值。

  • 最後一些函數會假定字符串是使用某特定編碼的,一般是 UTF-8。intl 擴展和 PCRE(上例中僅在使用了 u 修飾符時)擴展中的大部分函數都是這樣。儘管這是因爲其特殊用途,utf8_decode() 會假定 UTF-8 編碼而utf8_encode() 會假定 ISO-8859-1 編碼。

最後,要書寫可以正確使用 Unicode 的程序依賴於很當心地避免那些可能會損壞數據的函數。要使用來自於 intl 和mbstring 擴展的函數。不過使用能處理 Unicode 編碼的函數只是個開始。無論用何種語言提供的函數,最基本的仍是瞭解 Unicode 規格。例如一個程序若是假定只有大寫和小寫,那但是大錯特錯。

  

PS2:函數重載功能 ¶

你也許經常會發現現存的 PHP 應用很難運行在多字節環境下。 發生這種狀況的緣由是大多數那種 PHP 應用使用了標準的字符串函數,相似 substr(),已知沒法處理多字節編碼的字符串。

mbstring 支持一個「函數重載」功能,將對應的多字節版本重載到標準字符處理函數上,例如你可以讓這類應用在不修改代碼的前提下添加多字節的處理能力。 好比,啓用函數重載後,mb_substr() 將會代替 substr() 被調用。 在不少狀況下這個功能容許讓僅支持單字節編碼的應用簡單地和多字節環境對接。


要使用函數重載功能,設置 php.ini 裏的 mbstring.func_overload 爲正值,就是表示爲重載函數分類的位掩碼組合。 要重載 mail() 函數須要設置它爲 1。字符串函數設置爲 2,正則表達式函數爲 4。 例如,當它設置爲 7, mail、strings 和 正則表達式函數將都會被重載。 

相關文章
相關標籤/搜索