PHP
PHP 未知类型变量 empty isset 陷阱
3用 empty 检查位置类型的变量时,容易掉进难以察觉的陷阱,请看下面简单的代码,预测一下 3 个 var_dump 的输出:
$mixed_1 = array(); $mixed_2 = 'Nihao'; $bool_1 = empty($mixed_1['1_type']); $bool_2 = empty($mixed_2['2_type']); var_dump($bool_1); var_dump($bool_2); var_dump($mixed_2['2_type']);
为了不影响你的判断,我多空几行,然后对比结果,是否跟你判断的一致。
。
。
。
。
。
。
。
。
。
。
。
。
。
。
公布结果:
bool(true) bool(false) string(1) "h"
后面 2 个结果,是不是有点意外?
导致问题的原因,就是 PHP 会自动把字符串按照数组来处理,并且,当 $mixed 不是严格意义的数组时,对元素的引用 key 会把 key 转换为数字,2_type 转换为数字之后,就是数字 “2″,对应的元素值就是 “h”。
对应的,isset 也会认为 $mixed_2['2_type'] 是存在的。
结论:
在对未知类型的变量做 isset 或 empty 查询时,应同时附加 is_array 等类型检查,才会更安全。
Duplicate entry ‘localhost-’ for key 1
1导致这个错误的原因:
因为安装系统时设置了hostname为localhost导致mysql在创建表的时候没有创建成功。因此mysql库中user表里没有root这个用户或者说root没有显示出来,无法查看root的状态。这时需要手动创建。然而在安全模式无法直接创建用户,错误见下。
变个方向就能创建了,先给root给予权限,然后刷新表。之后就可以创建了。
一、killall -TERM mysqld
以安全模式启动mysql
/usr/bin/mysqld_safe –skip-grant-tables &
进入mysql
/usr/bin/mysql
mysql> use mysql
Database changed
mysql> select * from user where user=’root’;
Empty set (0.00 sec)
mysql> grant all privileges on *.* to root@localhost identified by ‘linuxtone’ with GRANT OPTION;
ERROR 1290 (HY000): The MySQL server is running with the –skip-grant-tables option so it cannot execute
this statement
mysql> update user set password=password(‘linuxtone’) where user=’root’;
Query OK, 0 rows affected (0.00 sec)
Rows matched: 0 Changed: 0 Warnings: 0
注意单双引号。
mysql> flush privileges; //注意先刷新下表
Query OK, 0 rows affected (0.00 sec)
mysql> grant all privileges on *.* to root@localhost identified by ‘linuxtone’ with GRANT OPTION;
Query OK, 0 rows affected (0.00 sec)
mysql> flush privileges;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from user where user=’root’;
后期处理:
/usr/local/mysql/bin/mysqladmin shutdown -uroot -p
刚才的密码
然后正常启动:/usr/bin/mysqld_safe &
/usr/local/mysql/bin/mysql -uroot -p
能正常登录!
二Duplicate entry ‘localhost-’ for key 1 问题,很可能是你的主机名有问题,比如是localhost?
导致mysql 的root 帐号的三个主机值(分别是%、localhost、主机名)的后二者的名字弄成一样了,导使唯一键值出现重复而禁用该记录了?
朋友的主机名设置成了:localhost 遭成了Duplicate entry ‘localhost-’ for key 1
设置好正确的主机名仍后重新编译就可以了,朋友安装系统的时候没有注意主机名
用工具提高效率 解决 PhpMyAdmin 乱码
0最近做的项目有点特殊,多人共用开发服务器,无法搭建个人独享的开发环境。
连数据库,不能使用客户端的 GUI 工具,本来我已经离不开 SQLyog 了,现在逼我用命令行,我怎么受得了?
欣慰的是,尽管 MySQL server 虽然不在开发服务器上,也没有 root 权限给我的 IP 做授权,但开发服务器的 IP 是在授权列表内的。
我又想起了 PhpMyAdmin 这个好久不用的工具。
在 Dev server 上装好,配好用户名和密码,然后即可访问 MySQL,不过又遇到新问题!
MySQL server、Database、Table 和 Column 的字符集都是 latin1,而写入数据库的字符是 GBK。虽然设计不够合理,但也算勉强能用,用 SecureCRT 的命令行连接 MySQL,是可以看到正确的中文字符的,因为 SecureCRT 设置的是默认的字符集。
但 PhpMyAdmin 却总是乱码!
查了下 Variables 标签,有 2 个关于字符集的高亮提示,意思是全局设置是 latin1,但 character set client 是 utf8。
任凭我怎么改 MySQL connection collation 的设置,都无效。
tail 了一下 MySQL query log,看到 PhpMyAdmin 总是发送
SET CHARACTER SET ‘utf8′
SET collation_connection = ‘utf8_general_ci’
这 2 条语句!
我又 grep 一下,看到有个文件中的代码,让这 2 条语句被强制执行!
用 解决 PhpMyAdmin 字符乱码问题 所述的方法修改,即刻解决问题!
这下又能高效的访问 MySQL 啦,我相信打字再快的人,输入一条 Select 语句也没有我点击一下鼠标快!
phpDocumentor PHP 文档生成 CHM
1好久没写新东西了。
最近遇到很头疼的事,好多代码要维护,时间长了,自己写过的代码都想不起来。
要调用某个对象的方法,每次都要打开那个类定义的 PHP 文件,找到方法的定义部分,然后仔细看看参数列表才能想起来怎么用。
最麻烦的是,有时候想不起来那个方法是在哪个类里面定义的,必须得去代码根目录 grep ,然后等半天……
相对比的,我开发程序时,手边必备的几个文档:PHP手册、MySQL手册、HTML手册、CSS手册、Javascript手册,以及最近开始用的 ActionScript手册。这些都是 chm 版本的!
Chm 帮助文件有 2 个最大的好处,一个是索引,另一个是全文检索。
用索引可以一边输入,一边过滤掉不匹配的关键词,迅速定位到想要的那个函数名。
当不知道关键词是什么的时候,可以用 like 语句在 chm 文件里面的每一句话中匹配关键词。
原本,用 Zend Studio 也有代码提示和自动完成,不过这个用 Java 虚拟机运行的 IDE 实在是慢,受不了,也不习惯。
今天花了好长时间,研究 php 文档生成工具,找到了几个,主要都是生成 html 页面的,而且格式很难看,重要的是没有好用的索引,更没有全文搜索,没意思。
PhpDocumentor 支持 chm 生成,这个让我眼前一亮!
简单看了下官方文档,然后就下载了 zip 格式的压缩包,就是这个压缩包,浪费了我好长时间去调试错误!!
设定好 php 源码路径,再设置文档格式,选择 chm,输出了一堆文件,包含 hhc,hhp 等,这些是 chm 的“源码”,需要用 Windows 自带的 HTML Help WorkShop 进行编译,然后才能生成 chm。
而那个 zip 压缩包,居然是有严重 bug 的版本,生成的 hhc 文件,里面有一行错误代码,提示 contents.hhc.tpl 找不到!!!
最开始我没有意识到问题在这里,只是用 WorkShop 反复编译那堆 hhc 文件,总是不成功!我都有放弃的念头了。
后来仔细看错误提示,好像是 hhc 文件的问题,打开一看,才知道是上述原因。
再去 phpDocument 源码里面找 contents.hhc.tpl,真的没有!不过,却有 contents.hhc.tp 这个文件!!!
然后列了下目录,几十个文件,大部分都是 tpl 扩展名,而有几个文件是 tp!!!
靠!什么玩意儿,是程序员马虎了还是打包工具的问题?
重新下了个 tgz 版本的 phpDocument,问题全无,chm 编译顺利!
另外,默认编译的 chm 是不带搜索(全文检索)功能的,更改方式如下:
打开一个项目文件,单击“Project(项目)”选项卡的“Change project options(改变项目选项)”,在“Options(选项)”对话框的“Compiler(编译)”选项卡中选中“Compile Full Text Search Information(编译全文搜索信息)”即可。
附赠命令行范例:
$> php phpdoc -o CHM:default:default -d ../lib/ -t ../docs/ > ../php_doc.log
CUrl 蜘蛛 优化 CURLOPT_HEADERFUNCTION
正搞一个小蜘蛛,用 CUrl 写的,遇到一个问题,分析完 url 后,有可能遇到 rar 或 jpg 等资源文件,也是要下载回来分析内容。
这样的做法太笨了,有很多缺点:
- 浪费带宽,文件也许很大
- 文件无用,下载回来也无法分析文件内容
怎么解决?
看浏览器会发送 Accept 指令,告诉服务器可接受的内容类型,但这不能阻止服务器返回不接受的内容,即哪怕请求的资源不在 Accept 列表中,仍然会把文件返回给客户端。
这方法不行。
想着,有没有办法,在得到服务器 response 的时候先分析 header,如果不是可接受的类型,就断掉连接,不必下载后续内容?
如果用 fsockopen 写的网络通讯接口,这一点很好实现。但 CUrl 怎么控制?
仔细看文档,找到一个选项 CURLOPT_HEADERFUNCTION。
这个选项要给一个回调函数作为参数,回调函数相当于一个触发器,CUrl 会把每一条 response 的 header 指令传给这个函数,由函数来决定如何执行后面的步骤。
回调函数要定义 2 个参数,第 1 个是 CUrl 的资源标识符,第 2 个是单独的一条 header 字符串。
如果想让 CUrl 继续下载后续内容,那么回调函数应该返回 header 字符串的长度:
return strlen($strOneHeader);
如果不想继续下载,直接返回数字 0,那么 CUrl 就会发送 TCP 的终止连接的指令,不会继续下载后续内容了。
加上这个触发器,经过测试,我的小蜘蛛可以成功地忽略非 text/html 类型的文件,大大加快了抓取速度,避免了很多问题!
小提示,给 CURLOPT_HEADERFUNCTION 设置回调函数的时候,文档上说要一个字符串形式的函数名作为参数。
这不准确。
实际上 PHP 可以指定任意类型的回调函数,可以是纯函数、类的静态方法和对象的方法:
- 纯函数:$callback = ‘my_callback_func’;
- 类常量:$callback = array(‘NAME_OF_CLASS’, ‘nameOfMethod’);
- 对象方法:$callback = array($object, ‘publicMethod’);
完。
类常量 字符串 对比 性能测试
1写一个处理 url 的 PHP 工具类,需要用到很多字符串相关的函数,比如 preg_match、preg_replace、strpos 之类的。
正则表达式和查找的 needle 字符,都是用字符加引号直接作为参数传递给上述函数,比如:
preg_match(“#^http[s]?://#i”, $strUrl);
用以匹配是否是 http 开头的 url。
记得以前看过过类常量与普通常量的性能对比,类常量快得多,在这个例子上,把字符串传给函数,是不是每次调用都是先把字符串转换成内部变量呢?如果预先定义类常量,然后以常量为参数传给函数,是不是能快一点?像下面这样:
const PREG_HTTP_URL = ‘#^http[s]?://#i’;
preg_match(self::PREG_HTTP_URL, $strUrl);
两种方式,性能肯定不一样,那种更快呢?
…
…
…
揭晓结果:
运行外层方法 50 万次各 3 次,取平均值
字符串:[31284.85]q/s [15.98]seconds
类常量:[29175.49]q/s [17.14]seconds
可以看到,用类常量的方式,性能不升反降!!
现在还不太理解为什么会这样。
xdebug xampp profiler 真是背到家了
0我在Windows环境开发PHP,用XAMPP套件,今天追查程序,看源码搞不定,求助xdebug。
想来很简单,不就在php.ini开启几个选项么,奶奶的,折腾半天没搞定!
问题是:不管怎么设置,开启,就是不输出 profiler 文件!
在网上看人家配置xdebug咋就那么顺利呢,我咋就这么费劲呢?
用中文关键字搜不到,试试英文的吧。
然后找到这个:
http://www.apachefriends.org/f/viewtopic.php?f=16&t=37137
这个天杀的 Wiedmann 估计是 ApacheFriends 的开发人员,把跟我一样可怜的 th1alb 兄弟害得够惨,2 个小时啊!!!
一切的根源居然是 Wiedmann 这怂儿把错误的配置参数包含到了 php.ini ,并打包到 xampp-1.7.2 的版本中!!!
我只能问候你母亲文明用语了!!!
给出问题解决方法,简单得要死:
把
xdebug.profiler_output_name = “xdebug_profile.%R::%u”
替换为
xdebug.profiler_output_name = “cachegrind.out.%p”
其实跟文件名前面的部分没关系,关键是那个”::”,Windows 不允许文件名中出现 “:”,在Linux下居然可以用这个字符当文件名,怎一个强字了得!
我测了下,Windows下”%R”这个占位符无效,所以,我是这么改的:
xdebug.profiler_output_name = “xdebug_profile.%u”
哦了,但愿后来的同学们能早点搜到,别再浪费宝贵的时间了!
XAMPP PHP Pecl Extension MemCache
0问题:用 xampp 套件的 php 测试程序,程序中用到了 memcache,xampp 中未编译这个 extension,因此无法运行。
如果为了 memcache,重新编译 Apache、PHP 和 MySQL,太费时间,没有收获,而且重要的是逃避了问题,没有解决。
这回找到了解决方法,可以用此方法编译其他 PECL 的 PHP Extension,使 xampp 的 PHP 有更多功能!
闲言少叙,直奔主题:给 xampp 套件的 PHP 安装 memcache 扩展。
流程:
下载 http://pecl.php.net/get/memcache-2.2.5.tgz,得到最新的 PHP memcache PECL extension。
解压后,是一些源码,不能直接 configure 和 make。
config9.m4 CREDITS memcache_consistent_hash.c memcache_queue.c memcache_standard_hash.c
config.m4 example.php memcache.dsp memcache_queue.h php_memcache.h
config.w32 memcache.c memcache.php memcache_session.c README
需要先用 phpize 配置一下,不过,我这次并不是平常的 phpize,因为我在 VMware 的 CentOS 上用的是 xampp 的集成环境,用这个环境下的 phpize 会提示缺少文件。
[leakon memcache-2.2.5]$ /opt/lampp/bin/phpize
grep: /opt/lampp/include/php/main/php.h: No such file or directory
grep: /opt/lampp/include/php/Zend/zend_modules.h: No such file or directory
grep: /opt/lampp/include/php/Zend/zend_extensions.h: No such file or directory
Configuring for:
PHP Api Version:
Zend Module Api No:
Zend Extension Api No:
提示缺少 3 个文件,这些文件在 xampp 中都没有,因为 xampp 提供的是运行环境,不是开发环境。
这样的目录结构(include/php/*)只有在安装好的 php 目录中才有,其实如果从源码直接复制更容易,不过那不是好方法。
下一个新版 PHP 源码,本地编译安装。
下载 http://cn2.php.net/get/php-5.2.11.tar.gz/from/cn.php.net/mirror。
configure 的时候不必跟上很多参数,我们只是要一个目录结构,而且是目录里面的源码,扩展和模块什么的都无所谓,只要能编译即可。
编译完成后,找到安装路径 /home/leakon/local/php5,可以看到 include 目录及其下面的目录结构。
回到 lampp 目录,下面并没有 include 目录,那我们创建一个:
[leakon ~]$ cd /opt/lampp/
[leakon lampp]$ mkdir include && cd include
[leakon include]$ ln -s /home/leakon/local/php5/include/php php
这样,建立一个指向 php 安装目录的符号链接。
再回到 memcache-2.2.5 源码目录,重新配置:
[leakon memcache-2.2.5]$ /opt/lampp/bin/phpize
Configuring for:
PHP Api Version: 20041225
Zend Module Api No: 20060613
Zend Extension Api No: 220060519
然后可以 configure 了:
[leakon memcache-2.2.5]$ ./configure –prefix=/home/leakon/local/pecl-memcache –with-php-config=/opt/lampp/bin/php-config
完成后 make 就可以啦,再完成后,编译的 so 文件生成到了 memcache-2.2.5/modules 目录下,有 2 个:
memcache.la memcache.so
memcache.so 是我们要用的。
然后,修改 php.ini,添加 extension=leakon-modules/memcache.so
这个过程,还有点小波折。
本来我把 memcache.so 放到了 /home/leakon/modules/ 下,然后设置 extension=”/home/leakon/modules/memcache.so”
结果用 php -m 找不到 memcache 模块。
我的 php 报错输出到单独的文件中,tail 那个文件,看到:
PHP Startup: Unable to load dynamic library ‘/opt/lampp/lib/php/extensions/no-debug-non-zts-20060613//home/leakon/modules/memcache.so’
原来是扩展的加载路径是相对的,不管你怎么设置,都在 /opt/lampp/lib/php/extensions/no-debug-non-zts-20060613/ 之下。
知道原因就好解决了,要么 cp memcache.so 到 no-debug-non-zts-20060613 目录下,要么做个 symbol link 链接到 /home/leakon/modules/,然后在 php.ini 修改正确的路径即可。
我记得很早写过一个编译 xdebug 的文章,刚才又看了一下,跟这回是一样的情况,也是在 xampp 的集成套件下编译 php extension。只是那次失败了,这次成功了。原文在这里:http://www.leakon.com/archives/44 。
搞了几个小时,总算时间没白费~~
memcache PECL bug fix
0使用 memcache 的过程中,遇到一个奇怪的问题,在 CentOS 环境下,PHP 的 memcache 扩展创建的客户端对象,在 delete 一个 key 的时候报错:
[25-Nov-2009 13:57:04] PHP Notice: Memcache::delete(): Server 192.168.10.131 (tcp 11211) failed with: CLIENT_ERROR bad command line format. Usage: delete <key> [noreply]
可是在 Windows 环境下,用的人家编译好的 php_memcache-cvs-20090703-5.3-VC6-x86.dll (20KB) 却没问题!
在 Google 上找了半天,终于在 PHP 官方手册的评论中发现了线索:
http://php.net/manual/en/function.memcache-delete.php
请看 10-Nov-2009 11:17 这条,简短翻译一下:
memcache 这个 PECL 扩展的 2.2.5 稳定版本有一个错误,导致在向 memcached 1.4.3 调用 delete 方法时返回 false。
用 -vvv 模式运行 memcached 将显示出为什么 delete 调用失败:
CLIENT_ERROR bad command line format. Usage: delete <key> [noreply]
简单修改一下,在这个 PECL 扩展的 memcache.c 这个文件的 1494 行的 mmc_delete() 函数中。把 command_len 修改为下面这样:
command_len = spprintf(&command, 0, “delete %s”, key);
去掉了弃用的第 3 个参数,然后 delete 就可以工作了。希望这个可以帮到你!
再提示一下改动前的代码:
// command_len = spprintf(&command, 0, “delete %s %d”, key, time);
修改完成后,重新编译出 memcache.so 即可。
Windows PHP dirname root
2遇到奇怪的问题,在 Windows 环境下,你猜 dirname(‘/’) 返回虾米?
string(1) “\”
Linux 下呢?
string(1) “/”
换个参数,dirname(‘/home/leakon/sofav.php’),如果你认为上面的不同,是因为 Windows 的目录分隔符是反斜线 “\”,那下面的结果如何解释?
Windows: string(12) “/home/leakon”
Linux: string(12) “/home/leakon”
再换参数,dirname(‘D:\Leakon\Code\sofav.php’)
Windows: string(14) “D:\Leakon\Code”
Linux: string(1) “.”
奇怪吧,为什么有这么大区别呢?
现在还不太清楚,提供一下我的测试环境:
Windows 下存在 D:\Leakon\Code\sofav.php 这个文件,Linux 下存在 /home/leakon/sofav.php。
等搞明白回来顶,看到的,用到的,留心记一下吧。
顺便说下,这个问题,是用单元测试发现的。
我知道很多开发者都懂单元测试,我也知道很多开发者并没有切身去实践单元测试。
