PHP获得用户真实IP

REMOTE_ADDR 是你的客户端跟你的服务器“握手”时候的IP。如果使用了“匿名代理”,REMOTE_ADDR将显示代理服务器的IP。
HTTP_CLIENT_IP 是代理服务器发送的HTTP头。如果是“超级匿名代理”,则返回none值。同样,REMOTE_ADDR也会被替换为这个代理服务器的IP。
$_SERVER[‘REMOTE_ADDR’]; //访问端(有可能是用户,有可能是代理的)IP
$_SERVER[‘HTTP_CLIENT_IP’]; //代理端的(有可能存在,可伪造)
$_SERVER[‘HTTP_X_FORWARDED_FOR’]; //用户是在哪个IP使用的代理(有可能存在,也可以伪造)

三个值区别如下:

一、没有使用代理服务器的情况:

REMOTE_ADDR = 您的 IP
HTTP_VIA = 没数值或不显示
HTTP_X_FORWARDED_FOR = 没数值或不显示

二、使用透明代理服务器的情况:Transparent Proxies

REMOTE_ADDR = 最后一个代理服务器 IP
HTTP_VIA = 代理服务器 IP
HTTP_X_FORWARDED_FOR = 您的真实 IP ,经过多个代理服务器时,这个值类似如下:203.98.182.163, 203.98.182.163, 203.129.72.215。

这类代理服务器还是将您的信息转发给您的访问对象,无法达到隐藏真实身份的目的。

三、使用普通匿名代理服务器的情况:Anonymous Proxies

REMOTE_ADDR = 最后一个代理服务器 IP
HTTP_VIA = 代理服务器 IP
HTTP_X_FORWARDED_FOR = 代理服务器 IP ,经过多个代理服务器时,这个值类似如下:203.98.182.163, 203.98.182.163, 203.129.72.215。

隐藏了您的真实IP,但是向访问对象透露了您是使用代理服务器访问他们的。

四、使用欺骗性代理服务器的情况:Distorting Proxies

REMOTE_ADDR = 代理服务器 IP
HTTP_VIA = 代理服务器 IP
HTTP_X_FORWARDED_FOR = 随机的 IP ,经过多个代理服务器时,这个值类似如下:203.98.182.163, 203.98.182.163, 203.129.72.215。

告诉了访问对象您使用了代理服务器,但编造了一个虚假的随机IP代替您的真实IP欺骗它。

五、使用高匿名代理服务器的情况:High Anonymity Proxies (Elite proxies)

REMOTE_ADDR = 代理服务器 IP
HTTP_VIA = 没数值或不显示
HTTP_X_FORWARDED_FOR = 没数值或不显示 ,经过多个代理服务器时,这个值类似如下:203.98.182.163, 203.98.182.163, 203.129.72.215。

完全用代理服务器的信息替代了您的所有信息,就象您就是完全使用那台代理服务器直接访问对象。

HTTP请求头和响应头

1,HTTP请求的基本概念    TCP/UPD/HTTP  *2,

HTTP请求头和响应头的含义 请求头: 

Accept: text/html,image/*(浏览器可以接收的类型) 

Accept-Charset: ISO-8859-1(浏览器可以接收的编码类型) 

Accept-Encoding: gzip,compress(浏览器可以接收压缩编码类型) 

Accept-Language: en-us,zh-cn(浏览器可以接收的语言和国家类型) 

Host: www.it315.org:80(浏览器请求的主机和端口)

 If-Modified-Since: Tue, 11 Jul 2000 18:23:51 GMT(某个页面缓存时间) 

Referer: http://www.it315.org/index.jsp(请求来自于哪个页面) 

User-Agent: Mozilla/4.0 (compatible; MSIE 5.5; Windows NT 5.0)(浏览器相关信息) 

Cookie:(浏览器暂存服务器发送的信息) 

Connection: close(1.0)/Keep-Alive(1.1)(HTTP请求的版本的特点) 

Date: Tue, 11 Jul 2000 18:23:51 GMT(请求网站的时间)  

响应头: 

Location: http://www.it315.org/index.jsp(控制浏览器显示哪个页面) 

Server:apache tomcat(服务器的类型) 

Content-Encoding: gzip(服务器发送的压缩编码方式)

 Content-Length: 80(服务器发送显示的字节码长度) 

Content-Language: zh-cn(服务器发送内容的语言和国家名) 

Content-Type: image/jpeg; charset=UTF-8(服务器发送内容的类型和编码类型)

 Last-Modified: Tue, 11 Jul 2000 18:23:51 GMT(服务器最后一次修改的时间) 

Refresh: 1;url=http://www.it315.org(控制浏览器1秒钟后转发URL所指向的页面) 

Content-Disposition: attachment; filename=aaa.jpg(服务器控制浏览器发下载方式打开文件)

 Transfer-Encoding: chunked(服务器分块传递数据到客户端)

 Set-Cookie:SS=Q0=5Lb_nQ; path=/search(服务器发送Cookie相关的信息) 

Expires: -1(服务器控制浏览器不要缓存网页,默认是缓存) 

Cache-Control: no-cache(服务器控制浏览器不要缓存网页)

 Pragma: no-cache(服务器控制浏览器不要缓存网页)   

Connection: close/Keep-Alive(HTTP请求的版本的特点)  

 Date: Tue, 11 Jul 2000 18:23:51 GMT(响应网站的时间) 

Centos7下添加开机自启动服务和脚本

添加开机自启脚本

在centos7中增加脚本有两种常用的方法,以脚本StartTomcat.sh为例:

#!/bin/bash
# description:开机自启脚本
/usr/local/tomcat/bin/startup.sh  #启动tomcat

方法一:

1、赋予脚本可执行权限(/opt/script/StartTomcat.sh是你的脚本路径)

# chmod +x /opt/script/StartTomcat.sh

2、打开/etc/rc.d/rc.local文件,在末尾增加如下内容

echo "/opt/script/StartTomcat.sh" >> /etc/rc.d/rc.local

3、在centos7中,/etc/rc.d/rc.local的权限被降低了,所以需要执行如下命令赋予其可执行权限

chmod +x /etc/rc.d/rc.local

方法二:

1、将脚本移动到/etc/rc.d/init.d目录下

# mv  /opt/script/StartTomcat.sh /etc/rc.d/init.d

2、增加脚本的可执行权限

chmod +x  /etc/rc.d/init.d/StartTomcat.sh

3、添加脚本到开机自动启动项目中

cd /etc/rc.d/init.d
chkconfig --add StartTomcat.sh
chkconfig StartTomcat.sh on

Specified key was too long; max key length is 1000 bytes

--
-- 表的结构 `ecs_goods_activity`
--

DROP TABLE IF EXISTS `ecs_goods_activity`;
CREATE TABLE `ecs_goods_activity` (
`act_id` mediumint(8) unsigned NOT NULL auto_increment,
`act_name` varchar(255) NOT NULL,
`act_desc` text NOT NULL,
`act_type` tinyint(3) unsigned NOT NULL,
`goods_id` mediumint(8) unsigned NOT NULL,
`product_id` mediumint(8) unsigned NOT NULL default '0',
`goods_name` varchar(255) NOT NULL,
`start_time` int(10) unsigned NOT NULL,
`end_time` int(10) unsigned NOT NULL,
`is_finished` tinyint(3) unsigned NOT NULL,
`ext_info` text NOT NULL,
PRIMARY KEY (`act_id`),
KEY `act_name` (`act_name`,`act_type`,`goods_id`)
) ENGINE=MyISAM;

关于FLUSH PRIVILEGES的使用

很多人在MySQL里面乱用FLUSH PRIVILEGES,你知道吗?包括从前的我,但是FLUSH PRIVILEGES你真的知道应该在什么场合使用么?

根据官方文档的如下链接内容可知:
https://dev.mysql.com/doc/refman/5.6/en/privilege-changes.html

当mysqld启动时,所有的权限都会被加载到内存中。
如果使用GRANT/REVOKE/SET PASSWORD/RENAME USER命令来更改数据库中的权限表,mysqld服务器将会注意到这些变化并立即加载更新后的权限表至内存中,即权限生效;
如果使用INSERT/UPDATE/DELETE语句更新权限表,则内存中的权限表不会感知到数据库中权限的更新,必须重启服务器或者使用FLUSH PRIVILEGES命令使更新的权限表加载到内存中,即权限需在重启服务器或者FLUSH PRIVILEGES之后方可生效。

权限生效的含义:
1、表级别/列级别的权限,当更新后的权限加载至内存表中,已存在的会话下一次请求时可使用该权限,在修改权限后的建立的会话则立即生效;
2、数据库级别的权限,当更新后的权限加载至内存表中,已存在会话下一次使用USE db_name后,可使用该权限,在修改权限后的建立的会话则立即生效;
3、全局权限或者修改密码,当更新后的权限加载至内存表中,需要在下一次登录mysqld后,可使用该权限或密码,对已存在会话不起作用。

MySQL复制表结构和内容到另一张表

复制表结构及数据到新表 

create table 新表 select * from 旧表

只复制表结构到新表

select table 新表
select * from 旧表 where 1=2

即:让where条件不成立.

方法二:(低版本的MySQL不支持,MySQL 4.0.25不支持,MySQL 5已经支持了)

create table 新表
like 旧表 

复制旧表的数据到新表(假设两个表结构一样)

insert into 新表
select * from 旧表 

复制旧表的数据到新表(假设两个表结构不一样)

insert into 新表(字段1,字段2,…….)
select 字段1,字段2,…… from 旧表

file_exist()和is_file()哪个性能更优?

今天浏览项目代码,在项目自动加载注册的路由方法发现前辈判断拼接的控制器文件是否存在时,用的是file_exists($file),而我个人风格更偏向于is_file(),为了弄清楚两个函数哪个性能更优,查阅了官方手册和一些博客文章。

PHP手册描述如下:

file_exists检查文件或目录是否存在

is_file判断给定文件名是否为一个正常的文件

由此我们知道,在功能上file_exists=is_file+is_dir,相当于后两者的集合,既可以判断文件是否存在,又可判断目录是否存在,那么这两个都可以判断文件是否存在的函数其性能哪个占优呢?

写程序验证一下(程序见附件):

分别执行1000次,记录所需时间。

文件存在(当前目录)
is_file:0.4570ms
file_exists:2.0640ms

文件存在(绝对路径3层/www/hx/a/)
is_file:0.4909ms
file_exists:3.3500ms

文件存在(绝对路径5层/www/hx/a/b/c/)
is_file:0.4961ms
file_exists:4.2100ms

文件不存在(当前目录)
is_file:2.0170ms
file_exists:1.9848ms

文件不存在(绝对路径5层/www/hx/a/b/c/)
is_file:4.1909ms
file_exists:4.1502ms

目录存在
file_exists:2.9271ms
is_dir:0.4601ms
目录不存在
file_exists:2.9719ms
is_dir:2.9359ms

is_file($file)
file_exists($file)
当$file是目录时,is_file返回false,file_exists返回true

文件存在的情况下,is_file比file_exists要快得多;
要检测文件所在的目录越深,速度差越多,但至少快4倍。

文件不存在的情况下,is_file比file_exists要慢一点点,但可以忽略不计。

目录存在的情况下,is_dir比file_exists要快得多;
目录不存在的情况下,is_dir比file_exists要慢一点点,但可以忽略不计。

结论:
如果要判断文件是否存在,用函数 is_file(),
如果要判断目录是否存在,用函数 is_dir(),
好像没地方需要用file_exists了,不确定传入的参数是文件还是目录的时候用。

—————————————-测试代码————————————————-

function runtime($t1){
return number_format((microtime(true) – $t1)*1000, 4).’ms’;
}
$times = 1000;

$t1 = microtime(true);
for($i=0;$i<$times;$i++){
is_file(‘/www/hx/www.9enjoy.com/config.php’);
}

echo ‘<br>is_file:’.runtime($t1);

$t2 = microtime(true);
for($i=0;$i<$times;$i++){
file_exists(‘/www/hx/www.9enjoy.com/config.php’);
}
echo ‘<br>file_exists:’.runtime($t2);

/*
$t3 = microtime(true);
for($i=0;$i<$times;$i++){
is_dir(‘/www/hx/www.9enjoy.com/’);
}
echo ‘<br>is_dir:’.runtime($t3);
*/

in_array()踩坑记录

前几天写api,前端发过来的参数约定是数字1-3,按照开发文档,我写了如下代码,

if (!in_array($param, range(0, 3))) {
    $vo['error_code'] = 101;
    return $vo;
}

后来自己单元测试,发现发送模拟参数’1abc’和’abc’,居然没返回错误码(报错),经查阅PHP手册,得知该函数还有第三个参数,是否进行参数类型比较,默认为FALSE,这就导致字符串’1abc’在和整型int比较时,由于PHP是弱类型语言,会发生隐式类型转换,所以这里’1abc’转换为’1’,同理‘abc’转换为字符串类型’0’,从而返回true;

要避免这个坑,有以下两个方法

1.自行进行强制类型验证

if (!is_int($param) || !in_array($param, range(0, 3))) {
    $vo['error_code'] = 101;
    return $vo;
}

2.填充in_array()第三个参数为True,函数内部则会进一步检查类型是否相同

if (!in_array($param, range(1, 3),True)) {
    $vo['error_code'] = 101;
    return $vo;
}