备忘日志【五】

52 篇文章 0 订阅
46 篇文章 0 订阅

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, 所以不受设定时间的限制.

curl伪装 ua

<?php

 $url = "http://xxxxx";
$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时才更新。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值