1、[2012-10-13] ubuntu中 cron 服务的默认日志存放位置
以前用 CentOS,cron 的默认日志存放在 /var/log/cron 处,
而 ubuntu 的cron日志还要设置一下
修改rsyslog文件,将/etc/rsyslog.d/50-default.conf 文件中的#cron.*前的#删掉;
重启rsyslog服务service rsyslog restart;
重启cron服务service cron restart;
more /var/log/cron.log;
PS:计划任务文件是/var/spool/cron/crontabs/root,当crontab -e不好用时,可以直接编辑者个文件。
2、[2012-10-13] 用PHP实现定时器功能
以前只知道用JS能实现定时器功能,而且很方便,但今天做项目过程中要实现个功能模块,做到其中某部分时,心想要是可以通过PHP来实现个定时器功能就好了,于是在网上找了找,发现用gnore_user_abort()函数配合set_time_limit()函数 和一个死循环就可以实现类似功能。虽然项目最终未使用该功能,但觉得潜在的使用价值还是很高的,因此后来又参考网上的一些资料,整理如下:
范例代码:
ignore_user_abort(true);//设置与客户机断开是否会终止脚本的执行。
set_time_limit(0); //设置脚本超时时间,为0时不受时间限制
ob_end_clean();//清空缓存
ob_start();//开始缓冲数据
while(1){
echo str_repeat(” “,1024);//写满IE有默认的1k buffer
ob_flush();//将缓存中的数据压入队列
flush();//输出缓存队列中的数据
echo “now time is “.date(‘h:i:s’) . ”
“;//打印数据,其实是先将数据存入了缓存中
usleep(1000000);//延迟一秒(暂停一秒)
}
注:
flush()和ob_flush()的正确顺序应是,先ob_flush()再flush(),不可弄混。
usleep()函数可替换成sleep()函数,不同之处在于sleep()的参数是秒。
3、[2012-10-16] 以子进程方式执行PHP文件
/**
* 以子进程方式执行PHP文件
*
* @param string $phpScriptFilePath 要执行的 PHP 文件的绝对路径
* @param mixed $args
* @param string $phpExeFile PHP可执行文件或php.exe所在位置的绝对路径
* @return void|mixed
* @throws Exception
*/
public static function executePhpScriptFile($phpScriptFilePath, $arguments = null, $phpExeFile = null)
{
if (!is_readable($phpScriptFilePath)){
throw new Exception($phpScriptFilePath . ' is not readable.');
}
if (!isset($phpExeFile)){
$config = \Sso\DAL\AllConfig\GlobalConfig::getSingleton();
$tmp = $config['phpPath'];
if (false === strpos($tmp, ' ')) $phpExeFile = $tmp;
else{
list($phpExeFile) = explode(' ', $tmp);
}
}
if (!is_executable($phpExeFile)){
throw new Exception($phpExeFile . ' is not executable.');
}
$pipes = array();
$descriptorspec = array(
0 => array('pipe', 'r'),
1 => array('pipe', 'w'),
2 => array('pipe', 'w'),
);
$fp = proc_open(
$phpExeFile . " " . $phpScriptFilePath
,$descriptorspec
,$pipes
);
if (!is_resource($fp)){
throw new RuntimeException('无法执行 ' . $phpScriptFilePath);
}
$retvl = null;
try{
if (null !== $arguments){
fwrite($pipes[0], json_encode($arguments));
fclose($pipes[0]);
}
// 子进程标准输出
$resultData = stream_get_contents($pipes[1]);
fclose($pipes[1]);
// 子进程的标准错误输出
$errMsg = stream_get_contents($pipes[2]);
fclose($pipes[2]);
// 过滤掉技能扩展的调试输出
$errMsg = str_replace("##########################create BuffSkill2025 2025\n", '', $errMsg);
proc_close($fp);
if ('' === $resultData) $retvl = '';
else{
$tmp = json_decode($resultData, true);
if (empty($tmp) OR ($tmp == $resultData)) $retvl = $resultData;
else $retvl = $tmp;
}
if (!empty($errMsg)) return $errMsg;
}
catch (Exception $ex){
if (is_resource($fp)) proc_close($fp);
throw $ex;
}
return $retvl;
}// end static method executePhpScriptFile
/**
* 在子进程中,调用此方法可以获得主进程传递过来的参数
*
* @return void|mixed
*/
public static function getArgumentsFromMainProcess()
{
$stdin = fgets(STDIN);
if (empty($stdin)) return null;
else return json_decode($stdin, true);
}// end static method getArgumentsFromMainProcess
4、[2012-10-26]有些函数和语句是特为 PHP-CLI 脚本设计的
向命令行输出:fwrite(STDOUT, 'Inpot Username:');
等待命令行输入:$name = fgets(STDIN); //程序暂停在这里,等待输入
当然这里的 fwrite() 函数的作用类似于 echo() 和 print() 函数了。
而这些 PHP-CLI 脚本完全可以加到 linux 的 cron 定时服务里,在设定的时间点执行。Windows 下加在计划任务里。这样就可以定时执行这些脚本,特别是例行的系统或数据库维护,都可以用 PHP 的命令行脚本来做。
由于 PHP-CLI 的最大执行时间是没有限制的(the maximum execution time is unlimited for CLI scripts),不像 Web 脚本默认最多只能执行30秒,所以用 PHP-CLI 脚本来维护系统是很合适的。
一个 PHP-CLI 脚本的例子:
echo "Hello! PHP Command line!\n";
echo 'php_sapi_name: '. php_sapi_name() ."\n"; //
echo getcwd() ."\n"; //取得当前脚本路径
fwrite(STDOUT, 'Inpot Username:'); //向命令行输出
$name = fgets(STDIN); //等待命令行输入
echo "Hello, " . trim($name). "! The time is currently ". date("r") . "\n";
die;
5、[2012-11-06]file_get_contents与curl
经常会使用php中的file_get_contents()函数去抓取远程页面, 对方页面文件过大、对方页面反应时间太久或网络传输慢等原因都可能会导致file_get_contents()执行时间超过php的最大执行时间。而一 旦file_get_contents出错即会终止整个程序。
测试用例
<?php
ini_set("max_execution_time", 2);
$url = "http://aymoo.cn/files/jQuery1.2API.chm";
$html = file_get_contents($url);
var_dump($html);
?>
上面代码首先将php的最大执行时间设置为2秒,然后去远程读取170KB的文件,按照本人的网络环境下载170KB文件需时超过1s.
现象
Fatal error: Maximum execution time of 2 second exceeded in C:\wamp\www\phptest\timeout.php on line 4
这是由网络造成的php执行超时,在file_get_contents超时时即报错并且程序停止执行。而基本上大多数需求是即使发生错误也要求程序继续向下执行。
解决办法
<?php ini_set("max_execution_time", 2); $url = "http://aymoo.cn/files/jQuery1.2API.chm"; //$html = file_get_contents($url); $ch = curl_init($url); curl_setopt($ch, CURLOPT_TIMEOUT, 1); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); $html = curl_exec($ch); curl_close ($ch); var_dump($html); ?>
现象
bool(false)
成功解决了超时导致程序不能继续执行的问题。
这里需要注意的是,设置curl的CURLOPT_TIMEOUT的值应该小于php最大执行时间。
为什么要说应该而不是必须?用两个例子对比来说明,假设抓取页面需4s。
example 1
max_execution_time 为 2
CURLOPT_TIMEOUT 为 3
example 2
max_execution_time 为 10
CURLOPT_TIMEOUT 为 20
我对这个过程的理解是,php监控整个脚本的时间,curl监控抓取页面的时间。对例1, 抓取远程页面的过程中已经达到php最大执行时间,得到的结果和file_get_contents一样。
例2中,整个脚本的执行时间不到5s, 所以不受设定时间的限制.
<?php
$ch = curl_init($url);
// 设置选项,有返回值
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
// 设置选项
curl_setopt($ch, CURLOPT_REFERER, $url);
// 设置选项,浏览器信息
$browser = array(
'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.2; SV1; .NET CLR 1.1.4322)',
'Mozilla/5.0 (Windows NT 5.1; rv:11.0) Gecko/20100101 Firefox/11.0',
'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET4.0C; .NET4.0E; .NET CLR 2.0.50727)'
);
curl_setopt($ch, CURLOPT_USERAGENT, $browser[rand(0, count($browser) - 1)]); //伪装 ua,解决防采集机制
// 执行
$contents = curl_exec($ch);
curl_close($ch); // 关闭handler
echo $contents;
?>
curl伪造IP和来源
<?php
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "http://localhost/2.php");
curl_setopt($ch, CURLOPT_HTTPHEADER, array('X-FORWARDED-FOR:8.8.8.8', 'CLIENT-IP:8.8.8.8')); //构造IP
curl_setopt($ch, CURLOPT_REFERER, "http://www.xssxss.com/ "); //构造来路
curl_setopt($ch, CURLOPT_HEADER, 1);
$out = curl_exec($ch);
curl_close($ch);
?>
小小的总结一下:
伪造的IP,最大程度上的识别请用REMOTE_ADDR来获取客户端IP
这里说的是最大程序上的,如果伪造者拥有很多代理IP,那就没话说了
附加说明: 模拟ip只是其中必备条件之一,还需要一下几点:
1.模拟ip
2.模拟header
3.模拟cookie
总之模拟浏览器就对了
6、[2012-12-10] 返回字符串键值全为小写或大写的数组
一、返回字符串键名全为小写或大写的数组
array array_change_key_case ( array $input [, int $case = CASE_LOWER ] )
array_change_key_case() 将 input 数组中的所有键名改为全小写或大写。改变是根据后一个选项 case 参数来进行的。本函数不改变数字索引。
二、把数组转为全为小写或大写的,PHP没提供这样的函数,自己写。
/**
* 返回字符串键值全为小写或大写的数组
* @param array $arr
* @param string $case
* @return array
*/
function array_change_value_case(array $arr,$case='strtolower'){
if(!is_array($arr)) $arr = (array) $arr;
if(!in_array(strtolower($case),array('strtolower','strtoupper'))) $case = 'strtolower';
return array_map($case, $arr);
}
主要是用到array_map('strtolower',$arr)这个函数;
7、[2013-01-10] ci在nginx环境下nginx配置
今天将ci框架做的项目放新服务器上,结果除了首页autoload的控制器能使用,其他的控制器全部都是404错误,
查了下资料原来是Nginx不像apache默认支持pathinfo
server {
listen 9901;
server_name 58.208.147.217;
charset UTF-8;
access_log off;
root /var/www;
location / {
index index.html index.htm index.php;
}
# removes trailing "index" from all controllers
if ($request_uri ~* index/?$) {
rewrite ^/(.*)/index/?$ /$1 permanent;
}
# removes trailing slashes (prevents SEO duplicate content issues)
if (!-d $request_filename) {
rewrite ^/(.+)/$ /$1 permanent;
}
# removes access to "system" folder, also allows a "System.php" controller
if ($request_uri ~* ^/system) {
rewrite ^/(.*)$ /index.php?/$1 last;
break;
}
# unless the request is for a valid file (image, js, css, etc.), send to bootstrap
if (!-e $request_filename) {
rewrite ^/(.*)$ /index.php?/$1 last;
break;
}
# catch all
error_page 404 /index.php;
location ~ \.php$ {
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
include fcgi.conf;
}
#rewrite ^/web/(.*)$ /$1 permanent;
location ~ /\.ht {
deny all;
}
}
http://ellislab.com/forums/viewthread/230128/#1042410
8、discuz 用cookie保存用户登陆状态
登陆是调用:
function setloginstatus($member, $cookietime) {
global $_G;
$_G['uid'] = $member['uid'];
$_G['username'] = $member['username'];
$_G['adminid'] = $member['adminid'];
$_G['groupid'] = $member['groupid'];
$_G['formhash'] = formhash();
$_G['session']['invisible'] = getuserprofile('invisible');
$_G['member'] = $member;
$_G['core']->session->isnew = 1;
dsetcookie('auth', authcode("{$member['password']}\t{$member['uid']}", 'ENCODE'), $cookietime, 1, true); //authcode加密
dsetcookie('loginuser');
dsetcookie('activationauth');
dsetcookie('pmnum');
}
分析:把password和uid加密后保存到 cookie,这个password是pre_common_member表的password, 而登陆时用的是pre_ucenter_members表的password。
判断用户是否登陆调用:
if($auth = getglobal('auth', 'cookie')) {
$auth = daddslashes(explode("\t", authcode($auth, 'DECODE')));
}
list($discuz_pw, $discuz_uid) = empty($auth) || count($auth) < 2 ? array('', '') : $auth;
if($discuz_uid) {
$user = getuserbyuid($discuz_uid, 1);
}
if(!empty($user) && $user['password'] == $discuz_pw) {
if(isset($user['_inarchive'])) {
C::t('common_member_archive')->move_to_master($discuz_uid);
}
$this->var['member'] = $user;
} else {
$user = array();
$this->_init_guest();
}
分析:先获取cookie,是判断password是否相等。
9、discuz x中的cookie设置和获取
后来查了一下资料,Discuz!把PHP的setcookie函数封装成了dsetcookie。顺藤摸瓜,在function_core.php里面找到了这两个函数:
function dsetcookie($var, $value = '', $life = 0, $prefix = 1, $httponly = false) {
源码略
}
function getcookie($key)
{
global $_G;
return isset($_G['cookie'][$key]) ? $_G['cookie'][$key] : '';
}
那么在Discuz!中调用cookie的思路出来了:
设置cookie:dsetcookie('cookie名', 'cookie值', '有效时间');
源码中dsetcookie还有两个参数$prefix与$httponly,$prefix为是否添加cookie前缀,true则自动添加网站的前缀,false则不添加,$httponly表示是否只允许http方式访问,这两个值只要默认值即可,有需要可另外设置,不过当用户访问的是手机版则$httponly会被自动设置为false,见function_core.php第272行
if(defined('IN_MOBILE')) {
$httponly = false;
}
读取cookie有两种方法,第一种使用getcookie函数:
getcookie('cookie名');
第二种直接访问全局变量$_G:
$_G['cookie']['cookie名']
10、[2013-02-20] 让命令在后台执行
/**
* 让命令在后台执行,并返回PID
*
*@param string $Command 需要执行的命令
*@param int $Priority 优先级
*@return int
*/
function run_in_background($Command, $Priority = 0){
if($Priority)
$PID = shell_exec("nohup nice -n $Priority $Command 2> /dev/null & echo $!");
else
$PID = shell_exec("nohup $Command 2> /dev/null & echo $!");
return($PID);
}
11、[2013-03-13] PHP调用Linux命令并返回数据
exec
原型:string exec ( string command [, array &output [, int &return_var]] )
描述:返回值保存最后的输出结果,而所有输出结果将会保存到$output数组,$return_var用来保存命令执行返回的最后一行。。
例子:$ret = exec("find / -name redis-cli", $output, $var);
注意:
A. 输出结果会逐行追加到$output中,因此在调用exec之前需要unset($output),特别是循环调用的时候。
B. 如果想通过exec调用外部程序后马上继续执行后续代码,仅仅在命令里加"&"是不够的,此时exec依然会等待命令执行完毕;需要再将标准输出做重定向才可以,例如:exec("find / -name redis-cli >/dev/null &", $output, $var),这样$output不能保存输出结果。exec("find / -name redis-cli 2>/dev/null &", $output, $var),$output能保存输出结果,但要等待命令执行完。;
12、[2013-03-28] 通行证整合ucenter遇到的难点及解决办法。
1、web集群,跨子域session共享的问题。
改写session的保存方式,把session保存到redis里。
ini_set('session.save_handler', 'user');
session_set_save_handler(
array($this, "open"),
array($this, "close"),
array($this, "read"),
array($this, "write"),
array($this, "destroy"),
array($this, "gc")
);
2、https与ucenter同步登陆时,有些浏览器block,不允许加载http资源。
同步登陆时,在https页面输出一段js去访问其他的设置了同步登陆的站点。但https页面不允许加载htts的资源。
解决办法:登陆验证完成后跳转到一个http页面再输出同步登陆的js。(js保存到session里,输出后删除)
3、本想把2中的js通过url加密后传递到另一个页面,但js太长,没找到好的可以固定长度的加密算法。ie中url的最大长度是2085字符,超过就会出错。rsa非对称加密可以固定长度,但对明文长度也有要求。rsa加密后是二进制字符,输出时会乱码,可通过base64_encode()编码后再输出。
13、[2013-04-15] 使用ci 发现mysql sleep进程过多
用ci开发了一个网站,发现mysql经常提示连接数过多,登陆mysql,用show processlist发现很多sleep的进程,原理是在application/config/database.php中设置了
$db['default']['pconnect'] = TRUE;
在mysql中用show global variables like 'wait_timeout';显示wait_timeout=28800。
原来是在ci中开了pconnect,且在mysql中wait_timeout为默认值28800,导致MySQL里大量的SLEEP进程无法及时释放,拖累系统性能。
14、[2013-07-11] 抽奖防并发
不锁表防止并发,加where条件。如:
"UPDATE `prize` SET `num` = `num`+1 WHERE `id`={$id} AND `num`< {$tatol}";
num为当前中奖数,$tatol为奖品总数。num<$tatol时才更新。