Mac环境PHP踩过的“坑” (一)strtoupper系列

如题,在Mac下本来愉快的写着代码。结果一个

strtoupper(‘法克‘); 输出乱码,把我搞了一个下午。。。。

上代码(摘自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的问题一概搜不到接近真相的可能 。

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

其实他们有说过,不要试图。。。。所以啊,这些个“坑”都真的是“坑”么?

附上摘录,懒惰的哥们可以不用点连接了:

字符串类型详解?

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 规格。例如一个程序如果假定只有大写和小写,那可是大错特错。

字符串类型详解?

你也许常常会发现现存的 PHP 应用很难运行在多字节环境下。 发生这种情况的原因是大多数那种 PHP 应用使用了标准的字符串函数,类似 substr(),已知无法处理多字节编码的字符串。

mbstring 支持一个“函数重载”功能,将对应的多字节版本重载到标准字符处理函数上,例如你能够让这类应用在不修改代码的前提下添加多字节的处理能力。 比如,启用函数重载后,mb_substr() 将会代替 substr() 被调用。 在很多情况下这个功能允许让仅支持单字节编码的应用简单地和多字节环境对接。

要使用函数重载功能,设置 php.ini 里的 mbstring.func_overload 为正值,就是表示为重载函数分类的位掩码组合。 要重载 mail() 函数需要设置它为 1。字符串函数设置为 2,正则表达式函数为 4。 例如,当它设置为 7, mail、strings 和 正则表达式函数将都会被重载。

时间: 2024-10-13 07:00:23

Mac环境PHP踩过的“坑” (一)strtoupper系列的相关文章

Appium+Java(三)搭建环境之踩过的坑

一.原因 有最少两年多没写手机自动化测试了,正巧公司有需求搞TOC端自动化测试调研,先入为主,因为之前写过appium所以先用它埋点试水了 二.现象: 1.我早期的appium版本是v1.4.16 2.那会写ui自动化,用真机华为P7,Android版本4.2.1(具体忘记了),脚本跑起来可稳定了 3.结果今天把环境搭建完,插上测试机,demo各路报错,来吧,最爱的血拼,发现现象如下: Android版本过高第一个报错 jdk版本过低 三.最新搭建方法: sdk版本不变保持(还好用,哈哈) ap

在Mac osx使用ADT Bundle踩过的坑

前言 本篇博客整理一下笔者在Mac下使用ADT Bundle踩过的坑,Google现在也不支持Eclipse了,开发者也到了抛弃Eclipse的时候,但考虑到大部分Java的开发者还是比较习惯与Eclipse下进行Android开发,Android Studio的使用还是有一定门槛的,比如它比较耗性能,界面.操作类似idea.不管是Eclipse还是Android Studio都有对应Windows版本.mac版本.Linux版本,windows版本不用多说,支持得比较好,兼容性较高,但mac在

React Native 环境搭建踩坑

React Native (web Android)环境搭建踩坑(真的是一个艰辛的过程,大概所有坑都被我踩了 官方文档地址 : https://facebook.github.io/react-native/docs/getting-started 选择  Building Projects with Native Code 大家可以参照官方文档一步一步来,下面是我遇到的一些问题总结 查看一下node版本           node -v ------- 进入安装之前最好查一下JDK版本   

【转载】Fragment 全解析(1):那些年踩过的坑

http://www.jianshu.com/p/d9143a92ad94 Fragment系列文章:1.Fragment全解析系列(一):那些年踩过的坑2.Fragment全解析系列(二):正确的使用姿势3.Fragment之我的解决方案:Fragmentation 本篇主要介绍一些最常见的Fragment的坑以及官方Fragment库的那些自身的BUG,这些BUG在你深度使用时会遇到,比如Fragment嵌套时或者单Activity+多Fragment架构时遇到的坑.如果想看较为实用的技巧,

初学spring boot踩过的坑

一.搭建spring boot环境 maven工程 pom文件内容 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-

Mac环境下安装运行splash

http://blog.csdn.net/chenhy8208/article/details/69391097 最近需要使用scrapy爬虫做一些开发,用到了splash.我本机是mac环境,跳着看资料,导致碰到了一些坑,记录一下mac如何安装运行splash 1.下载安装 DockerToolbox(下载地址) 下载完成以后,会安装下面3个app.  点击第一Terminal运行. 2.按照官方文档 下载.运行启动 splash 1.Pull the image: $ Docker pull

多线程和异步编程示例和实践-踩过的坑

上两篇文章,主要介绍了Thread.ThreadPool和TPL 多线程异步编程示例和实践-Thread和ThreadPool 多线程异步编程示例和实践-Task 本文中,分享两则我们在做多线程和异步编程中实际踩过的坑,实际生产环境遇到的问题,以及解决办法. 1. HttpClient 业务场景:使用HttpClient实现第三方业务推送,当第三方的Http服务器不通.或者返回很慢时 线程数暴涨 Asp.Net\Asp.Net MVC场景下,并发多线程导致的线程阻塞:HttpClient.Pos

Python(Django) 连接MySQL(Mac环境)

看django的文档,详细的一塌糊涂,这对文档来时倒是好事,可是数据库连接你别一带而过啊.感觉什么都想说又啥都没说明白,最有用的一句就是推荐mysqlclient.展开一个Django项目首先就是成功连接数据库然后安装顶层应用.那链接数据库就分两点: 首先你的python环境可以连接到你的mysql环境. 把数据库参数写到Django配置文件中. 第二点文档写的很明白了照做即可,关键是小弟没用python连过mysql啊.当年用php的时候哪有这么多事,一键搭建环境舒服的不行,部署也就是FTP传

1.MySQL5.7.19 安装配置踩过的坑

这篇文章主要是分享 安装MySQL时遇到的一些问题,以及解决方法. 第一步:下载MySQL 下载地址:https://dev.mysql.com/downloads/mysql/5.1.html#downloads 我下载的是5.7.19版本,下载地址里面也只有解压版,下载后再进行环境变量的配置就可以. 2.解压并安装Mysql5.7.19 (1)将下载的包解压到指定的路径,自己可以指定路径,我直接解压到了D盘,解压到了                         D:\mysq的目录下.则