韩天峰博客 php基础知识学习记录
http://rango.swoole.com
写好PHP代码真的不容易,给大家几个建议:
- 慎用全局变量,全局变量不好管理的,会导致你的代码依赖于全局变量,而耦合度太高。
- 一定不要复制粘贴代码,可重用的代码一定要写成函数,或者类。等你代码多了,就知道这个多么重要了。
- 不要硬编码数据到代码中,一定要可配置化。如果是全局使用的就搞个全局config。如果仅在类中使用,请使用类静态变量配置。别直接在代码里头写
- 程序的结构化要做好,先规划一个流程。代码怎么运转的,要很清晰,有主线,从A->B->C,一眼就明白了,而且很容易修改流程和增加,替换环节。不要A直接进入B然后没踪影了,然后在B中又进入C又没踪影了。
- 搞个Trace系统,在你的代码中加入Trace来进行调试。跟踪变量的变化,这样会很方便。发布代码时,可注释掉所有Trace,只需要批量替换即可。
- 多写点单元测试的脚本,一旦更改了代码,做了大的版本更新。不需要你挨个去试,跑一下单元测试就知道是不是有的地方出问题了。
PHP的Session数据同步问题
近来公司某个域名流量大涨,发现一个外部QQ登录的接口经常失败,用户登录总是不成功。经过排查发现是登录的第一步需要保存一个token_secret。当时的程序员编写此代码时直接用了$_SESSION来保存token_secret。这里在并发量小时问题不大,并发大了之后极有可能会出问题。
假设A,B两个请求同时出发,A,B同时session_start,读取到了一个$_SESSION的值。当A完成请求写入$_SESSION,但B还没有完成请求,B完成请求后再写$_SESSION,就会覆盖A所设置的值。
类似的数据同步问题其实很多的,比如file_put_contents写PHP的Cache文件,如果不加LOCK_EX就可能会存在同步问题,当一个进程写file时,只写到一半有另外的进程去require此文件,这时就会因为文件不完整,导致PHP语法错误。所以涉及到同时读写同一个文件或数据时一定要加锁,否则在高并发的情况下会产生严重错误。
PHP5以上的版本,面向对象的功能基本上已经达到了Java C#的水平。
1、对象的传递全部采用传引用。
- $obj = new MClass;
可以把$obj任意传递到函数、对象、方法、属性,都是不会引起复制操作的,仍然是操作$obj本身。使用数据库类的操作,这一点非常好用,$db对象,可以被引用到任意的地方。模版引擎也可以,比如$smarty对象可以传递到任何地方。
通过is_object()函数可以判断是否是对象,或者是instanceof运算符,判断是否是某类。
- if(is_object($db))
- if($obj instanceof MClass)
要想传递值,拷贝对象,使用clone关键字
clone $obj;
2、private public protected 类的属性
final abstract 方法和属性的修饰符
static 静态方法和属性,可以使用self::methodName或者是MClass::methodName 来引用
如果这个类的方法中没有用到$this,这个方法没有声明为static,也可以认为是static的。
const 声明类常量,通过self::NAME或者是MClass::NAME来引用
3、继承关系
一个类可以继承一个父类,使用extends
class A{}
class B extends A{}
B就是A的子类,继承了A所有的方法和属性。B在类中也可以覆盖父类的方法和属性。
4、接口interface
interface iA
{
function setA()
{
}
}
interface iB extends iA
{
function setA()
{
}
}
声明了接口iA,和iB,iB继承了iA,接口可以继承多个父接口
class ATest implements iA{}
声明类ATest,类ATest加载了接口iA,所以类ATest必须实现iA接口的所有函数。
实例,如何通过数组访问方式来访问对象:
class Test implements ArrayAccess
{
var $_data = array();
function offsetExists($keyname)
{
return array_key_exists($keyname,$this->_data);
}
function offsetGet($keyname)
{
return $this->_data[$keyname];
}
function offsetSet($keyname,$value)
{
$this->_data[$keyname] = $value;
}
function offsetUnset($keyname)
{
unset($this->_data[$keyname]);
}
}
<div>
<div>
5、操作符和特殊方法
- parent:: 调用父类的方法和属性
- self:: 调用本类的方法和属性
- $this-> 调用对象的方法和属性
- function __construct() 构造方法,生成对象时调用
- function __toString() 转为字符串,用于echo $object。
- function __destruct() 析构方法,销毁对象时调用
- function __get() $obj->name ,获取属性不存在时,调用这个方法。
- function __set() $obj->name = $value ,设置属性不存在时,调用这个方法。
- function __call() $obj->func(),调用对象的方法不存在时,调用这个方法。
- function __clone() clone $obj,复制对象时,调用这个方法。
6、function __autoload($class_name) 自动加载类文件
通过函数__autoload可以自动require或include类的.php文件。
有上述几个功能,PHP的面向对象其实已经非常灵活了。PHP6将加入的名字空间和包,使PHP的面相对象特性更强。有点类似于脚本的C++了。
关于路线规划
我最开始工作也是在2家小公司,后来加入腾讯阿里,主要原因还是我坚持学习基础知识,从而得倒了这个机会。有几个方面的基础知识,我建议每一位PHP程序员都应该好好学习一下。我推荐几本书给大家,包括深入理解计算机系统、现代操作系统、C程序设计语言、C语言数据结构和算法、Unix环境高级编程、TCP/IP网络通信详解。另外我建议大家学习一下面向对象方面知识,PHP这方面的书不太多,建议看Java面向对象编程、Java编程思想、J2EE这些书。PHP语言基础方面,建议认真地把PHP5权威编程这本书好好读完。另外不光要读,还要照着书中的讲解动手去编程实践。
大型网站运营的技术经验分享
一、日志系统
对于一个大型网站来说,代码库非常庞大,模块众多。部门协作的人数规模在百人以上,如何跟踪定位问题不像小网站那样容易。而且我们的服务器都是集群化的,动辄几千台。有一套可查询方便使用的日志系统至关重要。
对于日志的使用也有了一定的经验。我们的日志通常会非常详细的记录各种参数,环境变量,HOST等信息,在出现异常的情况下,必须要记录日志。我们使用了MySQL按时间分片的方式来记录日志,所有集群内节点均通过网络方式来写入到中心日志系统。
在管理端我们提供了很方便友好的工具,来查询定位日志,可按用户ID、类型、时间等几种索引方式查询。日志系统帮助我们定位到了很多问题。
二、PHP错误日志收集系统
PHP错误日志是所有PHP程序问题最直接的反馈渠道。通过分析PHP日志可以发现和分析出系统现有的Bug和潜在的问题。我们通过在节点部署监控工具,实时收集PHP错误日志,Fatal Error告警。解决了很多问题
三、数据统计中心
我们有一整套数据上报的系统,系统内各种接口调用、请求响应、错误返回,都会上报到数据统计中心。我们以报表的形式展现出来,可以很方便的看到每个模块,每个接口的可用性,成功率,数据规模。根据数据,我们还做了成功率告警,当接口成功率低于某个数值,比如99.99%时就会发送短信报警。还有历史数据对比报警,当发现今日数据与往期数据差距较大时,可能是系统出现了问题,会及时进行报警。
四、硬件系统报警
我们有一套工具,来监控每个服务器节点的CPU、硬盘、内存、网卡流量信息,以及其他系统关键参数的信息。并以图表方式提供展示,方便了解服务器运行情况。每次新版本,或新功能上线,都通过这些信息来感知访问量变化,以及机器的负载情况。
五、容灾工具
当一组Server中其中一台出现问题时,会及时发现,并踢掉。这个也可以做成自动容灾,不过控制不好会发生雪崩。
关于PHP程序员解决问题的能力
这个话题老生长谈了,在面试中必然考核的能力中,我个人认为解决问题能力是排第一位的,比学习能力优先级更高。解决问题的能力既能看出程序员的思维能力,应变能力,探索能力等,又可以看出他的经验。如果解决问题能力不佳是无法通过面试的。
这里举个例子,假如我执行了一个PHP的脚本,如php test.php,预期是可以返回一个字符串。但执行后没有任何信息输出,这时候通过什么方法能知道程序错在哪里?这里可以将解决问题能力分为8个等级,越到后面的表示能力越强。
Lv0 查看PHP错误信息
程序没有达到预期效果,证明代码出错了,看PHP的错误信息是第一步。如果直接忽略错误信息,表明这个人不适合担任专业的程序员岗位。有些情况下php.ini配置中关闭了错误显示,需要修改php.ini打开错误信息,或者错误信息被导出到了日志文件,这种情况可以直接tailf php_error.log来看错误信息。
拿到错误信息后直接定位到程序代码问题,或者到Google/百度搜索,即可解决问题。
注:打开错误显示的方法是
- php.ini中display_errors / display_startup_errors 设置为On
- php.ini中error_reporting 设置为E_ALL
- PHP代码中设置error_reporting(E_ALL)
Lv1 存在多个版本的php或php-cli与php-fpm加载不同的配置
存在多个版本的php,懂得通过which php来看是哪个PHP,或者加绝对路径制定php版本。表示此PHPer通过了此层级的50%考验。
另外一个情况就是php-cli与php-fpm得到的执行情况不一样,如在web浏览器中执行是对的,cli下执行是错的。这时候可能是2个环境加载的php.ini不同所致。cli下通过php -i |grep php.ini得到加载了哪个php.ini。而fpm下通过phpinfo()函数可以得到php.ini的绝对路径。
Lv2 var_dump/die打印变量值信息单步调试
这是惯用的程序调试手段,也是最简单粗暴有效的解决问题方法。高级一点的手段是使用PHP的Trace类/日志类,花哨一点的可以借助phpstorm+xdebug在IDE工具里进行Debug。
Trace工具还可以分析脚本的耗时,进行PHP程序的性能优化。
这3个考验全部通过,表明此程序员已经具备了专业PHP程序员应该有的解决问题能力了。PHP程序员只要过了这个等级,就足以应多大部分情况,在中小型网站中毫无压力。
Lv3 使用strace工具跟踪程序执行
strace可以用来查看系统调用的执行,使用strace php test.php,或者strace -p 进程ID。strace就可以帮助你透过现象看本质,掌握程序执行的过程。这个手段是在大型网站,大公司里最常用的。如果没掌握strace,这里只能说抱歉了,我们不接受不会strace的PHPer。
strace其实也是对程序员基础的考验,如果不懂操作操作系统,完全不懂底层,肯定也达不到会用strace的程度。当然strace对于PHP代码里的死循环是解决不了的。比如你发现一个php-fpm进程CPU100%了,strace恐怕是解决不了的。因为strace是看系统调用,一般都是IO类操作,既然是IO密集,那CPU一定不可能是100%。
Lv4 使用tcpdump工具分析网络通信过程
tcpdump可以抓到网卡的数据通信过程,甚至数据内容也可以抓到。使用tcpdump可以看到网络通信过程是什么样的,如何时发起了TCP SYN3次握手,何时发送FIN包,何时发送RST包。这是一个基本功,如果不懂tcpdump,证明不具备网络问题解决能力。
Lv5 统计函数调用的耗时和成功率
使用xhporf/xdebug导出PHP请求的调用过程,然后分析每个函数调用的过程和耗时。能够分析PHP程序的性能瓶颈,找出可以优化的点。
另外一个对于网络服务的调用,如mysql查询,curl,其他API调用等,通过记录起始和结束时microtime,返回的是不是false,可以得到调用是否成功,耗时多少。如果可以汇总数据,整理出调用的成功率,失败率,平均延时,证明此程序员对接口质量敏感,有大型网站项目经验。
Lv6 gdb使用
gdb是C/C++调试程序的利器,需要具备一定C/C++功底的程序员才会能熟练使用gdb。上面说的strace无法跟踪php程序CPU100%,而gdb是可以跟踪的。另外gdb也可以解决php程序core dump的问题。
通过gdb -p 进程ID,再配合php-src的.gdbinit zbacktrace等工具,可以很方便地跟踪PHP程序的执行。像上面的CPU100%往往是PHP程序中发生死循环了,gdb进行多次查看,就大致可以得到死循环的位置。具备gdb解决问题能力的PHP程序员少之又少。如果能使用gdb解决PHP问题,这个PHPer百分之百可以通过面试,并且可以拿到较高的技术评级。
Lv7 查看PHP内核和扩展源码
如果能熟悉PHP内核和扩展的源码,遇到PHP程序中最复杂的内存错误,也可以有解决的能力。这类PHP程序员就是凤毛麟角了。配合gdb工具和对PHP源码的熟悉,可以查看opcode的信息,execute_data的内存,全局变量的状态等。