本文链接
// 是否先log到buffer,再通过CT_flush()一次性写入文件
$ct_log_buffer = true;
$CT_off = true;
$request_num = uniqid();
$CT_format = "";
if ($ct_path) {
$dir = dirname($ct_path);
if (!file_exists($dir)) {
mkdir($dir, 0777, true);
}
$file = fopen($ct_path, "a+");
$file_ct_log = $file;
}
if ($ct_log_path) {
$dir = dirname($ct_log_path);
if (!file_exists($dir)) {
mkdir($dir, 0777, true);
}
$file_ct_log = fopen($ct_log_path, "a+");
}
$ct_buffer = [];
$path_my = __DIR__ . "/common.my.php";
if (is_file($path_my)) {
require $path_my;
}
function clear_log() {
Global $log_path;
unlink($log_path);
}
function clog($content, $with_lf = true) {
Global $log_path;
$file = fopen($log_path,"a+");
fwrite($file, $content);
CT_log($content);
if($with_lf) {
fwrite($file, "\n");
CT_log("\n");
}
fclose($file);
}
function get_safe($obj, $key, $def = NULL) {
if(isset($obj[$key])) {
return $obj[$key];
}
return $def;
}
function get_stack_trace($title = "") {
$html = "=================stack trace:".$title."\n";
$array =debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
foreach($array as $row) {
$html .= sprintf("file:%s, line:%d, class:%s, function:%s\n",
get_safe($row, 'file'), get_safe($row, 'line'), get_safe($row, 'class'), get_safe($row, 'function'));
}
return $html;
}
function log_stack_trace($title = "") {
clog(get_stack_trace($title));
}
// error handler function with stack trace.
// use like this:
// $old_error_handler = set_error_handler("err_handler");
function err_handler($errno, $errstr, $errfile, $errline)
{
$errno_map = array(1 => "E_ERROR", 2 => "E_WARNING", 4 => "E_PARSE", 8 => "E_NOTICE",
16 => "E_CORE_ERROR", 32 => "E_CORE_WARNING", 64 => "E_COMPILE_ERROR",
128 => "E_COMPILE_WARNING", 256 => "E_USER_ERROR", 512 => "E_USER_WARNING",
1024 => "E_USER_NOTICE", 2048 => "E_STRICT", 4096 => "E_RECOVERABLE_ERROR",
8192 => "E_DEPRECATED", 16384 => "E_USER_DEPRECATED", 32767 => "E_ALL");
clog(sprintf("------------ %s(%d), msg:%s", $errno_map[$errno], $errno, $errstr));
log_stack_trace("");
/* Don't execute PHP internal error handler */
return true;
}
// 获取当前系统时间,返回float格式,单位:秒
function get_time() {
date_default_timezone_set('Asia/Shanghai');
list($usec, $sec) = explode(" ", microtime());
return ((float)$usec + (float)$sec);
}
function get_prefix() {
return "";
Global $ip, $pid;
if(!isset($ip)) {
$pid = getmypid();
$ip = $_SERVER['REMOTE_ADDR'];
}
return $pid.' '.$ip.' '.date("m-d H:i:s ");
}
function CT($content) {
Global $CT_off, $file;
if($CT_off || !$file)
return;
Global $last_time, $first_time, $is_first, $ct_log_buffer, $ct_buffer, $request_num, $CT_format;
if ($CT_format == "raw") {
$all_out = $content . "\n";
} else {
// 通过stack trace计算缩进
$array =debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
$ignore_count = 0;
$count = count($array);
$ignore_names = ["call_user_func_array", "call_user_func", "spl_autoload_call"];
$ignore_classes = ["ReflectionClass"];
for ($i = 2; $i < $count; $i++) {
$frame = $array[$i];
if (in_array($frame["function"], $ignore_names) || isset($frame["class"]) && in_array($frame["class"], $ignore_classes)) {
$ignore_count++;
}
}
$all_out = get_prefix() . str_pad("", $count - 2 - $ignore_count, " ") . $request_num . " " . $content;
$cur_time=get_time();
if(!$is_first) {
$is_first = true;
$last_time = $first_time = $cur_time;
}
$total_time=$cur_time-$first_time;
$delta_time=$cur_time-$last_time;
$overtime_flag = "";
// 添加超时标记
if($delta_time * 1000 > 10)
$overtime_flag = "----overtime";
$all_out = $all_out." cur_time: $cur_time, total_time: $total_time, delta_time: $delta_time $overtime_flag\n";
$last_time=$cur_time;
}
if ($ct_log_buffer === true) {
$ct_buffer[] = $all_out;
} else {
fwrite($file, $all_out);
}
}
/**
* 将buffer的CT内容写入文件
* @param boolean turn_off_buffer, 完成后是否关闭buffer,以保证通过register_shutdown_function等调用的函数能够被输出
*/
function CT_flush($turn_off_buffer)
{
global $file, $ct_buffer, $ct_log_buffer;
if (!$file) {
return;
}
fwrite($file, join("", $ct_buffer));
$ct_buffer = [];
$ct_log_buffer = !$turn_off_buffer;
}
function CT_log($content = "", $path = NULL) {
Global $file_ct_log;
if (!$file_ct_log && !$path) {
return;
}
$content = get_prefix().print_r($content, true)."\n";
if($path) {
file_create_path($path);
$file = fopen($path, "a+");
if($file) {
fwrite($file, $content);
fclose($file);
}
}
else {
fwrite($file_ct_log, $content);
}
}
/**
* 日志输出,使用info level
* @param $content
* @param array $params
* @param string $logger
*/
function slog($content, $params = [], $logger = "default")
{
SeasLog::info($content, $params, $logger);
}
/**
* 日志输出,使用debug level
* @param $content
* @param array $params
* @param string $logger
*/
function slog_debug($content, $params = [], $logger = "default")
{
SeasLog::debug($content, $params, $logger);
}
/**
* 日志输出,使用error level
* @param $content
* @param array $params
* @param string $logger
*/
function slog_error($content, $params = [], $logger = "default")
{
SeasLog::error($content, $params, $logger);
}
/**
* 日志输出,使用warning level
* @param $content
* @param array $params
* @param string $logger
*/
function slog_warning($content, $params = [], $logger = "default")
{
SeasLog::warning($content, $params, $logger);
}
/*
获取指定的http response header 值。
eg:
HTTP/1.1 200 OK
Server: Tengine/2.1.2
Date: Sun, 02 Apr 2017 02:49:34 GMT
Content-Type: text/html; charset=gb2312
Content-Length: 124378
Connection: keep-alive
Cache-Control: private
X-AspNetMvc-Version: 4.0
X-AspNet-Version: 4.0.30319
*/
function curl_get_header($ch, $response, $key)
{
$header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
$header = substr($response, 0, $header_size);
// header processing
$header_arr = explode("\r\n", $header);
$value = "";
$key .= ":";
foreach ($header_arr as $entry) {
if (!strncmp($entry, $key, strlen($key))) {
$value = trim(substr($entry, strlen($key)));
break;
}
}
return $value;
}
// 获取response状态码
function curl_get_status($ch, $response)
{
$header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
$header = substr($response, 0, $header_size);
$header_arr = explode("\r\n", $header);
return explode(" ", $header_arr[0])[1];
}
function startsWith($haystack, $needle)
{
// search backwards starting from haystack length characters from the end
return $needle === "" || strrpos($haystack, $needle, -strlen($haystack)) !== FALSE;
}
// http 301 is handled.
function http_get_core($url, &$status = null)
{
CT_log("-----------http_get:" . $url);
$ch = curl_init();
$status = -1;
// 301 最多嵌套3次。
for ($i = 0; $i < 3; $i++) {
$options = array(
CURLOPT_HEADER => 1,
CURLOPT_POST => 0, // 请求方式为POST
CURLOPT_URL => $url, // 请求URL
CURLOPT_RETURNTRANSFER => 1, // 获取请求结果
CURLOPT_TIMEOUT_MS => 30000, // 超时时间(ms)
CURLOPT_POSTFIELDS => http_build_query(array()), // 注入接口参数
CURLOPT_SSL_VERIFYPEER => 0, // 不验证证书
);
curl_setopt_array($ch, $options);
curl_setopt($ch, CURLOPT_ENCODING, "gzip,deflate"); // 百度不支持
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
if (($response = curl_exec($ch))) {
// 有的网站header、指定的编码不一致,会导致乱码。因此如果有编码信息,将其转送到client。
$content_type = curl_get_header($ch, $response, "Content-Type");
if($content_type) {
header("Content-Type: " . $content_type);
}
$header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
$status = $code = curl_get_status($ch, $response);
if ($code == 301 || $code == 302) {
$redirect_url = curl_get_header($ch, $response, "Location");
$parsed_re = parse_url($redirect_url);
if(isset($parsed_re["host"])) {
$url = $redirect_url;
}
else {
$parsed = parse_url($url);
if(startsWith($redirect_url, "/")) {
$url = $parsed["schema"]. "://" . $parsed["host"] . $redirect_url;
}
else {
// TODO 相对路径拼接
$content = "relative path TODO";
curl_close($ch);
return $content;
}
}
continue;
} else if ($code == 404) {
header("Status: 404 Not Found");
$msg = array(
"status" => 404
);
$content = "" . json_encode($msg) . "
";
$content .= "404, not found!";
} else if ($code == 200) {
$content = substr($response, $header_size);
} else {
$content = "http error, code=" . $code . "\n" . substr($response, $header_size);
}
} else {
$msg = array(
"status" => -1
);
$content = "" . json_encode($msg) . "
";
$content .= "invoke error[" . curl_error($ch) . "]";
}
curl_close($ch);
return $content;
}
}
// 递归为path创建必要的路径
function file_create_path($path)
{
$dir = dirname($path);
if ($dir && !file_exists($dir)) {
mkdir($dir, 0755, true);
}
}
// $save_path 需要gbk编码
function file_save($content, $save_path, $append)
{
file_create_path($save_path);
if ($append) {
$file = fopen($save_path, "a+");
if ($file) {
fwrite($file, $content);
fclose($file);
}
} else {
file_put_contents($save_path, $content);
}
}
// 默认的curl封装
function curl_do($url, $close_after_use = true)
{
CT_log("curl_do: " . $url);
$ch = curl_init();
$options = array(
CURLOPT_HEADER => 0,
CURLOPT_POST => 0, // 请求方式为POST
CURLOPT_URL => $url, // 请求URL
CURLOPT_RETURNTRANSFER => 1, // 获取请求结果
CURLOPT_TIMEOUT_MS => 30000, // 超时时间(ms)
CURLOPT_POSTFIELDS => http_build_query(array()), // 注入接口参数
CURLOPT_SSL_VERIFYPEER => 0, // 不验证证书
);
curl_setopt_array($ch, $options);
curl_setopt($ch, CURLOPT_ENCODING, "gzip");
$response = curl_exec($ch);
$err = curl_error($ch);
if ($err) {
CT_log("curl error: " . $err);
}
if ($close_after_use) {
curl_close($ch);
}
return array("handle" => $ch, "response" => $response, "err" => $err);
}
/**
* url拼接,没有处理user,pass两个components
* 详细定义参见单元测试
* @param $url
* @param $base
* @return string
*/
function get_absolute_url($url, $base)
{
// 两种情况直接返回$url:
if (!$base)
return $url;
$url_host = parse_url($url, PHP_URL_HOST);
if ($url_host) {
return $url;
}
$base_parsed = parse_url($base);
$base_scheme = get_safe($base_parsed, "scheme", "");
$base_host = get_safe($base_parsed, "host", "");
$base_port = isset($base_parsed["port"]) ? ":" . $base_parsed["port"] : "";
$base_path = get_safe($base_parsed, "path");
if ($base_host) {
$base_calc = $base_scheme . "://" . $base_host . $base_port;
if (startsWith($url, "/")) {
return $base_calc . $url;
} else if ($base_path) {
$pos = strrpos($base_path, "/");
if ($pos !== false) {
$dir = substr($base_path, 0, $pos + 1); // with last "/"
return $base_calc . $dir . $url;
} else {
return $base_calc . "/" . $url;
}
} else {
return $base_calc . "/" . $url;
}
} else {
if (startsWith($url, "/")) {
return $url;
} else if ($base_path) {
$pos = strrpos($base_path, "/");
if ($pos !== false) {
$dir = substr($base_path, 0, $pos + 1); // with last "/"
return $dir . $url;
} else {
return $url;
}
} else {
return $url;
}
}
}
function get_absolute_url_tests()
{
// empty tests
$tests[] = [null, null, null];
$tests[] = ["", null, ""];
$tests[] = [null, "", null];
$tests[] = ["", "", ""];
$tests[] = [null, "/", "/"];
$tests[] = [null, "/a", "/"];
$tests[] = [null, "/a/", "/a/"];
$tests[] = ["a.html", "b", "a.html"];
$tests[] = ["a.html", "", "a.html"];
$tests[] = ["a.html", "/", "/a.html"];
$tests[] = ["a/b/c/a.html", "http://1.1.1.1", "http://1.1.1.1/a/b/c/a.html"];
$tests[] = ["a.html", "http://1.1.1.1:83", "http://1.1.1.1:83/a.html"];
$tests[] = ["a.html", "http://1.1.1.1:83/", "http://1.1.1.1:83/a.html"];
$tests[] = ["a.html", "http://1.1.1.1:83/a/b", "http://1.1.1.1:83/a/a.html"];
$tests[] = ["a.html", "http://1.1.1.1:83/?", "http://1.1.1.1:83/a.html"];
$tests[] = ["a.html", "http://1.1.1.1:83?", "http://1.1.1.1:83/a.html"];
$tests[] = ["a.html", "http://1.1.1.1:83?a=b", "http://1.1.1.1:83/a.html"];
$tests[] = ["a.html", "https://1.1.1.1:83?a=b#1", "https://1.1.1.1:83/a.html"];
$tests[] = ["a.html", "www.baidu.com?a=b#1", "a.html"]; // www被认为是path
// starts with "/"
$tests[] = ["/a.html", "b", "/a.html"];
$tests[] = ["/a.html", "", "/a.html"];
$tests[] = ["/a.html", "/", "/a.html"];
$tests[] = ["/a/b/c/a.html", "http://1.1.1.1", "http://1.1.1.1/a/b/c/a.html"];
$tests[] = ["/a.html", "http://1.1.1.1:83", "http://1.1.1.1:83/a.html"];
$tests[] = ["/a.html", "http://1.1.1.1:83/", "http://1.1.1.1:83/a.html"];
$tests[] = ["/a.html", "http://1.1.1.1:83/a/b", "http://1.1.1.1:83/a.html"];
$tests[] = ["/a.html", "http://1.1.1.1:83/?", "http://1.1.1.1:83/a.html"];
$tests[] = ["/a.html", "http://1.1.1.1:83?", "http://1.1.1.1:83/a.html"];
$tests[] = ["/a.html", "http://1.1.1.1:83?a=b", "http://1.1.1.1:83/a.html"];
$tests[] = ["/a.html", "https://1.1.1.1:83?a=b#1", "https://1.1.1.1:83/a.html"];
$tests[] = ["/a.html", "www.baidu.com?a=b#1", "/a.html"]; // www被认为是path
$r = true;
foreach ($tests as $test) {
$abs = get_absolute_url($test[0], $test[1]);
echo $abs . "\n";
if ($abs !== $test[2]) {
$r = false;
break;
}
}
echo "pass: " . $r . "\n";
// return self tests
$tests_self = [];
$tests_self[] = ["http://test.com/a.html", "b", ""];
$tests_self[] = ["http://test.com/a.html", "", ""];
$tests_self[] = ["http://test.com/a.html", "/", ""];
$tests_self[] = ["http://test.com/a/b/c/a.html", "http://1.1.1.1", ""];
$tests_self[] = ["http://test.com/a.html", "http://1.1.1.1:83", ""];
$tests_self[] = ["http://test.com/a.html", "http://1.1.1.1:83/", ""];
$tests_self[] = ["http://test.com/a.html", "http://1.1.1.1:83/a/b", ""];
$tests_self[] = ["http://test.com/a.html", "http://1.1.1.1:83/?", ""];
$tests_self[] = ["http://test.com/a.html", "http://1.1.1.1:83?", ""];
$tests_self[] = ["http://test.com/a.html", "http://1.1.1.1:83?a=b", ""];
$tests_self[] = ["http://test.com/a.html", "https://1.1.1.1:83?a=b#1", ""];
$tests_self[] = ["http://test.com/a.html", "www.baidu.com?a=b#1", ""]; // www被认为是path
echo "-------------------return self test--------:\n";
$r = true;
foreach ($tests_self as $test) {
$abs = get_absolute_url($test[0], $test[1]);
echo $abs . "\n";
if ($abs !== $test[0]) {
$r = false;
break;
}
}
echo "pass: " . $r . "\n";
}
/**
* 路径规范化。
* eg:
* a/../b => b
* ../a/../../b => ../b
* ./a/../b => ./b
* @param $path
* @return mixed|string
*/
function normalize_path($path)
{
if (!$path) {
return $path;
}
$path = str_replace("\\", "/", $path);
$dotdotCount = 0; // 以".."开头的处理
$arr = explode("/", $path);
$pathStack = [];
foreach ($arr as $ele) {
if ($ele !== "..") {
array_push($pathStack, $ele);
} else {
if (count($pathStack) === 0) {
$dotdotCount++;
} else {
array_pop($pathStack);
}
}
}
unset($ele);
$r = str_pad("", $dotdotCount, "../");
foreach ($pathStack as $path) {
if ($r === "") {
$r = $path;
} else {
$r .= "/" . $path;
}
}
return $r;
}
// 尝试gbk、utf-8两种编码;优先尝试传入编码
function is_file_ex($path)
{
if (is_file($path)) {
return true;
}
$enc = mb_detect_encoding($path, "gb2312", true);
if ($enc === 'EUC-CN') {
$path2 = iconv("gbk", "utf-8", $path);
} else {
$path2 = iconv("utf-8", "gbk", $path);
}
return is_file($path2);
}
// 尝试gbk、utf-8两种编码;优先尝试传入编码
function file_get_contents_ex($path)
{
if (is_file($path)) {
return file_get_contents($path);
}
$enc = mb_detect_encoding($path, "gb2312", true);
if ($enc === 'EUC-CN') {
$path2 = iconv("gbk", "utf-8", $path);
} else {
$path2 = iconv("utf-8", "gbk", $path);
}
if (is_file($path2)) {
return file_get_contents($path2);
}
return false;
}
// 创建文件lock,如果路径不存在则创建之
function create_file_lock($path, &$output = null)
{
file_create_path($path);
$f = null;
// 不使用"@",这样忽略文件存在的报错,其他异常返回(如权限问题)
try {
$f = fopen($path, "x");
} catch (Exception $e) {
$msg = $e->getMessage();
if (strpos($msg, "File exists") === false && strpos($msg, "文件已存在") === false) {
$output = $e->getMessage() . "\n" . $e->getTraceAsString();
}
}
return $f;
}
// 关闭、释放文件lock
function release_file_lock($f, $path)
{
if ($f) {
fclose($f);
unlink($path);
}
}
/**
* echo udate('Y-m-d H:i:s.u T');
* @param string $format
* @param null $utimestamp
* @return false|string
*/
function udate ($format = 'u', $utimestamp = null)
{
if (is_null($utimestamp))
$utimestamp = microtime(true);
$timestamp = floor($utimestamp);
$milliseconds = round(($utimestamp - $timestamp) * 1000000);
return date(preg_replace('`(?