DZ3.3论坛程序

discuz运行机制

<!–{eval print_r($handcarUserinfo[‘yphoto’]);die(‘###’);}–>

 

最后让我们总结一下当网站收到用户的请求home.php?mod=space&do=blog时,网站程序内部分派工作的次序
1 home.php
2 分派给 source\module\home\home_space.php
3 分派给 source\include\space\space_blog.php
4 数据取自source\class\table\table_home_blog.php
5 格式定义在 template\default\home\space_blog_list.htm
从这些文件的内容作用看,Discuz是按照MVC 架构模式将程序分割成若干逻辑部件:模型-视图-控制器(Model-View-Controller) 。source\include\space\space_blog.php属于服务层,source\class\table\table_home_blog.php属于数据层,二者合起来组成了模型层,template\default\home\space_blog_list.htm属于视图层,而home.phpsource\module\home\home_space.php属于控制器层

Dz用户头像

echo avatar($uid,small)

diy修改用的模板目录

D:\xampp\htdocs\zhihubbs\data\template\1_diy_forum_viewthread.tpl.php

Action_source的解释

action source 有以下几种:

asc

asc_after

clm

clm_login_after

my

saic

saic_after

asc开头是asc用户,CLM开头是CLM用户。my 、saic 、saic_after  这三个是大通员工

DZ插件开发开启

$_config[‘plugindeveloper’] = 0;

$_config[‘plugindeveloper’] = 1;开启插件设计者模式

$_config[‘plugindeveloper’] = 2;开启设计插件功能和显示嵌入点

 

mysql常用配置

mysql运行、访问记录,错误等日志

1.首先确认你日志是否启用了

mysql>show variables like ‘log_bin’;

2.如果启用了,即ON

那日志文件就在mysql的安装目录的data目录下

3.怎样知道当前的日志

mysql>show master status;                                                                                                                                                

4.看二进制日志文件用mysqlbinlog

shell>mysqlbinlog mail-bin.000001

或者shell>mysqlbinlog mail-bin.000001 | tail

Windows 下用类似的。

Windows下Mysql 配置文件my.ini

#在终端中查看当前的错误日志配置,缺省情况下位于数据目录

mysql> show variables like ‘log_error‘;

查看其他变量同理

 

apache常用配置

apache错误日志路径

配置文件httpd.conf中ErrorLog “logs/error.log”

ErrorLog 可能指向你 Linux 系统中的任意位置

Apache 访问日志配置

在apache的配置文件httpd.conf中,有一行是这么配置的:

CustomLog “logs/access.log” common

 

php常用配置总结

Php在代码中开启报错

ini_set(‘display_errors’,0);
error_reporting ( E_ALL  ^  E_NOTICE );

php查看错误日志和设置日志位置

php.ini中开启

display_errors = On

error_log = D:\phpStudy\php53\php_errors.log

配置Nginx前端Apache后端服务器LNMPA-与LNMP,HHVM性能比拼

Apache和Nginx是当今最流行的两个Web服务器软件,Apache用户基础大,稳定,各种兼容。但是Nginx与其相比,在高并发下nginx能保持低资源低消耗,支持更多的并发连接,体现更高的效率,所以很多人愿意使用Nginx来作Web服务器。

但是Nginx也有劣势,虽然在静态的反向方面Nginx表现优秀,但是在处理PHP动态请求方面不如Apache,并且在操作使用方面不如Apache方便,例如Wordpress一些插件就只能在Apache上起作用。Nginx一个最常见的问题出现访问502错误。

事实上很大型的网站都是采用Nginx前端Apache后端的服务器架构,这样可以很好地结合了Nginx高并发和静态页面高效率以及Apache稳定的动态页面处理特点,再也不用担心Nginx以FastCGI模式运行PHP时的502问题和Apache处理静态页面过慢、负载过高的问题。

本篇文章将介绍Nginx前端Apache后端服务器LNMPA配置方法,同时采用简单的服务器压力测试比较一下LNMPA、LNMP、HHVM性能表现的优异。更多的有关于服务器架构的教程有:

配置Nginx前端Apache后端服务器LNMPA-与LNMP,HHVM性能比拼

一、Nginx前端Apache后端LNMPA一键快速安装

1、Nginx前端Apache后端的操作流程是在服务器上安装好Nginx和Apache,让Nginx监听80端口,Apache监听非80端口,让Nginx过滤PHP请求给Apache处理,而Nginx只负载前台的静态页面请求处理。

LNMPA监听端口

2、Nginx前端Apache后端配置起来点复杂,不想折腾的朋友可以直接使用Nginx前端Apache后端服务器LNMPA一键安装包:

  • 1、官方网站:https://lnmp.org/lnmpa.html

3、LNMPA是LNMP的扩展,需要先按LNMP安装教程安装好LNMP后,在LNMP压缩包解压后的目录里再执行./apache.sh 开始安装。LNMP相信大家已经非常地熟悉了,新版的LNMP集成了Redis、OPcache、MariaDB等新的特性。安装时增加MariaDB选项。

新版LNMP增加的功能

4、LNMP安装与使用可以参考:LNMP新版VPS主机控制面板安装

LNMP成功安装

5、LNMP安装成功后可以在探针中看到服务器引擎是Nginx。

LNMP看到Nginx引擎

6、在LNMP上安装好Wordpress后占用内存大概是170MB,测试环境是阿里云服务器,内存为512MB,CPU为1核。

LNMP安装好占用的内存

7、进入LNMP压缩包解压后的目录里再执行./apache.sh 开始安装。

LNMPA一键安装

8、会提示输入邮箱,回车确认后将会自动开始安装。低于1G内存不建议使用LNMPA,升级到LNMPA后无法降回LNMP。

LNMPA输入邮箱安装

9、看到如下提示就表示Nginx前端Apache后端服务器LNMPA安装成功了。

LNMPA安装成功

10、Wordpress运行在LNMPA内存占用大概是135MB。

LNMPA安装后占用的内存

11、探针中显示的服务器引擎已经变成了Apache。

LNMPA服务器引擎变成Apache

12、LNMPA添加虚拟主机:/root/vhost.sh  。删除虚拟主机,ssh执行:rm /usr/local/nginx/conf/vhost/域名.conf 同时需要:rm /usr/local/apache/conf/vhost/域名.conf 。升级LNMPA PHP版本:LNMP压缩包解压后的目录里执行:./upgrade_lnmpa_php.sh

二、LNMPA与LNMP服务器性能比较

1、在同样的VPS主机配置上,安装运行Wordpress,测试方法采用简单的压力测试:网站服务器压力Web性能测试:Apache Bench,Webbench,http_load安装使用

2、在LNMP环境下,Webbench在10秒内同时发起10个连接请求,显示结果是510页面/分钟,95083 字节/秒。

LNMP压力测试

3、此时查看到的VPS负载最高是5。

LNMP服务器压力值

4、继续将连接增加到20个,此时服务器是648页面/分钟,110336 字节/秒。

LNMP增加到20个连接

5、观察到此刻服务器负载最高是11。

LNMP服务器压力是11

6、继续将连接增加到50个,此时服务器是930页面/分钟,135096 字节/秒。

LNMP增加服务器压力50

7、此时系统负载最高是22。

LNMP系统负载

8、在Nginx前端Apache后端服务器LNMPA情况下,10个连接,显示是240页面/分钟,48392字节/秒。

LNMPA压力测试

9、此时服务器负载压力显示为2.3,比LNMP时的5少了近一半。

LNMPA压力比LNMP少了一半

10、增加到了20个连接后,显示为300页面/分钟,54131字节/秒。

LNMPA增加压力测试

11、此时服务器负载压力显示为3.84,比LNMP时的11少了近2/3。

LNMPA服务器承受力增强

12、将连接增加到了50个后,服务器显示为288页面/分钟,58070字节/秒。

LNMPA增加到50个连接

13、此时VPS的压力值为8,比LNMP环境少了近3/5。

LNMPA处理更多的请求

三、LNMPA与HHVM性能比拼

1、HHVM是由著名脸谱公司开发的PHP虚拟机,安装教程:HHVM安装使用教程-高效的PHP运行环境提升PHP性能9倍以上

2、当我把连接增加到200个时,LNMPA环境下的VPS显示4050页面/分钟,620411字节/秒。

LNMPA最多承受压力

2、系统负载压力最向达到170,并且直接宕机。

LNMPA服务器宕机

3、而在HHVM环境下,当连接是10个时,服务器是481548页面/分钟,2905267字节/秒。请注意该数值几值是100倍以上。

HHVM的压力测试

4、而服务器压力仅为1.3,是LNMPA环境下的一半,LNMP环境下的1/5。

HHVM更小的系统负载

5、当我把连接增加到200后,HHVM环境显示520920页面/分钟,3142449字节/秒。

HHVM更强的页面处理能力

6、而VPS主机的系统负载是28,且服务器仍然可以正常被访问。

HHVM服务器很稳定

四、Nginx前端Apache后端服务器LNMPA安装使用小结

1、LNMPA官方建议服务器的内存最少在1GB以上,本篇文章部落用的是阿里云的512MB单核CPU主机,可能没有发挥出LNMPA的优势,但是从与LNMP的对比中仍然可以看出LNMPA的架构存在一定的优势。

2、但是这种“优势”在HHVM面前突然变得那么“渺小”了,512Mb的VPS主机上,从Webbench测试的结果就能看出HHVM在处理请求时的能力有多强,而系统负载居然没有超过30。追求速度的朋友抛弃LNMP直接用HHVM吧。

HHVM性能强大

文章出自:免费资源部落 https://www.freehao123.com/ 版权所有。本站文章除注明出处外,皆为作者原创文章,可自由引用,但请注明来源

php中static静态类与static 静态变量用法区别

static 是定义一个静态对象或静态变量,关于static 定义的变量或类方法有什么特性我们看完本文章的相关实例后就见分晓了。

1. 创建对象$object = new Class(),然后使用”->”调用:$object->attribute/function,前提是该变量/方法可访问。
2. 直接调用类方法/变量:class::attribute/function,无论是静态/非静态都可以。但是有前提条件:
A. 如果是变量,需要该变量可访问。
B. 如果是方法,除了该方法可访问外,还需要满足:
b1) 如果是静态方法,没有特殊条件;
b2) 如果是非静态方法,需要改方法中没有使用$this,即没有调用非静态的变量/方法,当然,调用静态的变量/方法没有问题。

然后我们再看一下使用$object->… 和使用class::… 都有什么区别:
1. 使用$object->… ,需要执行构造函数创建对象;
2. 使用class::… 调用静态方法/变量,不需要执行构造函数创建对象;
3. 使用class::… 调用非静态方法/变量,也不需要执行构造函数创建对象。

然后奇怪的地方就出来了,既然2和3都一样,那静态方法/变量存在还有什么意义呢?
静态 static
声明类成员或方法为 static ,就可以不实例化类而直接访问,不能通过一个对象来访问其中的静态成员(静态方法除外)。静态成员属于类,不属于任何对象实例,但类的对象实例都能共享

  1. <?php  
  2. Class Person{  
  3.     // 定义静态成员属性  
  4.     public static $country = “中国”;  
  5.     // 定义静态成员方法  
  6.     public static function myCountry() {  
  7.         // 内部访问静态成员属性  
  8.         echo “我是”.self::$country.“人<br />”;  
  9.     }  
  10. }  
  11. class Student extends Person {  
  12.     function study() {  
  13.         echo “我是”. parent::$country.“人<br />”;  
  14.     }  
  15. }  
  16. // 输出成员属性值  
  17. echo Person::$country.“<br />”;  // 输出:中国  
  18. $p1 = new Person();  
  19. //echo $p1->country;   // 错误写法  
  20. // 访问静态成员方法  
  21. Person::myCountry();   // 输出:我是中国人  
  22. // 静态方法也可通过对象访问:  
  23. $p1->myCountry();  
  24. // 子类中输出成员属性值  
  25. echo Student::$country.“<br />”// 输出:中国  
  26. $t1 = new Student();  
  27. $t1->study();    // 输出:我是中国人  
  28. ?>  
  1. 运行该例子,输出:  
  2. 中国  
  3. 我是中国人  
  4. 我是中国人  
  5. 中国  
  6. 我是中国人  
  7. 小结  
  8. 在类内部访问静态成员属性或者方法,使用 self::(注意不是 $slef),如:  
  9.  代码如下   复制代码  
  10. slef:: $country  
  11. slef:: myCountry()  
  12. 在子类访问父类静态成员属性或方法,使用 parent::(注意不是 $parent),如:  
  13.  代码如下   复制代码  
  14. parent:: $country  
  15. parent:: myCountry()  
  16. 外部访问静态成员属性和方法为 类名/子类名:: ,如:  
  17.  代码如下   复制代码  
  18. Person::$country  
  19. Person::myCountry()  
  20. Student::$country  
  21. 但静态方法也可以通过普通对象的方式访问。  
  22.   
  23. 例子 声明静态变量  
  24.  代码如下   复制代码  
  25. <?PHP  
  26.    function foo(){  
  27.    static $int = 0;// correct  
  28.    static $int = 1+2;   // wrong (as it is an expression)  
  29.    static $int = sqrt(121); // wrong (as it is an expression too)  
  30.      
  31.    $int++;  
  32.    echo $int;  
  33.    }  
  34.    ?>  
  35. 例子 使用静态变量的例子  
  36.  代码如下   复制代码  
  37. <?PHP  
  38.    function Test()  
  39.    {  
  40.    static $w3sky = 0;  
  41.    echo $w3sky;  
  42.    $w3sky++;  
  43.    }  
  44.    ?>  
  45. 现在,每次调用 Test() 函数都会输出 $w3sky 的值并加一。  
  46.   
  47. 静态变量也提供了一种处理递归函数的方法。递归函数是一种调用自己的函数。写递归函数时要小心,因为可能会无穷递归下去。必须确保有充分的方法来中止递归。一下这个简单的函数递归计数到 10,使用静态变量 $count 来判断何时停止:  
  48. 例子 静态变量与递归函数  
  49.  代码如下   复制代码  
  50. <?PHP  
  51.    function Test()  
  52.    {  
  53.    static $count = 0;  
  54.      
  55.    $count++;  
  56.    echo $count;  
  57.    if ($count < 10) {  
  58.    Test();  
  59.    }  
  60.    $count–;  
  61.    }  
  62.    ?>  
  63. 注: 静态变量可以按照上面的例子声明。如果在声明中用表达式的结果对其赋值会导致解析错误  

可以这样去理解group by和聚合函数

写在前面的话:用了好久group by,今天早上一觉醒来,突然感觉group by好陌生,总有个筋别不过来,为什么不能够select * from Table group by id,为什么一定不能是*,而是某一个列或者某个列的聚合函数,group by 多个字段可以怎么去很好的理解呢?不过最后还是转过来了,简单写写吧,大牛们直接略过吧。

=========正文开始===========

先来看下表1,表名为test:

表1

执行如下SQL语句:

SELECT name FROM test
GROUP BY name

你应该很容易知道运行的结果,没错,就是下表2:

表2

可是为了能够更好的理解 “group by”多个列“”聚合函数“ 的应用,我建议在思考的过程中,由表1到表2的过程中,增加一个虚构的中间表: 虚拟表3 。下面说说如何来思考上面SQL语句执行情况:

1.FROM test:该句执行后,应该结果和表1一样,就是原来的表。

2.FROM test Group BY name:该句执行后,我们想象生成了 虚拟表3, 如下所图所示,生成过程是这样的:group by name,那么找name那一列,具有相同name值的行,合并成一行,如对于name值为aa的,那么<1 aa 2>与<2 aa 3>两行合并成1行,所有的id值和number值写到一个单元格里面。

3.接下来就要针对 虚拟表3 执行Select语句了:

(1)如果执行select *的话,那么返回的结果应该是虚拟表3,可是id和number中有的单元格里面的内容是多个值的,而关系数据库就是基于关系的,单元格中是不允许有多个值的,所以你看,执行select * 语句就报错了。

(2)我们再看name列,每个单元格只有一个数据,所以我们select name的话,就没有问题了。为什么name列每个单元格只有一个值呢,因为我们就是用name列来group by的。

(3)那么对于id和number里面的单元格有多个数据的情况怎么办呢?答案就是用聚合函数,聚合函数就用来输入多个数据,输出一个数据的。 如cout(id),sum(number),而每个聚合函数的输入就是每一个多数据的单元格。

(4)例如我们执行select name,sum(number) from test group by name,那么sum就对虚拟表3的number列的每个单元格进行sum操作,例如对name为aa的那一行的number列执行sum操作,即2+3,返回5,最后执行结果如下:

(5)group by 多个字段该怎么理解呢:如group by name,number,我们可以把name和number 看成一个 整体字段 ,以他们整体来进行分组的。如下图

(6)接下来就可以配合select和聚合函数进行操作了。如执行select name,sum(id) from test group by name,number,结果如下图:

至此,我已经对我自己对如此简单的问题有如此天马行空的想法所折服,洗洗睡觉

Session变量不能传送到下一页.解决: session.use_trans_sid = 1

在PHP中使用过SESSION的朋友可能会碰到这么一个问题,SESSION变量不能跨页传递。这令我苦恼了好些日子,最终通过查资料思考并解决了这个问题。我认为,出现这个问题的原因有以下几点:
1、客户端禁用了cookie
2、浏览器出现问题,暂时无法存取cookie
3、php.ini中的session.use_trans_sid = 0或者编译时没有打开–enable-trans-sid选项

为什么会这样呢?下面我解释一下:

Session储存于服务器端(默认以文件方式存储session),根据客户端提供的session id来得到用户的文件,取得变量的值,session id可以使用客户端的Cookie或者Http1.1协议的Query_String(就是访问的URL的“?”后面的部分)来传送给服务器,然后服务器读取Session的目录……。也就是说,session id是取得存储在服务上的session变量的身份证。当代码session_start();运行的时候,就在服务器上产生了一个session文件,随之也产生了与之唯一对应的一个session id,定义session变量以一定形式存储在刚才产生的session文件中。通过session id,可以取出定义的变量。跨页后,为了使用session,你必须又执行session_start();将又会产生一个session文件,与之对应产生相应的session id,用这个session id是取不出前面提到的第一个session文件中的变量的,因为这个session id不是打开它的“钥匙”。如果在session_start();之前加代码session_id($session id);将不产生新的session文件,直接读取与这个id对应的session文件。

PHP中的session在默认情况下是使用客户端的Cookie来保存session id的,所以当客户端的cookie出现问题的时候就会影响session了。必须注意的是:session不一定必须依赖cookie,这也是session相比cookie的高明之处。当客户端的Cookie被禁用或出现问题时,PHP会自动把session id附着在URL中,这样再通过session id就能跨页使用session变量了。但这种附着也是有一定条件的,即“php.ini中的session.use_trans_sid = 1或者编译时打开打开了–enable-trans-sid选项”。

用过论坛的朋友都知道,在进入论坛的时候,往往会提示你检查Cookie是否打开,这是因为大多数论坛都是基于Cookie的,论坛用它来保存用户名、密码等用户信息,方便使用。而且很多朋友都认为Cookie不安全(其实不是这样),往往禁用它。其实在PHP程序中,我们完全可以用SESSION来代替Cookie,它可以不依赖于客户端是否开启Cookie。

所以,我们可以抛开cookie使用session,即假定用户关闭cookie的情况下使用session,其实现途径有以下几种:

1、设置php.ini中的session.use_trans_sid = 1或者编译时打开打开了–enable-trans-sid选项,让PHP自动跨页传递session id。
2、手动通过URL传值、隐藏表单传递session id。
3、用文件、数据库等形式保存session_id,在跨页过程中手动调用。

途径1举例说明:

s1.php

<?php
session_start();
$_SESSION[’var1’]=”中华人民共和国”;
$url=”<a href=”.””s2.php”>下一页</a>”;
echo $url;
?>

s2.php

<?php
session_start();
echo “传递的session变量var1的值为:”.$_SESSION[’var1’];
?>

运行以上代码,在客户端cookie正常的情况下,应该可以在得到结果“中华人民共和国”。
现在你手动关闭客户端的cookie,再运行,可能得不到结果了吧。如果得不到结果,再“设置php.ini中的session.use_trans_sid = 1或者编译时打开打开了–enable-trans-sid选项”,又得到结果“中华人民共和国”

 

途径2举例说明:

s1.php

<?php
session_start();
$_SESSION[’var1’]=”中华人民共和国”;
$sn = session_id();
$url=”<a href=”.””s2.php?s=”.$sn.””>下一页</a>”;
echo $url;
?>

s2.php

<?php
session_id($_GET[’s’]);
session_start();
echo “传递的session变量var1的值为:”.$_SESSION[’var1’];
?>

隐藏表单的方法基本原理同上。

途径3举例说明:

login.html

<!DOCTYPE HTML PUBLIC “-//W3C//DTD HTML 4.01 Transitional//EN”>
<html>
<head>
<title>Login</title>
<meta http-equiv=”Content-Type” content=”text/html; charset=gb2312″>
</head>
<body>
请登录:
<form name=”login” method=”post” action=”mylogin1.php”>
用户名:<input type=”text” name=”name”><br>
口 令:<input type=”password” name=”pass”><br>
<input type=”submit” value=”登录”>
</form>
</body>
</html>

mylogin1.php

<?php

$name=$_POST[’name’];
$pass=$_POST[’pass’];
if(!$name || !$pass) {
echo “用户名或密码为空,请<a href=”login.html”>重新登录</a>”;
die();
}
if (!($name==”youngong” && $pass==”123″) {
echo “用户名或密码不正确,请<a href=”login.html”>重新登录</a>”;
die();
}
//注册用户
ob_start();
session_start();
$_SESSION[’user’]= $name;
$psid=session_id();
$fp=fopen(“e:/tmp/phpsid.txt”,”w+”;
fwrite($fp,$psid);
fclose($fp);
//身份验证成功,进行相关操作
echo “已登录<br>”;
echo “<a href=”mylogin2.php”>下一页</a>”;

?>

mylogin2.php

<?php
$fp=fopen(“e:/tmp/phpsid.txt”,”r”;
$sid=fread($fp,1024);
fclose($fp);
session_id($sid);
session_start();
if(isset($_SESSION[’user’]) && $_SESSION[’user’]=”laogong” {

echo “已登录!”;
}
else {
//成功登录进行相关操作
echo “未登录,无权访问”;
echo “请<a href=”login.html”>登录</a>后浏览”;
die();
}

?>

同样请关闭cookie测试,用户名:youngong 密码:123 这是通过文件保存session id的,文件是:e: mpphpsid.txt,请根据自己的系统决定文件名或路径。

至于用数据库的方法,我就不举例子了,与文件的方法类似。

总结一下,上面的方法有一个共同点,就是在前一页取得session id,然后想办法传递到下一页,在下一页的session_start();代码之前加代码session_id(传过来的session id);
====================================================================

还有,我的php.ini文件存放在两个地方 1.c:/php4/php.ini     2.c:/winnt/php.ini ,,而我的程序好像只认识 c:/php4/php.ini,对于 winnt 下的php.ini 改动不起作用.!!!
在不清楚哪一个php.ini起作用的时候最好同时改动两个文件,虽然是麻烦,但能解决问题

php中的static关键字总结

静态成员是一种类变量,可以把它看成时属于整个类而不是属于类的某个实例。与一般的实例变量不同的是,静态成员只保留一个变量值,而这个变量值对所有的实例都是有效的,也就是说,所有的实例共享这个成员。

$this只表示类的当前实例,而 self:: 表示的是类本身,在类之外的代码中不能使用这个操作符,而且它不能识别自己在继承树层次结构中的位置。也就是说,在扩展类中使用self 作用域时,self 可以调用基类中声明的方法,但它调用的总是已经在扩展类中重写的方法。与$this不同的是,在使用静态变量时,必须在作用域限定符后面加上$符号。

在扩展类中,在基类的方法被重写的情况下,使用 parent 作用域调用定义在基类中的方法。静态成员也可以只属于父类。如果在子类和父类中同时声明了某个成员,也可以使用parant::在子类中访问父类中的变量。在这种情况下,父类的静态成员和子类的静态成员保存的是不同的值。

可以在 :: 操作符的左边写上类的名称来静态地访问某个成员,这样避免创建类的实例。不仅省略掉实例化类的代码,而且还会更高效,因为类的每个实例都会占用一小部分的系统资源。

在使用 :: 操作符访问成员变量时,需要再次注意对$符号的使用。因为PHP当前不支持动态的静态变量的使用,也就是说不支持可变的静态变量。在使用$this->$var时,被访问的成员是包含在$var中的变量的值。而不用$符号访问某个变量实际上查找的是类的某个常量,而常量是不能通过$this来访问的。

PHP6中提出的static::作用域使我们不再需要使用self::和parent::。当希望指向最终的实现功能的类时,就可以使用static::,这个限定符会在代码执行之前立即计算出继承层次机构上最后那个类的成员。之一过程被称为延迟绑定,它使我们可以在子类中重写某个静态变量,并且也可以从某个在父类中声明的函数中反问这个最终成员。

有时,可能有必要创建供所有类实例共享的字段和方法,这些字段和方法与所有的类实例有关,但不能由任何特定对象调用。例如,假设要编写一个类,跟踪网页访问者的数量。你一定不希望每次实例化该类时都把访问者数量重置为0 ,此时就可以将该字段设置为static作用域:

  1. <?php
  2.     class visitors
  3.     {
  4.         private static $visitors = 0;
  5.         function __construct()
  6.         {
  7.              self::$visitors++;
  8.         }
  9.         static function getVisitors()
  10.         {
  11.             return self::$visitors;
  12.         }
  13.     }
  14.     /* Instantiate the visitors class. */
  15.     $visits = new visitors();
  16.     echo visitors::getVisitors().“<br />”;
  17.     /* Instantiate another visitors class. */
  18.     $visits2 = new visitors();
  19.     echo visitors::getVisitors().“<br />”;
  20. ?>

程序运行结果:

1

2

因为$visitors字段声明为static,所以对其值的任何改变都会反映到所有实例化对象中。还要注意,静态字段和方法应使用self关键字和类名来引用,而不是通过this和箭头操作符。这是因为使用“正常”方法引用静态字段是不可能的,会导致语法错误。

不能在类中使用$this来引用为static字段。

静态变量
静态变量是只存在于函数作用域的变量,不过,在函数执行完成后,这种变量的值不会丢失,也就是说,在下一次调用这个函数时,变量仍然会记得原来的值。要将某个变量定义为静态的,只需要在变量前加上 static 关键字即可。

在类中,static关键字有两种主要用法,一是用来定义静态成员,一是用来定义静态方法。在类的内部,可以使用作用域限定符 (::) 来访问不同层次作用域的变量。

静态方法
静态方法和非静态方法之间有一个重要的区别:在调用静态方法时,不再需要拥有类的实例。

静态方法和非静态方法使用原则:一是如果某个方法中不包含$this变量,就应该时静态方法;如果不需要类的实例,可能还应该使用静态类,这样可以免去实例化类的工作。另,在静态方法中时不能使用$this变量的,因为静态方法不属于某个特定的实例。关于 php中static静态类与static 静态变量用法区别 请参阅 php中static静态类与static 静态变量用法区别

PHP的错误机制总结

PHP的错误机制总结

PHP的错误机制也是非常复杂的,做了几年php,也没有仔细总结过,现在就补上这一课。

特别说明:文章的PHP版本使用5.5.32

PHP的错误级别

首先需要了解php有哪些错误。截至到php5.5,一共有16个错误级别

注意:尝试下面的代码的时候请确保打开error_log:

error_reporting(E_ALL);
ini_set('display_errors', 'On');

E_ERROR

这种错误是致命错误,会在页面显示Fatal Error, 当出现这种错误的时候,程序就无法继续执行下去了

错误示例:

// Fatal error: Call to undefined function hpinfo() in /tmp/php/index.php on line 5
hpinfo();  //E_ERROR

注意,如果有未被捕获的异常,也是会触发这个级别的。

// Fatal error: Uncaught exception 'Exception' with message 'test exception' in /tmp/php/index.php:5 Stack trace: #0 {main} thrown in /tmp/php/index.php on line 5
throw new \Exception("test exception");

E_WARNING

这种错误只是警告,不会终止脚本,程序还会继续进行,显示的错误信息是Warning。比如include一个不存在的文件。

//Warning: include(a.php): failed to open stream: No such file or directory in /tmp/php/index.php on line 7
//Warning: include(): Failed opening 'a.php' for inclusion (include_path='.:/usr/share/pear:/usr/share/php') in /tmp/php/index.php on line 7
include("a.php"); //E_WARNING

E_NOTICE

这种错误程度更为轻微一些,提示你这个地方不应该这么写。这个也是运行时错误,这个错误的代码可能在其他地方没有问题,只是在当前上下文情况下出现了问题。

比如$b变量不存在,我们把它赋值给另外一个变量

//Notice: Undefined variable: b in /tmp/php/index.php on line 9
$a = $b; //E_NOTICE

E_PARSE

这个错误是编译时候发生的,在编译期发现语法错误,不能进行语法分析。

比如下面的z没有设置为变量。

// Parse error: syntax error, unexpected '=' in /tmp/php/index.php on line 20
z=1; // E_PARSE

E_STRICT

这个错误是PHP5之后引入的,你的代码可以运行,但是不是PHP建议的写法。

比如在函数形参传递++符号

// Strict Standards: Only variables should be passed by reference in /tmp/php/index.php on line 17
function change (&$var) {
  $var += 10;
}

$var = 1;
change(++$var);
// E_STRICT

E_RECOVERABLE_ERROR

这个级别其实是ERROR级别的,但是它是期望被捕获的,如果没有被错误处理捕获,表现和E_ERROR是一样的。

经常出现在形参定义了类型,但调用的时候传入了错误类型。它的错误提醒也比E_ERROR的fatal error前面多了一个Catachable的字样。

//Catchable fatal error: Argument 1 passed to testCall() must be an instance of A, instance of B given, called in /tmp/php/index.php on line 37 and defined in /tmp/php/index.php on line 33
class A {
}

class B {
}

function testCall(A $a) {
}

$b = new B();
testCall($b);

E_DEPRECATED

这个错误表示你用了一个旧版本的函数,而这个函数后期版本可能被禁用或者不维护了。

比如curl的CURLOPT_POSTFIELDS使用\@FILENAME来上传文件的方法

// Deprecated: curl_setopt(): The usage of the @filename API for file uploading is deprecated. Please use the CURLFile class instead in /tmp/php/index.php on line 42
$ch = curl_init("http://www.remotesite.com/upload.php");
curl_setopt($ch, CURLOPT_POSTFIELDS, array('fileupload' => '@'. "test"));

E_CORE_ERROR, E_CORE_WARNING

这两个错误是由PHP的引擎产生的,在PHP初始化过程中发生。

E_COMPILE_ERROR, E_COMPILE_WARNING

这两个错误是由PHP引擎产生的,在编译过程中发生。

E_USER_ERROR, E_USER_WARNING, E_USER_NOTICE, E_USER_DEPRECATED,

这些错误都是用户制造的,使用trigger_error,这里就相当于一个口子给用户触发出各种错误类型。这个是一个很好逃避try catch异常的方式。

trigger_error("Cannot divide by zero", E_USER_ERROR);
// E_USER_ERROR
// E_USER_WARING
// E_USER_NOTICE
// E_USER_DEPRECATED

E_ALL

E_STRICT出外的所有错误和警告信息。

错误控制

php中有很多配置和参数是可以控制错误,以及错误的日志显示的。第一步,我们需要了解的是php中的有关错误的配置有哪些?

我们按照php+php-fpm的模型来说,会影响php错误显示的其实是有两个配置文件,一个是php本身的配置文件php.ini,另外一个是php-fpm的配置文件,php-fpm.conf。

php.ini中的配置

error_reporting = E_ALL  // 报告错误级别,什么级别的
error_log = /tmp/php_errors.log // php中的错误显示的日志位置
display_errors = On // 是否把错误展示在输出上,这个输出可能是页面,也可能是stdout
display_startup_errors = On // 是否把启动过程的错误信息显示在页面上,记得上面说的有几个Core类型的错误是启动时候发生的,这个就是控制这些错误是否显示页面的。
log_errors = On // 是否要记录错误日志
log_errors_max_len = 1024 // 错误日志的最大长度
ignore_repeated_errors = Off // 是否忽略重复的错误
track_errors = Off // 是否使用全局变量$php_errormsg来记录最后一个错误
xmlrpc_errors = 0 //是否使用XML-RPC的错误信息格式记录错误
xmlrpc_error_number = 0 // 用作 XML-RPC faultCode 元素的值。
html_errors = On  // 是否把输出中的函数等信息变为HTML链接
docref_root = http://manual/en/ // 如果html_errors开启了,这个链接的根路径是什么
fastcgi.logging = 0 // 是否把php错误抛出到fastcgi中

我们经常会被问到,error_reporting和display_errors有什么区别呢?这两个函数是完全不一样的。
PHP默认是会在日志和标准输出(如果是fpm模式标准输出就是页面)
error_reporting的参数是错误级别。表示什么样子的级别才应该触发错误。如果我们告诉PHP,所有错误级别都不需要触发错误,那么,不管是日志,还是页面,都不会显示这个错误,就相当于什么都没有发生。
display_errors是控制是否要在标准输出展示错误信息
log_errors则是控制是否要在日志中记录错误信息。

error_log是显示错误日志的位置,这个在php-fpm中往往会被重写,于是往往会发现的是cli和fpm的错误日志竟然不是在同一个文件中。

ignore_repeated_errors这个标记控制的是如果有重复的日志,那么就只会记录一条,比如下面的程序:

error_reporting(E_ALL);
ini_set('ignore_repeated_errors', 1);
ini_set('ignore_repeated_source', 1);

$a = $c; $a = $c; //E_NOTICE
//Notice: Undefined variable: c in /tmp/php/index.php on line 20

本来会出现两次NOTICE的,但是现在,只会出现一次了…

track_errors开启会把最后一个错误信息存储到变量里面去,这个可能在对记日志的时候会有一些用处吧。不过我觉得真是没啥用…

html_errors 和 docref_root 两个是个挺有人性化的配置,配置了这两个参数以后,我们返回的错误信息中如果有一些在文档中有的信息,就会变成链接形式。

error_reporting(E_ALL);
ini_set('html_errors', 1);
ini_set('docref_root', "https://secure.php.net/manual/zh/");

include("a2.php"); //E_WARNING

页面显示:WARNING页面

能让你快速定位到我们出现错误的地方。是不是很人性~

php-fpm中的配置

error_log = /var/log/php-fpm/error.log // php-fpm自身的日志
log_level = notice // php-fpm自身的日志记录级别
php_flag[display_errors] = off // 覆盖php.ini中的某个配置变量,可被程序中的ini_set覆盖
php_value[display_errors] = off // 同php_flag
php_admin_value[error_log] = /tmp/www-error.log // 覆盖php.ini中的某个配置变量,不可被程序中的ini_set覆盖
php_admin_flag[log_errors] = on // 同php_admin_value
catch_workers_output = yes // 是否抓取fpmworker的输出
request_slowlog_timeout = 0 // 慢日志时长
slowlog = /var/log/php-fpm/www-slow.log // 慢日志记录

php-fpm的配置中也有一个error_log配置,这个很经常会和php.ini中的error_log配置弄混。但他们记录的东西是不一样的,php-fpm的error_log只记录php-fpm本身的日志,比如fpm启动,关闭。
而php.ini中的error_log是记录php程序本身的错误日志。

那么在php-fpm中要覆盖php.ini中的error_log配置,就需要使用到下面几个函数:

  • php_flag
  • php_value
  • php_admin_flag
  • php_admin_value

这四个函数admin的两个函数说明这个变量设置完之后,不能在代码中使用ini_set把这个变量重新赋值了。而php_flag/value就仍然以php代码中的ini_set为准。

slowlog是fpm记录的,可以使用request_slowlog_timeout设置判断慢日志的时长。

总结

我们经常弄混的就是日志问题,以及某些级别的日志为何没有记录到日志中。最主要的是要看error_log,display_errors, log_errors这三个配置,只是在看配置的时候,我们还要注意区分php.ini里面的配置是什么,php-fpm.ini里面的配置是什么。

好吧,我觉得弄懂这些配置,基本就没有php日志记录不了的WTF的问题了