在Linux中将目录压缩打包为zip文件 并通过ftp上传的Shell脚本

下面的shell脚本是将/opt下yyyymmdd格式的目录压缩打包为bak_yyyymmdd.zip,并通过ftp上传到192.168.0.2上的bak目录下。

#!/bin/sh
root='/opt'
t=`date +%Y%m%d`
d="$root/$t"
if [ -d "$d" ]; then
    zip -r bak_$t $t/*
fi
ftp -v -n 192.168.0.2 << EOF
user ftpuser pw123
binary
hash
cd /bak
lcd $root
prompt
mput bak_$t.zip
bye
EOF

阳光部落原创,更多内容请访问http://www.sunbloger.com/

苹果消息推送服务(APNS)的PHP版服务器端公共类

APNS(全称:Apple Push Notification Service),翻译为:苹果消息推送服务。

下面的代码是用PHP语言编写的一个公共类,用来完成从服务器端向APNS Server推送消息的过程。(这个类被应用在新京报新闻这个App的消息推送服务器端)

<?php
/**
 * ApplePush 苹果消息推送公共类
 * @author www.sunbloger.com
 */
class ApplePush
{
    /**
     * APNS server url
     * 
     * @var string
     */
    protected $apns_url = 'ssl://gateway.push.apple.com:2195'; //沙盒地址:ssl://gateway.sandbox.push.apple.com:2195
    
    /**
     * 推送数据
     * 
     * @var string
     */
    private $payload_json;
    
    /**
     * 数据流对象
     * 
     * @var mixed
     */
    private $fp;
    
    /**
     * 设置APNS地址
     * 
     * @param string $url
     */
     
    public function setApnsUrl($url)
    {
        if (empty($url)) {
            return false;
        } else {
            $this->apns_url = $url;
        }
        return true;
    }
    
    /**
     * 设置推送的消息
     * 
     * @param string $body
     */
    public function setBody($body)
    {
        if (empty($body)) {
            return false;
        } else {
            $this->payload_json = json_encode($body);
        }
        return true;
    }
    
    /**
     * Open 打开APNS服务器连接
     * 
     * @param string $pem 证书
     * @param string $passphrase 证书密钥
     */
    public function open($pem, $passphrase)
    {
        if (empty($pem)) {
            return false;
        }
        if (empty($passphrase)) {
            return false;
        }
        $ctx = stream_context_create();
        stream_context_set_option($ctx, 'ssl', 'local_cert', $pem);
        stream_context_set_option($ctx, 'ssl', 'passphrase', $passphrase);
        $fp = stream_socket_client($this->apns_url, $err, $errstr, 60, STREAM_CLIENT_CONNECT, $ctx);
        if (!$fp) {
            return false;
        }
        $this->fp = $fp;
        return true;
    }
    
    /**
     * Send 推送
     * 
     * @param string $token
     */
    public function send($token)
    {
        $msg = chr(0) . pack('n', 32) . pack('H*', $token) . pack('n', strlen($this->payload_json)) . $this->payload_json;
        $result = fwrite($this->fp, $msg, strlen($msg));
        return $result;
    }
    
    /**
     * Close APNS server 关闭APNS服务器连接
     * 
     */
    public function close()
    {
        fclose($this->fp);
        return true;
    }
}
?>

阳光部落原创,更多内容请访问http://www.sunbloger.com/

用PHP解析XML为数组的方法

XML——可扩展标记语言 。

可扩展标记语言,标准通用标记语言的子集,一种用于标记电子文件使其具有结构性的标记语言。

它可以用来标记数据、定义数据类型,是一种允许用户对自己的标记语言进行定义的源语言。 它非常适合万维网传输,提供统一的方法来描述和交换独立于应用程序或供应商的结构化数据。

PHP5已经内置的用于解析XML的函数,但使用起来并不顺手,于是就有了下面这段代码:

<?php
function xmlToArray($xml) {
    $array = (array)(simplexml_load_file($xml, null, LIBXML_NOCDATA));
    foreach ($array as $key => $value) {
        $array[$key] = structToArray((array)$value);
    }
    return $array;
}

function structToArray($item) {
    if (!is_string($item)) {
        $item = (array)$item;
        foreach ($item as $key => $value) {
            $item[$key] = structToArray($value);
        }
    }
    return $item;
}

$file ="test.xml";
$arrXml = xmlToArray($file);
print_r($arrXml);
?>

 

一段经典的抽奖算法 for PHP版

首先,我们构造一个奖项数组:

<?php
$prize_arr = array(
    array('id'=>1, 'prize'=>'No.1', 'v'=>1),
    array('id'=>2, 'prize'=>'No.2', 'v'=>3),
    array('id'=>3, 'prize'=>'No.3', 'v'=>6),
    array('id'=>4, 'prize'=>'No.4', 'v'=>10),
    array('id'=>5, 'prize'=>'No.5', 'v'=>20),
    array('id'=>6, 'prize'=>'Sorry', 'v'=>60),
);
?>

上面的数组中,No.1代表1等奖,以此类推,No.5代表5等奖,而Sorry代表没有中奖。v代表概率。

继续,我们看看算法的代码:

<?php
function get_rand($proArr) {
    $result = '';
    //概率数组的总概率精度
    $proSum = array_sum($proArr);
    //概率数组循环
    foreach ($proArr as $key => $proCur) {
        $randNum = mt_rand(1, $proSum);
        if ($randNum <= $proCur) {
            $result = $key;
            break;
        } else {
            $proSum -= $proCur;
        }
    }
    unset($proArr);
    return $result;
}
?>

上面的代码从1等奖开始,根据概率逐一计算每个奖项是否中出,直至最后没有中奖。

究竟这个算法是否准确,我们来模拟一下:

<?php
//通过奖项数组,构造出一个用于抽奖的概率数组
foreach ($prize_arr as $key => $val) {
    $arr[$key] = $val['v'];
}

//模拟1万次抽奖
for ($i=1; $i<=10000; $i++) {
    $prize_key = get_rand($arr);
    if (isset($test[$prize_arr[$prize_key]['prize']])) {
        $test[$prize_arr[$prize_key]['prize']]++;
    } else {
        $test[$prize_arr[$prize_key]['prize']] = 1;
    }
}
print_r($test);
?>

上面代码的模拟结果输出:

Array
(
    [sorry] => 6017
    [No.5] => 2011
    [No.4] => 968
    [No.3] => 587
    [No.2] => 310
    [No.1] => 107
)

通过模拟结果可以看出,这个算法还是很靠谱的。

阳光部落原创,更多内容请访问http://www.sunbloger.com/

预防注入的4条建议

1.假定所有输入都是可疑的,必须对所有输入中的script、iframe等字样进行严格的检查。这里的输入不仅仅是用户可以直接交互的输入接口,也包括HTTP请求中的Cookie中的变量,HTTP请求头部中的变量等;

2.不要仅仅验证数据的类型,还要验证其格式、长度、范围和内容;

3.不要仅仅在客户端做数据的验证与过滤,关键的过滤步骤在服务端进行;

4.对输出的数据也要检查,数据库里的值有可能会在一个大网站的多处都有输出,即使在输入做了编码等操作,在各处的输出点时也要进行安全检查;

阅读更多

简单几行PHP代码 搞定Slave数据库的连接平均分布

对于访问量较大的动态网站来说,后端MySQL数据库通常会采用主从(Master / Slave)同步的架构设计。

如果资金宽裕,可以购买昂贵的f5负载均衡器来解决多台Slave数据服务器的访问。还可通过免费的开源软件LVS来实现多台后端Slave数据服务器的访问,但LVS的设置比较繁琐,需要具备一些网络技术方面的知识储备,门槛还是较高的。

那么,是否还有更简单的方法呢?下面我来分享一段PHP代码,简单几行,就可以搞定多台后端Slave数据库的连接平均分布。

<?php
//定义多个数据库服务器信息
$slaveDbList = array(
    '1' => array('DB_HOST'=>'192.168.1.1', 'DB_USER'=>'slaveuser', 'DB_PW'=>'123456'),
    '2' => array('DB_HOST'=>'192.168.1.2', 'DB_USER'=>'slaveuser', 'DB_PW'=>'123456')
);

//定义hash矩阵
$slaveHashList = array(
    '0'=>'1', '1'=>'1', '2'=>'1', '3'=>'1', '4'=>'1', '5'=>'1', '6'=>'1', '7'=>'1',
    '8'=>'2', '9'=>'2', 'a'=>'2', 'b'=>'2', 'c'=>'2', 'd'=>'2', 'e'=>'2', 'f'=>'2'
);

//获取客户端ip,并计算hash代码
$clientIp = getClientIp();
$ipHash = substr(md5($clientIp), 0, 1);

//声明数据库连接信息
define('SLAVE_DB_HOST',         $slaveDbList[$slaveHashList[$ipHash]]['DB_HOST']); //数据库服务器地址
define('SLAVE_DB_USER',         $slaveDbList[$slaveHashList[$ipHash]]['DB_USER']); //用户名
define('SLAVE_DB_PW',           $slaveDbList[$slaveHashList[$ipHash]]['DB_PW']);   //密码
define('SLAVE_DB_NAME',         'test'); //数据库名称
define('SLAVE_DB_CHARSET',      'utf8'); //编码格式

function getClientIp()
{
    $clientIp = null;
 
    if ($clientIp !== null) return $clientIp;
    if (isset($_SERVER)) {
        if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
            $arr = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
            foreach ($arr as $ip) {
                $ip = trim($ip);
                if ($ip != 'unknown') {
                    $clientIp = $ip;
                    break;
                }
            }
        } elseif (isset($_SERVER['HTTP_CLIENT_IP'])) {
            $clientIp = $_SERVER['HTTP_CLIENT_IP'];
        } else {
            if (isset($_SERVER['REMOTE_ADDR'])) {
                $clientIp = $_SERVER['REMOTE_ADDR'];
            } else {
                $clientIp = '0.0.0.0';
            }
        }
    } else {
        if (getenv('HTTP_X_FORWARDED_FOR')) {
            $clientIp = getenv('HTTP_X_FORWARDED_FOR');
        } elseif (getenv('HTTP_CLIENT_IP')) {
            $clientIp = getenv('HTTP_CLIENT_IP');
        } else {
            $clientIp = getenv('REMOTE_ADDR');
        }
    }
    preg_match("/[\d\.]{7,15}/", $clientIp, $onlineIp);
    $clientIp = !empty($onlineIp[0]) ? $onlineIp[0] : '0.0.0.0';
    return $clientIp;
}
?>

阳光部落原创,更多内容请访问http://www.sunbloger.com/

列举WEB网页常用的DOCTYPE声明

HTML 5

<!DOCTYPE html>

 HTML 4.01 Strict

该 DTD 包含所有 HTML 元素和属性,但不包括展示性的和弃用的元素(比如 font)。不允许框架集(Framesets)。

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" 
"http://www.w3.org/TR/html4/strict.dtd">

 HTML 4.01 Transitional

该 DTD 包含所有 HTML 元素和属性,包括展示性的和弃用的元素(比如 font)。不允许框架集(Framesets)。

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" 
"http://www.w3.org/TR/html4/loose.dtd">

阅读更多

一个小巧的PHP防注入类

很多 web 开发者没有注意到 SQL 查询是可以被篡改的,因而把 SQL 查询当作可信任的命令。殊不知道,SQL 查询可以绕开访问控制,从而绕过身份验证和权限检查。更有甚者,有可能通过 SQL 查询去运行主机操作系统级的命令。

直接 SQL 命令注入就是攻击者常用的一种创建或修改已有 SQL 语句的技术,从而达到取得隐藏数据,或覆盖关键的值,甚至执行数据库主机操作系统命令的目的。这是通过应用程序取得用户输入并与静态参数组合成 SQL 查询来实现的。下面将会给出一些真实的例子。

由于在缺乏对输入的数据进行验证,并且使用了超级用户或其它有权创建新用户的数据库帐号来连接,攻击者可以在数据库中新建一个超级用户。

下面分享一个用于防注入的PHP类:

阅读更多

MySQL主从(Master-Slave)同步快速部署

Master:192.168.1.1
Slave:192.168.1.2

Master端:

vi /etc/my.cnf

server-id = 1  #master端ID号
binlog-ignore-db = mysql  #不同步mysql库
binlog-ignore-db = test  #同上
binlog-ignore-db = information_schema  #同上

/usr/local/mysql/bin/mysql -uroot -p
mysql>grant replication slave on *.* to slave@’192.168.1.2′ identified by ‘123456’;
mysql>flush privileges;
mysql>flush tables with read lock;  #只读锁表
mysql>show master status;  #获取并记录下当前日志file和position的值,稍后Slave端需要用到
File             | Position  |
+——————+———-
| mysql-bin.000001 | 291392712

另开一个终端窗口,然后执行如下的操作:

tar zcvf /usr/local/mysql/var.tgz /usr/local/mysql/var  //将var目录打包
scp /usr/local/mysql/var.tgz root@192.168.1.2:/usr/local/mysql/var.tgz  //将文件包传至Slave端

完成后回到前一个终端窗口解锁只读。

mysql>unlock tables;

Slave端:

vi /etc/my.cnf
server-id = 2  #Slave的ID号,此处不能与到Master端的ID重复。
保存退出。

service mysqld stop 或 /usr/local/mysql/bin/mysqladmin -uroot -p shutdown

tar zxvf /usr/local/mysql/var.tgz /usr/local/mysql/var

chown -R mysql:mysql /usr/local/mysql/var

service mysqld start

/usr/local/mysql/bin/mysql -uroot -p

mysql>stop slave;

mysql>change master to
>master_host=’192.168.1.1′,
>master_user=’slave’,
>master_password=’123456′,
>master_log_file=’mysql-bin.000001′,    此前记录下的Master端file值
>master_log_pos=291392712;    此前记录下的Master端position值

mysql>start slave;

mysql>show slave status \G

查看上面命令返回的结果,如果Slave_IO_Running: Yes 和Slave_SQL_Running: Yes  2个均为yes,则证明主从同步正常,如果任一个显示NO,则证明同步有问题。

Cacti数据采集周期修改为一分钟一次的方法

Cacti Logo

Cacti 默认的数据采集周期是5分钟一次,这对于监控业务繁忙的系统来说似乎过于漫长了,一些突发状况很难在第一时间发现,所以我们需要缩短这个周期。

首先,先登录 Cacti,进入 console > Settings > Poller 界面,将 Poller Interval Cron Interval 均改为 “Every Minute” 选项,然后点击 Save 按钮保存。

然后,进入 Linux,修改 crontab 中的数据采集脚本运行周期为*/1 * * * *,例如:

*/1 * * * * /usr/local/php/bin/php /htdocs/cacti/poller.php > /dev/null 2>&1

最后,回到 Cacti,进入 console > Data Templates 界面,修改所用到的数据模板,将 Step 设为60,这里的单位是秒。

完成上述三个步骤后,还需要我们手工清理历史数据后 Cacti 才能正常采集数据和显示图表。

进入 console > System Utilities 界面,点击 “Rebuild Poller Cache”

至此我们已经成功将 Cacti 的数据采集周期由5分钟一次修改为1分钟一次了。

如果不能正常显图表,可尝试手工清除 cacti/rra 目录下的 rrd 文件。

转载请注明:阳光部落原创,更多内容请访问http://www.sunbloger.com/