admin 发布的文章

记忆之北京马拉松

总会有一种冲动,让我们义无反顾,不论生活还是爱情。

前记

  北京马拉松9月20号这天是周日,临行前的周一晚强跑已经把左腿胫骨弄伤了,吓得周二到周五一点运动没敢做,尽量让身体保持静养,希望在抵京前恢复最佳状态,然而周六抵京后先去了国家会议中心领装备溜达了一圈,又绕了一圈去老友家聚聚看看刚出生的小盆友,晚上回到宾馆已是夜里10点,次日早4点半起床收拾准备了,各项状态都不太好,是个教训。21号下午跑完赶到机场后又遇到晚点,等了几个小时辗转周折才回到家,这时已然是夜里12点,匆忙中发封邮件,第二天只能告假在家休息了。
  北马是我的全程首马,在这之前跑过无锡半马,上海半马,还有一些小范围的跑步活动,虽有艰难,却无大碍。这次北京马拉松全程下来,确实是吃了一些苦,只有跑过的人自己才能知道,没跑过这42.195公里的人永远别说懂和理解。
请输入图片描述

前20公里

  前20公里真的可以用so easy来形容...记得第一次跑马时尽往外道跑,不管是官方摄像,还是群众的手机拍照,都会兴奋的冲上去各种摆pose秀,哈哈想来第一次都是兴奋加幼稚的。经历过几次后,这次我尽量沿着内道跑,因为我还是对第一次的全程马拉松怀揣着畏惧的,沿着内道跑可以避开去抢镜头的,和横穿马路去拿补给的人群,尽量保持这自己的节奏,毕竟马拉松是长距离耐力运动,匀速才是最省力的。事实也确实这样,前20公里我甚至都没看到路边的距离指示牌就到了。

20-30公里

  20-30公里这段路程,已经是我身体和内心斗争的时刻了,同时也是身体各部位告警的时候,遇到吃的要赶紧吃点,我也补充了一根香蕉和小块月饼,直接拿了一瓶水在手上,毕竟2.5公里才能遇到一个补给站,又上了一次厕所。然而人一旦停下来,再找回节奏几乎是不可能的,何况又在这种体力透支耗尽的情况下。路上如果遇到朋友一起陪跑的,都会听到相互鼓励 “ 加油,跑到30公里的时候咱们就歇一下”,我的心里其实也是这么想的,到30公里的时候,我就晃晃吧,现在再加下油,给后面争取点时间。总得来说,20-25公里跑走比例7:3,25-30公里已经是5:5了。

30-35公里

  跑过全马的人都知道,全程马拉松的真正开始是在30公里的地方。30-35这段路程已经是我的极限了,左腿巨疼,全靠右腿迈进拖着左腿和身体迈进。30公里的时候周围的群众都说"就剩10公里了加油",31公里的时候旁边的小伙伴们也都在说"加油就剩10公里了",32公里的时候.....我绝望着往前,尤其中间个路口,可以看到奥森路线折返点的人,哇了个去,那群人咋跑那么快,要知道那群人可是比我当时的位置快乐六七公里的样子啊,禽兽,这群牲口啊!没办法,还得咬牙往前啊,不得不说这段路程是身体的绝望,脚底板各种疼啊,走的时候都歪着脚走,尽量不让脚底板触地。30-35公里跑走比例是3:7吧。

35-40公里

  到了35公里就呵呵哒了,环顾四周,能冲的早冲走了,剩下来都和自己差不多的水平的了,这段路程完全是度日如年,路边补给站水没了,降温海绵没了,香蕉月饼没了,就剩火辣辣的太阳了,听说往年这段路程的路线都是奥森公园里走的,让人绝望,幸好几年是外面绕的。路上还遇到收容车从身边驶过,看着车上坐满的人,心里咯噔一下,不会等下就从后面来把我们这批人强收了吧...使出吃奶的力气赶紧再往前移移,吃了那么多苦,这时候被拖上收容车那不是白瞎了吗?同时还看到530的兔子跑过,我勒个去,530过去了,600的兔子是不是马山个就要上来了,那可能就要被关在门外了... 所以这段路程是身体心理各方面都到极限的同时,也自我催眠了一把,其实我心里有数,下面的路程就算是走,应该都能完赛。35-40公里的跑走比例1:9吧。

40-42.195公里

  腰间的手机我自己都不知道其实早已没电自动关机了,幸好我带了运动手表,我知道这最后2公里肯定能完赛,但还是拼了一下下,跑走比例0.5:9.5, 哈哈哈..这时候路边的加油群众也越来越多了,每个人都在给我们打气,加油就省最后1公里了,加油前面就是鸟巢了,加油前面拐弯就到了,加油最后500米,小伙子跑起来呀...你个锤子啊,哥就差用手往前爬了啊大爷!最后100米的时候其实还是跑着冲过终点的,毕竟终点有很多摄像师对不对..

后记

  其实自己还是有点小小的骄傲的,毕竟我坚持下来了,看看身后的人群,我不比他们弱,何况我是发令枪响15分钟后才跑的..这里就不得不吐槽下北马的一些噪点了...

吐槽

  • 19号周六外地跑友刚到北京,真正领装备的高峰期,我中午1点左右到的,居然衣服全剩XL号的了,你让同志们情何以堪,你好歹也是帝都,国际性赛事,就算是你给各种官爷星爷留了个后门,但也确是搓了一点...
  • 起点厕所少,虽然小便池解决了大部分人的需求,但移动厕所太少了,我排了一小时队,到发令枪响了10分钟我还没排上,很多哥们儿都钻女厕所了好么
  • 那什么后10公里的补给太少了,尤其是水啊,那时候完全就是救命,很多哥们儿路边捡起一小半瓶没喝完的水就如获至宝,组委会你造么...
  • 医用喷雾剂太少了,差不多20公里开始吧,路边一问医疗急救的志愿者,喂喷雾还有吗?没了,前面可能有你去问问!锤子啊,临到终点都没再听到有的话。组委会你个扣门啊

不得不说的给力

  • 北马毕竟是首都举办的国际赛事,从我参加过的几场马拉松来对比看,确是逼格最高的一次了。赛前领装备的展览会,各种赠送活动,尤其喜欢那面刻上全体选手姓名的透明玻璃墙,哇喔,一进门就看到了,找到自己名字的时候好兴奋..
  • 路线不得不说很给力,前20公里几乎全是笔直的宽广大道,整体路线平坦没有起伏的过桥路面,尤其还经过天安门前,那种感觉,只能用棒棒哒来形容了...
  • 奖牌无意是本次的亮点,镶金啊同志们,拿在手里沉沉重有木有
  • 赛后恢复区的免费按摩也要点个赞,谢谢给我放松按摩的那位姐姐
  • 赛后恢复区还可以免费领取冰袋,尤其是阿迪提供的冰水泡脚,爽到爆有木有,想起来阿迪的那个吹冷气的风扇,我吹的很爽,但是后来我在机场一摸脸,你个锤子啊,全是盐,你再水里掺了盐是好意,你到是告我一下啊...

整体来说是一场愉快的旅程,如果我的左脚没有手上的话..话说今天我自己都不知道我怎么靠着一只右脚把整个身体拖到公司又拖回家的..同时也嗟叹单身狗悲哀,行动不便的时候身边连个照顾的人都木有啊!

  

请输入图片描述
请输入图片描述
请输入图片描述
请输入图片描述
请输入图片描述
请输入图片描述
请输入图片描述
请输入图片描述

JavaScript语言核心之对象转换为原始值

对象到布尔值


  对象到布尔值的转换非常简单,所有的对象(包括数组和函数)都转换为true。对于包装对象亦是如此:new Boolean(false)是一个对象而不是原始值,它将转换为true
  

对象到字符串和对象到数字


它们的转换是通过调用待转换对象的一个方法来完成的。一个麻烦的事实是,`Javascript`对象有两个不同的方法来执行转换,并且接下来要讨论的特殊场景更加复杂。值得注意的是,这里提到的字符串和数字的转换规则只是用于本地对象(native object)。宿主随想(例如,有Web浏览器定义的对象)根据各自的算法可以转换成字符串和数字。
所有的对象继承了两个转换方法。

第一个是toString()


  • 它的作用是返回一个反应这个对象的字符串。默认的toString()方法并不会返回一个有趣的值(在例6-4中我们发现它非常有用):
({x:1, y:2}).toString()     // "[object Object]"

很多类定义了更多特定版本的toString()方法。例如,

  • 数组类(Array class)的的toString()方法将每个数组元素转换为一个字符串,并在元素之间添加逗号后合并成结果字符串;
  • 函数类(Function class)的toString()方法返回这个函数的实现定义的表示方式。实际上,这里的实现方式是通常是将用户定义的函数转换为Javascript源代码字符串
  • 日期类(Date class)定义的toString()方法返回了一个可读的(可被JavaScript解析的)的日期和时间字符串;
  • RegExp类(RegExp class)定义的toString()方法将RegExp对象转换为表示正则表达式直接量的字符串:
[1,2,3].toString()                  // => "1,2,3"
(function(x) { f(x); }).toString()  // =>"function(x) {\n f(x);\n}"
/\d+/g.toString()                   // "/\d+/g"
new Date(2010,0,1).toString()       // "Fri Jan 01 2010 00:00:00 GMT-0800 (PST)"

另一个转换对象的函数是valueOf()


  • 这个方法的任务并未详细定义:如果存在任意原始值,它将默认将对象转换为表示它的原始值。
  • 对象是复合值,而且大多数对象无法真正的表示为一个原始值,因此默认的valueOf()方法简单的返回对象本身,而不是返回一个原始值。
  • 数组、函数和正则表达式简单的继承了这个默认方法,调用这些类型的实例valueOf()方法会返回它的一个内部表示:1970年1月1日一来的毫秒数
var d = new Date(2010, 0, 1)    // 2010年1月1日(太平洋时间)
d.valueOf()                     // 1262332800000

通过使用我们刚刚讲解过的toString()valueOf()方法,就可以做到对象到字符串和对象到数字的转换了。但需要注意的是,在某些特殊的场景中,JavaScript执行了完全不同的对象到原始值的转换。这些特殊场景在最后会讲到。

对象到字符串的转换过程


  • 如果对象具有toString()方法,则调用这个方法。如果它返回一个原始值,JavaScript将这个值转换为字符串(如果本身不是字符串的话),并返回这个字符串的结果。需要注意的是,原始值到字符串的转换
  • 如果对象没有toString()方法,或者这个方法并不返回一个原始值,那么JavaScript会调用valueOf()方法。如果存在这个方法,则JavaScript调用它。如果返回值是原始值,JavaScript将这个值转换为字符串(如果本身不是字符串的话),并返回这个字符串结果。
  • 否则,JavaScript无法从toString()valueOf()获得一个原始值,因此这时它将抛出一个类型错误异常。

对象到数字的转换过程


  • 和转换字符串的过程类似,只是它会首先尝试使用valueOf()方法
  • 如果对象具有valueOf()方法,侯着返回一个原始值,则JavaScript将这个原始值转换为数字(如果需要的话)并返回这个数字
  • 否则,如果对象具有toString()方法,后者返回一个原始值,则JavaScript将其转换并返回
  • 否则,JavaScript抛出一个类型错误的异常。

对象转换为数字的细节解释了为什么空数组会被转换为数字0以及为什么具有单个元素的数组同样会转换成一个数字。数组继承了默认的valueOf()方法,这个方法返回一个对象而不是一个原始值,因此,数组到数字的转换则调用toString()方法。空数组转换成为空字符串,空字符串转换为数字0。含有一个元素的数组转换为字符串的结果和这个元素转换字符串的结果一样。如果数组只包含一个数组元素,这个数字转换为字符串,再转换为数字。

JavaScript中的 "+" 运算符可以进行数学加法和字符串链接操作。如果他的其中一个操作数是对象,则JavaScript将使用特殊的方法将对象转换为原始值,而不是使用其他的算术运算符的方法执行对象到数字的转化, "==" 相等运算符与此类似。如果将对象和一个原始值比较,则转换将会遵守对象到原始值的转换方式进行。

"+""==" 应用的对象到原始值的转换包含日期对象的一种特殊情形。日期类是JavaScript语言核心中唯一的预先定义类型,它定义了有意义的向字符串和数字类型的转换。对于所有非日期的对象来说,对象到原始值的转换基本上是对象到数字的转换(首先调用valueOf()),日期对象则使用对象到字符串的转换模式,然而,这里的转换和上文讲述的完全一致: 通过valueOf()toString()返回的原始值将被直接使用,而不会被强制转换为数字或字符串。

"==" 一样,"<" 运算符以及其他关系运算符也会做对象到原始值的转换,但要除去日期对象的特殊情形: 任何对象都会首先尝试调用valueOf(),然后调用toString()不管得到的原始值是否直接使用,它都不会进一步被转换为数字或字符串。

"+""==""!=" 和关系运算符是唯一执行这种特殊的字符串到原始值的转换方式的运算符。其他运算符到特定类型的转换都很明确,而且对日期对象来讲也没有特殊情况。例如 -(减号)运算符把它的两个操作数都转换为数字。下面的代码展示了日期对象和 "+""-""==" 以及 ">" 的运算结果:

var now = new Date();       // 创建一个日期对象
typeof (now + 1)            // => "string": "+"将日期转换为字符串
typeof (now - 1)            // => "number": "-"使用对象到数字的转换
now == now.toString()       // => true: 隐式的和显式的字符串转换
now > (now - 1)             // => true: ">"将日期转换为数字

全文主要摘自《JavaScript权威指南》第3.8.3节

PHP的DES加密和RSA签名(兼容java)

主要用于php对接java的接口
rsa签名用SHA1WithRSA算法

<?php
/**
 * DES加密/解密,RSA加密/验签
 * @author jiangwei<jw90098@gmail.com>
 * @version $Id: Crypt.class.php  $
 */

class Crypt
{

    // DES加密
    public function encrypt($str, $key)  {  
        $block = mcrypt_get_block_size('des', 'ecb');  
        $pad = $block - (strlen($str) % $block);  
        $str .= str_repeat(chr($pad), $pad);  
        $str = mcrypt_encrypt(MCRYPT_DES, $key, $str, MCRYPT_MODE_ECB);  
        return base64_encode($str);  
    }  

    // DES解密
    public function decrypt($str, $key) {  
        $str = base64_decode($str);  
        $str = mcrypt_decrypt(MCRYPT_DES, $key, $str, MCRYPT_MODE_ECB);  
        $block = mcrypt_get_block_size('des', 'ecb');  
        $pad = ord($str[($len = strlen($str)) - 1]);  
        return substr($str, 0, strlen($str) - $pad);  
    }  


    // RSA签名(SHA1WithRSA算法)
    public function sign($content, $rsaPrivateKeyPem) {
            $priKey = file_get_contents($rsaPrivateKeyPem);
            $res = openssl_get_privatekey($priKey);
            openssl_sign($content, $sign, $res);
            openssl_free_key($res);
            $sign = base64_encode($sign);
            return $sign;
     }

     // RSA验签
     public function verify($data, $sign, $publicRsaPath)  {
        //读取公钥文件
        $pubKey = file_get_contents($publicRsaPath);
        //转换为openssl格式密钥
        $res = openssl_get_publickey($pubKey);
        //调用openssl内置方法验签,返回bool值
        $result = (bool)openssl_verify($data, base64_decode($sign), $res);
        //释放资源
        openssl_free_key($res);
        //返回资源是否成功
        return $result;
    }
}

php7 废除了 mcrypt_encrypt, 改用 openssl_encrypt

class DES{
    public static function encrypt($str,$key){
        $str = self::pkcs5_pad($str, 8);
        if (strlen($str) % 8) {
            $str = str_pad($str,
                strlen($str) + 8 - strlen($str) % 8, "\0");
        }
        $sign = openssl_encrypt (
            $str,
            'DES-EDE3' ,
            $key,
            OPENSSL_RAW_DATA | OPENSSL_NO_PADDING ,
            ''
        );

        return strtoupper(bin2hex($sign));
    }

    private static function pkcs5_pad($text, $blocksize) {
        $pad = $blocksize - (strlen($text) % $blocksize);
        return $text . str_repeat(chr($pad), $pad);
    }
}

对比示例 java php

模拟易名中国域名的点赞

易名中国的域名有点赞功能,点赞数高可以被推荐,但只能点一次,那么需求就来了

ename.jpg

方法一: ajax请求

与源代码的js类似,需要注意的是跨域问题, 因为请求的二级域名是www,所以也要在www主机页面的控制台(console)里跑,否则会返回非法请求

var praise = 0, num = 123456;   // num为你域名页面的id,在浏览器的url中可以直观看到
var _this = $('#praise');
var url = "http://www.ename.com/auction/praise"+'/' + num + '/' + praise;
// 这是它页面的源代码
$.ajax({
        url:url,
        type:'get',
        dataType:'json',
        success:function(data) {
            switch(data.flag) {
                case 1:
                    _this.find('.praise_amount').html("("+data.count+")");
                    var html = '<div class="dz_tip"><span class="outsideb"></span><span class="insideb"></span>点赞成功</div>';
                        _this.find('.praise_r').after(html);
                    $('.dz_tip').stop().fadeOut(2000, function() {
                        $(this).remove();
                    })
                    praise = data.praise;
                    break;
                case 2:
                default:
                    alert(data.msg);
            }
        }
});
  • 问题: 由于它服务端有对ip的判断,所以这代码只能用一次,而且不利于在linux服务器上跑脚本,所以替换方案来了

方法二: curl模拟

<?php

header('Content-Type: text/html; charset=utf-8');

// $num为你页面域名的id,可以在浏览器的url中直观看到
function run($num, $second = 30){

    $url = "http://www.ename.com/auction/praise/$num";

    $headers['CLIENT-IP'] = '202.103.229.46'; 
    $headers['X-FORWARDED-FOR'] = '202.103.229.46';
    $headers['X-Requested-With'] = 'XMLHttpRequest';    // 重要
    // X-Requested-With:XMLHttpRequest

    $headerArr = array(); 
    foreach( $headers as $n => $v ) { 
        $headerArr[] = $n .':' . $v;  
    }

    $ch = curl_init();
    curl_setopt ($ch, CURLOPT_URL, $url.'/0');
    curl_setopt ($ch, CURLOPT_HEADER, 1);
    curl_setopt ($ch, CURLOPT_HTTPHEADER , $headerArr );  //构造IP
    // curl_setopt ($ch, CURLOPT_REFERER, $url);   //构造来路

    $content = curl_exec($ch);
    curl_close($ch);
    return $content;
}

$res = run(123456);
  • 可以再对其优化下,加个循环把ip随机, 再加个sleep停顿下,再在header中中随机个UA模拟下更像
  • 返回结果如下就成功了
{"flag":1,"msg":"\u70b9\u8d5e\u6210\u529f","count":48,"praise":1}

再次提醒慎用,导致域名被封了概不负责

检测MySQL是否宕机并重启

检测MySQL是否宕机并重启,建立脚本添加到crontab

#!/bin/bash
 
result=`/usr/bin/mysqladmin ping`
expected='mysqld is alive'
 
if [[ "$result" != "$expected" ]]
then
echo "It's dead - restart mysql"
 
# email subject
SUBJECT="[MYSQL ERROR] - Attempting to restart service"
 
# Email To ?
EMAIL="info@endyourif.com"
 
# Email text/message
EMAILMESSAGE="/tmp/emailmessage.txt"
echo "$result was received"> $EMAILMESSAGE
echo "when we were expected $expected" >>$EMAILMESSAGE
# send an email using /bin/mail
mail -s "$SUBJECT" "$EMAIL" < $EMAILMESSAGE
 
sudo /etc/init.d/mysql restart
fi

crontab中5分钟检测一次

*/5 * * * * /<path_to>/scripts/mysql.sh

原文出自http://www.codeproject.com/Articles/988967/Mysql-Uptime-Check-Script

JavaScript黑魔法jother编码之谜

js的黑魔法jother编码之谜

引入


jother编码是我最开始想写的内容,原因有两点:

  1. 原理比较简单,不需要太多关于算法的知识。
  2. 比较有趣,是在对javascript有了很深的理解之后催生的产物。如果你只需要知道jother编码和解码的方法,那么你可以直接跳过文章正文看结论部分。如果你想知道其中的原理那么你可以带着这个疑问和我一起开始jother探索之旅。

在出发前,我们需要做一些准备工作,就如同去沙漠探险需要带上充足的水和干粮一样。有几样东西需要读者准备一下:

  1. javascript匿名函数相关知识。
  2. 递归思想。
  3. javascript变量类型基本知识。
  4. javascript一些基本函数。
  5. 一颗好奇的心。

那么我们现在就出发吧。首先给出一个非官方的jother编码定义:jother是一种运用于javascript语言中利用少量字符构造精简的匿名函数方法对于字符串进行的编码方式。其中少量字符包括:"!"、"+"、"("、")"、"["、"]"、"{"、"}"。只用这些字符就能完成对任意字符串的编码,我们可以得出两个结论:

  1. 递归是不可或缺的。
  2. 编码压缩率肯定是大于100%而且很高,也就是说编码之后的长度比原长度大很多。

我们先来看一个匿名函数的例子:

[function(){
   alert(1)
}()];

使用如果你把上面代码保存到之间,然后把保存的文件以html后缀结尾,并在浏览器中打开,你会看到一个弹框,弹框的内容为"1"。如果你习惯用console.log而不喜欢alert也是可以的。 紧接着我们稍微修改一下原代码:

alert([function(){
   alert(1)
}()]);

保存一下,再次刷新页面,我们可以看到先弹出了"1",后弹出了一个空白的框。

对于这个现象我们的解释是:第一次执行了alert(1),第二次执行了alert(函数)。而函数是一个匿名函数(有返回值),所以就是弹出的就是函数本身的返回值(如果无返回值的函数则是undefined)。如果你注意到了弹框的先后次序,那很好,说明你特别细心,这个原因是由于函数入栈和出栈导致的,在alert函数调用了匿名函数,当然要等待匿名函数先返回,然后自己才能返回。通过这里我们需要注意的是,javascript在alert函数中是可以执行新的函数的,而不仅仅是输出一个字符串。

下面我们再修改一下源代码:

alert([]);

保存执行一下,你看到了什么?依然弹了一个空白的框框,这个就说明[]也是一个匿名函数,而且是最简单的匿名函数,它也执行了。由于函数体都去掉了,所以导致仅仅留下了一个匿名函数的“空壳”。
我们再接着修改源码:

alert(+[]);

你发现了什么?弹出了0,不是吗?因为我们的运算符“+”,对于无法显示出来的空(void)的函数返回值进行了强制类型转换,将其转为了整形的“0”。 道生一,一生二,二生三,三生万物。

我们只有“0”如何生“1”呢?不要急,看下面一个例子:

alert(![]);

运行以后,依然弹框了,弹出了一个false,false是什么?是bool(在javascript里通常是指"boolbean")运算符,为什么变成了false?是因为"!"对其进行了强制类型转换。但是这又有什么用呢?不要急,我们用两个"!"来试试:

alert(!![]);

这次屏幕上弹出了"true",虽然仍然是bool类型,但是已经更加接近"1"了,如何把“true”变为“1”呢?不要看后面的内容,请大家思考一分钟。 我想大部分人都已经有答案了,让我们来验证一下:

alert(+!![]);

对,就是这样!使用加号进行强制类型转换,将true转换为"1"。有了1就好办了,至少其他数字我们都可以表示了,下面来验证一下你的想法:

"+[]",                                            //0
"+!![]",                                           //1
"!![]+!![]",                                       //2
"!![]+!![]+!![]",                                  //3
"!![]+!![]+!![]+!![]",                             //4
"!![]+!![]+!![]+!![]+!![]",                        //5
"!![]+!![]+!![]+!![]+!![]+!![]",                   //6
"!![]+!![]+!![]+!![]+!![]+!![]+!![]",              //7
"!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]",         //8
"!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]"     //9

它很酷,不是吗? 只有数字还不行,我们需要字符,字符串。那么数字如何变为字符串呢?这里就有点文章可做了,我一开始想到的是ascii码,但是如何将ascii码转换成字符呢?我们需要引入函数才行,但是能不能不引入新的函数?我们换一种思路来考虑这件事情:想办法生成一个数组,然后用数组中已有的字符直接指定下标显示。比如我们很容易就能产生的"true"、"false"。我们想要表示"a"的时候直接想办法输出一个"false",然后制定下标为"1"(注意这里"f"的下标为"0",所以"a"就是"1")不就可以了吗?

我们来证实一下:

alert((![])[+!![]]);

怎么样?看到了a吗?如果你回答:“看到了”,那么你肯定没有认真去动手做。这里输出的是“undefined”,这是为什么呢?因为![]表示的是false这个false是bool型的,当我们取一个bool型变量下标的时候javascript是不能允许的,所以这里给了一个undefined。那么我们如何想办法将bool型转为字符串呢?这里用到的是:"![]+[]"。

alert(typeof(![]+[]));

看到了吗?变成了string。这里就是说明了javascript在对bool类型和number类型做“+”运算的时候将其强制转换为了string类型。大家也可以自己动手用alert(typeof(xxx))试着判断一下之前提到的那些类型是否正确。

利用这个办法,我们就可以表示"a"、"e"、"f"、"l"、"r"、"s"、"t"、"u"。当然还有一个很容易得到的字符串——"undefined",又可以丰富一下我们的字典。这样,还是有很多字符无法表示,下面我们就来讨论一种扩展,Object:

alert({});

在javascript中"{}"表示一个最简单的类,如果运行上面的代码,会显示"[object Object]",同样这个object也是不能直接按数组下标获取元素的,我们需要做一个转换:({}+[])

alert(({}+[])[+[]]);

这样就可以取到第0个元素:"["。 做这个扩展其实主要是为了得到一个重要的字符"o",因为我们后面要说的sort函数需要用到它。

扩展

----

首先我们来补充一个前置概念——javascript匿名函数的原生形式

[]['sort']['constructor']('函数体')();

​这样的构造可以执行任意javascript代码。

为了吸引读者眼球,这里先埋下伏笔:

利用jother编码可以在不用字母和数字的情况执行任意js代码,这个在XSS攻击中是十分有用的,唯一的不足就是编码太长了,如何缩短编码,其实还是有些办法的,而且结合真实的攻击环境中可能允许输入一些字符,我们就可以指替换部分代码。比如代码中对“alert”过滤,是否可以利用jother重新编码函数,用匿名函数调用“alert”,在调用“alert”的时候仅替换alert中的r,这样就形成了“ale”+xxx(jother)+“t”的形式。

下面就开始正式介绍如何利用jother编码调用匿名函数,在第一篇内容中我们已经拥有了一些字符串,这些字符串中的每个字符我们都可以利用数组下标定位的方式取到,我们来看一下这些字符都有什么:“true”、“false”、“undefined”、“[object Object]”(注意这里有个很有用的字符空格)。我们来对比一下[]'sort'1;我们还缺少什么?其实已经什么都不缺了。下面我们来构造这样的一个形式,为了直观我直接给出构造的结果:

[][(![]+[])[!![]+!![]+!![]]+({}+[])[+!![]]+(!![]+[])[+!![]]+(!![]+[])[+[]]][({}+[])[!![]+!![]+!![]+!![]+!![]]+({}+[])[+!![]]+({}[[]]+[])[+!![]]+(![]+[])[!![]+!![]+!![]]+(!![]+[])[+[]]+(!![]+[])[+!![]]+({}[[]]+[])[+[]]+({}+[])[!![]+!![]+!![]+!![]+!![]]+(!![]+[])[+[]]+({}+[])[+!![]]+(!![]+[])[+!![]]]((!![]+[])[+!![]]+(!![]+[])[!![]+!![]+!![]]+(!![]+[])[+[]]+({}[[]]+[])[+[]]+(!![]+[])[+!![]]+({}[[]]+[])[+!![]]+({}+[])[!![]+!![]+!![]+!![]+!![]+!![]+!![]]+(![]+[])[!![]+!![]]+({}+[])[+!![]]+({}+[])[!![]+!![]+!![]+!![]+!![]]+(![]+[])[+!![]]+(!![]+[])[+[]]+({}[[]]+[])[!![]+!![]+!![]+!![]+!![]]+({}+[])[+!![]]+({}[[]]+[])[+!![]])()

这个例子实际上等价于:

[]['sort']['constructor']('return location')()

注意这里是return后面的空格就是需要用前面我们提到的[object Object]来取到。

有了这个函数我们就可以获取很多有用的字符了,比如在线的话,就可以取到"http(s)://" 这样就有了新的字符“p”。

有了这个字符以后我们就可以利用escape和unescape函数组合出更多的字母以及特殊符合,例如利用escape(' ');得到“%20”,利用“%”和数字字母组合再unescape得到新的字符。

后面的事情就由你来自由发挥了。

当然构造一些字母的时候还有其他的技巧,比如构造出Infinity 其实是利用数字接近无穷大,原理就是想办法达到 e的100000次方,我们在这里就不一一列出了,具体的思路可以参照附件中的jother.js,这是jother发明者写的一段jother encode的demo。

这里为了证明一开始提到的猜想我们给出一个不用任何字母和数字就能实现alert(1)的例子:

[][(![]+[])[!![]+!![]+!![]]+({}+[])[+!![]]+(!![]+[])[+!![]]+(!![]+[])[+[]]][({}+[])[!![]+!![]+!![]+!![]+!![]]+({}+[])[+!![]]+({}[[]]+[])[+!![]]+(![]+[])[!![]+!![]+!![]]+(!![]+[])[+[]]+(!![]+[])[+!![]]+({}[[]]+[])[+[]]+({}+[])[!![]+!![]+!![]+!![]+!![]]+(!![]+[])[+[]]+({}+[])[+!![]]+(!![]+[])[+!![]]]((![]+[])[+!![]]+(![]+[])[!![]+!![]]+(!![]+[])[!![]+!![]+!![]]+(!![]+[])[+!![]]+(!![]+[])[+[]]+[][(![]+[])[!![]+!![]+!![]]+({}+[])[+!![]]+(!![]+[])[+!![]]+(!![]+[])[+[]]][({}+[])[!![]+!![]+!![]+!![]+!![]]+({}+[])[+!![]]+({}[[]]+[])[+!![]]+(![]+[])[!![]+!![]+!![]]+(!![]+[])[+[]]+(!![]+[])[+!![]]+({}[[]]+[])[+[]]+({}+[])[!![]+!![]+!![]+!![]+!![]]+(!![]+[])[+[]]+({}+[])[+!![]]+(!![]+[])[+!![]]]((!![]+[])[+!![]]+(!![]+[])[!![]+!![]+!![]]+(!![]+[])[+[]]+({}[[]]+[])[+[]]+(!![]+[])[+!![]]+({}[[]]+[])[+!![]]+({}+[])[!![]+!![]+!![]+!![]+!![]+!![]+!![]]+({}[[]]+[])[+[]]+({}[[]]+[])[+!![]]+(!![]+[])[!![]+!![]+!![]]+(![]+[])[!![]+!![]+!![]]+({}+[])[!![]+!![]+!![]+!![]+!![]]+(![]+[])[+!![]]+([]+[][(![]+[])[!![]+!![]+!![]]+({}+[])[+!![]]+(!![]+[])[+!![]]+(!![]+[])[+[]]][({}+[])[!![]+!![]+!![]+!![]+!![]]+({}+[])[+!![]]+({}[[]]+[])[+!![]]+(![]+[])[!![]+!![]+!![]]+(!![]+[])[+[]]+(!![]+[])[+!![]]+({}[[]]+[])[+[]]+({}+[])[!![]+!![]+!![]+!![]+!![]]+(!![]+[])[+[]]+({}+[])[+!![]]+(!![]+[])[+!![]]]((!![]+[])[+!![]]+(!![]+[])[!![]+!![]+!![]]+(!![]+[])[+[]]+({}[[]]+[])[+[]]+(!![]+[])[+!![]]+({}[[]]+[])[+!![]]+({}+[])[!![]+!![]+!![]+!![]+!![]+!![]+!![]]+(![]+[])[!![]+!![]]+({}+[])[+!![]]+({}+[])[!![]+!![]+!![]+!![]+!![]]+(![]+[])[+!![]]+(!![]+[])[+[]]+({}[[]]+[])[!![]+!![]+!![]+!![]+!![]]+({}+[])[+!![]]+({}[[]]+[])[+!![]])())[!![]+!![]+!![]]+(!![]+[])[!![]+!![]+!![]])()([][(![]+[])[!![]+!![]+!![]]+({}+[])[+!![]]+(!![]+[])[+!![]]+(!![]+[])[+[]]][({}+[])[!![]+!![]+!![]+!![]+!![]]+({}+[])[+!![]]+({}[[]]+[])[+!![]]+(![]+[])[!![]+!![]+!![]]+(!![]+[])[+[]]+(!![]+[])[+!![]]+({}[[]]+[])[+[]]+({}+[])[!![]+!![]+!![]+!![]+!![]]+(!![]+[])[+[]]+({}+[])[+!![]]+(!![]+[])[+!![]]]((!![]+[])[+!![]]+(!![]+[])[!![]+!![]+!![]]+(!![]+[])[+[]]+({}[[]]+[])[+[]]+(!![]+[])[+!![]]+({}[[]]+[])[+!![]]+({}+[])[!![]+!![]+!![]+!![]+!![]+!![]+!![]]+(!![]+[])[!![]+!![]+!![]]+(![]+[])[!![]+!![]+!![]]+({}+[])[!![]+!![]+!![]+!![]+!![]]+(![]+[])[+!![]]+([]+[][(![]+[])[!![]+!![]+!![]]+({}+[])[+!![]]+(!![]+[])[+!![]]+(!![]+[])[+[]]][({}+[])[!![]+!![]+!![]+!![]+!![]]+({}+[])[+!![]]+({}[[]]+[])[+!![]]+(![]+[])[!![]+!![]+!![]]+(!![]+[])[+[]]+(!![]+[])[+!![]]+({}[[]]+[])[+[]]+({}+[])[!![]+!![]+!![]+!![]+!![]]+(!![]+[])[+[]]+({}+[])[+!![]]+(!![]+[])[+!![]]]((!![]+[])[+!![]]+(!![]+[])[!![]+!![]+!![]]+(!![]+[])[+[]]+({}[[]]+[])[+[]]+(!![]+[])[+!![]]+({}[[]]+[])[+!![]]+({}+[])[!![]+!![]+!![]+!![]+!![]+!![]+!![]]+(![]+[])[!![]+!![]]+({}+[])[+!![]]+({}+[])[!![]+!![]+!![]+!![]+!![]]+(![]+[])[+!![]]+(!![]+[])[+[]]+({}[[]]+[])[!![]+!![]+!![]+!![]+!![]]+({}+[])[+!![]]+({}[[]]+[])[+!![]])())[!![]+!![]+!![]]+(!![]+[])[!![]+!![]+!![]])()(({}+[])[+[]])[+[]]+(!![]+!![]+[])+(!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+[]))+(+!![]+[])+[][(![]+[])[!![]+!![]+!![]]+({}+[])[+!![]]+(!![]+[])[+!![]]+(!![]+[])[+[]]][({}+[])[!![]+!![]+!![]+!![]+!![]]+({}+[])[+!![]]+({}[[]]+[])[+!![]]+(![]+[])[!![]+!![]+!![]]+(!![]+[])[+[]]+(!![]+[])[+!![]]+({}[[]]+[])[+[]]+({}+[])[!![]+!![]+!![]+!![]+!![]]+(!![]+[])[+[]]+({}+[])[+!![]]+(!![]+[])[+!![]]]((!![]+[])[+!![]]+(!![]+[])[!![]+!![]+!![]]+(!![]+[])[+[]]+({}[[]]+[])[+[]]+(!![]+[])[+!![]]+({}[[]]+[])[+!![]]+({}+[])[!![]+!![]+!![]+!![]+!![]+!![]+!![]]+({}[[]]+[])[+[]]+({}[[]]+[])[+!![]]+(!![]+[])[!![]+!![]+!![]]+(![]+[])[!![]+!![]+!![]]+({}+[])[!![]+!![]+!![]+!![]+!![]]+(![]+[])[+!![]]+([]+[][(![]+[])[!![]+!![]+!![]]+({}+[])[+!![]]+(!![]+[])[+!![]]+(!![]+[])[+[]]][({}+[])[!![]+!![]+!![]+!![]+!![]]+({}+[])[+!![]]+({}[[]]+[])[+!![]]+(![]+[])[!![]+!![]+!![]]+(!![]+[])[+[]]+(!![]+[])[+!![]]+({}[[]]+[])[+[]]+({}+[])[!![]+!![]+!![]+!![]+!![]]+(!![]+[])[+[]]+({}+[])[+!![]]+(!![]+[])[+!![]]]((!![]+[])[+!![]]+(!![]+[])[!![]+!![]+!![]]+(!![]+[])[+[]]+({}[[]]+[])[+[]]+(!![]+[])[+!![]]+({}[[]]+[])[+!![]]+({}+[])[!![]+!![]+!![]+!![]+!![]+!![]+!![]]+(![]+[])[!![]+!![]]+({}+[])[+!![]]+({}+[])[!![]+!![]+!![]+!![]+!![]]+(![]+[])[+!![]]+(!![]+[])[+[]]+({}[[]]+[])[!![]+!![]+!![]+!![]+!![]]+({}+[])[+!![]]+({}[[]]+[])[+!![]])())[!![]+!![]+!![]]+(!![]+[])[!![]+!![]+!![]])()([][(![]+[])[!![]+!![]+!![]]+({}+[])[+!![]]+(!![]+[])[+!![]]+(!![]+[])[+[]]][({}+[])[!![]+!![]+!![]+!![]+!![]]+({}+[])[+!![]]+({}[[]]+[])[+!![]]+(![]+[])[!![]+!![]+!![]]+(!![]+[])[+[]]+(!![]+[])[+!![]]+({}[[]]+[])[+[]]+({}+[])[!![]+!![]+!![]+!![]+!![]]+(!![]+[])[+[]]+({}+[])[+!![]]+(!![]+[])[+!![]]]((!![]+[])[+!![]]+(!![]+[])[!![]+!![]+!![]]+(!![]+[])[+[]]+({}[[]]+[])[+[]]+(!![]+[])[+!![]]+({}[[]]+[])[+!![]]+({}+[])[!![]+!![]+!![]+!![]+!![]+!![]+!![]]+(!![]+[])[!![]+!![]+!![]]+(![]+[])[!![]+!![]+!![]]+({}+[])[!![]+!![]+!![]+!![]+!![]]+(![]+[])[+!![]]+([]+[][(![]+[])[!![]+!![]+!![]]+({}+[])[+!![]]+(!![]+[])[+!![]]+(!![]+[])[+[]]][({}+[])[!![]+!![]+!![]+!![]+!![]]+({}+[])[+!![]]+({}[[]]+[])[+!![]]+(![]+[])[!![]+!![]+!![]]+(!![]+[])[+[]]+(!![]+[])[+!![]]+({}[[]]+[])[+[]]+({}+[])[!![]+!![]+!![]+!![]+!![]]+(!![]+[])[+[]]+({}+[])[+!![]]+(!![]+[])[+!![]]]((!![]+[])[+!![]]+(!![]+[])[!![]+!![]+!![]]+(!![]+[])[+[]]+({}[[]]+[])[+[]]+(!![]+[])[+!![]]+({}[[]]+[])[+!![]]+({}+[])[!![]+!![]+!![]+!![]+!![]+!![]+!![]]+(![]+[])[!![]+!![]]+({}+[])[+!![]]+({}+[])[!![]+!![]+!![]+!![]+!![]]+(![]+[])[+!![]]+(!![]+[])[+[]]+({}[[]]+[])[!![]+!![]+!![]+!![]+!![]]+({}+[])[+!![]]+({}[[]]+[])[+!![]])())[!![]+!![]+!![]]+(!![]+[])[!![]+!![]+!![]])()(({}+[])[+[]])[+[]]+(!![]+!![]+[])+(!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+[])))()

注意这里我们由于用到了对'('、')'的编码,其实就要用到escape和unescape,所以要在http(s)://协议下面使用,如果是本地文件那么会取到"file://"导致无法正常执行。 最后给出jother编码和解码的方法:

  • 编码:我想如果你仔细阅读了全文,这个问题对你来说应该很简单了。
  • 解码:直接使用

alert(xxx)、console(xxx)、document.write(xxx)即可(xxx为编码内容)。

  • 思考: 中文如何弄出来?
unescape('%u4e2d')    // 中

原文出自: http://drops.wooyun.org/web/4410
附一个编码工具: http://qqxxoo.zz.mu/

php的session超时问题

session通常是根据php.ini中的 session.gc_maxlifetimie 的来控制超时时间,gc回收机制是有概率的,也就是说超时过期后不一定会清掉session

gc回收概率是通过php.ini中的session.gc_probabilitysesssion.gc_divisor决定的;

回收几率 = probability / divisor

session.gc_probability默认值为1; session.gc_divisor默认值为1000;也就是说session默认是1/1000分之一的概率,每1000次才会触发一次回收机制,当访问量小,比如只有一个用户在线时,该用户session超时了也不一定会被清掉,这就不能满足我们程序的严谨性。

解决方案一

修改session.gc_probability的值为1000,这样概率达到100%,每次操作都会触发回收机制,保证无漏网之鱼

解决方案二

为了程序的移植方便,抛弃配置文件自动回收; 通过代码自己验证session时间,可能更为灵活

  • 用户登陆后在当前session里存入当前时间
// login.php
$_SESSION['user']['onlinetime'] = time();
  • 在程序的基类中靠前位置加入判断
// baseClass.php
$nowtime = time();
$outtime = 1800;        // 过期时间为30分钟
if($nowtime - $_SESSION['user']['onlinetime'] > $outtime){
        session_destroy();
}else{
        $_SESSION['user']['onlinetime'] = $nowtime;
}

// ... 后续你肯定有对session的判断,session为空跳登陆页面咯

效率问题

没有对效率实测,个人觉得第二种方案效率略高,而且在相同环境下若存在多个站,能不影响别的站

sql使用for update控制并发

MySQLselect * from table where …... for update的用法

由于InnoDB预设是Row-Level Lock(行级锁),所以只有「明确」的指定主键,MySQL才会执行Row lock (只锁住被选取的资料例) ,否则MySQL将会执行Table Lock (表锁)

举个例子: 假设有个表单t,里面有id跟name二个栏位,id是主键。


CREATE TABLE t (
id int(11) NOT NULL DEFAULT '0',
name varchar(255) DEFAULT NULL,
PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

例1: (明确指定主键,并且有此笔资料,row lock)


  • 由此可见,当明确指定主键,并且有此资料时,锁的是where后面的记录,即这里的id= 1; 接下来来看下没有此资料的情况下会不会被锁。

窗口一:

mysql> begin;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from t where id = 1 for update;
id name
1 bingo
1 row in set (0.00 sec)

窗口二:

mysql> select * from t where id = 1;
id    name
1    bingo
1 row in set (0.01 sec)
mysql> update t set name = 'xxm' where id = 2;
Query OK, 1 row affected 
(0.08 sec)
Rows matched: 1 Changed: 1 Warnings: 0
mysql> update t set name = 'icey' where id = 1;
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction

例2: (明确指定主键,若查无此笔资料,无lock)


  • 由此得出结论,在没有此资料的情况下,即使你for update也是不锁的

窗口1:

mysql> begin;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from t where id = 11 for update;
Empty set (0.00 sec)

窗口2:

mysql> update t set name = 'qweq' where id = 1;
Query OK, 1 row affected (0.04 sec)
Rows matched: 1 Changed: 1 Warnings: 0
mysql> update t set name = 'qw' where id = 2;
Query OK, 1 row affected (0.06 sec)
Rows matched: 1 Changed: 1 Warnings: 0
mysql> update t set name = 'vqw' where id = 3;
Query OK, 1 row affected (0.05 sec)
Rows matched: 1 Changed: 1 Warnings: 0
mysql> update t set name = 'vqws' where id = 4;
Query OK, 1 row affected (0.04 sec)
Rows matched: 1 Changed: 1 Warnings: 0
mysql> update t set name = 'vqs' where id = 5;
Query OK, 1 row affected (0.03 sec)
Rows matched: 1 Changed: 1 Warnings: 0
mysql> update t set name = 'vqs' where id = 11;
Query OK, 0 rows affected (0.00 sec)
Rows matched: 0 Changed: 0 Warnings: 0
mysql> select * from t;
+----+------+

id    name
1    qweq
2    qw
3    vqw
4    vqws
5    vqs

例3: (无主键,table lock)


  • 由此可见,没有明确主键的情况下锁全表;

窗口1:

mysql> select * from t where name ='qweq' for update;
id    name
1    qweq
1 row in set (0.00 sec)

窗口2:

mysql> update t set name = 'vqs' where id = 1;
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
mysql> update t set name = 'vqs' where id = 2;
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction

例4: (主键不明确,table lock)


  • 在主键不明确的情况下,锁全表
SELECT * FROM products WHERE id<>'3' FOR UPDATE;

窗口1:

mysql> select * from t where id <> 2 for update;
id    name
1    qweq
3    vqw
4    vqws
5    vqs
4 rows in set (0.00 sec)

窗口2:

mysql> update t set name = 'vqs' where id = 1;
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
mysql> update t set name = 'vqs' where id = 2;
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction

例5: (主键不明确,table lock)


  • 锁全表

窗口1:

mysql> begin;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from t where id like 3 for update;
id    name
3    vqw
1 row in set (0.00 sec)

窗口2:

mysql> update t set name = 'vqs' where id = 1;
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
mysql> update t set name = 'vqs' where id = 2;
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction

注1: FOR UPDATE仅适用于InnoDB,且必须在交易区块(BEGIN/COMMIT)中才能生效。

一些常用php的header头

<?php 
header('HTTP/1.1 200 OK'); // ok 正常访问
header('HTTP/1.1 404 Not Found'); //通知浏览器 页面不存在
header('HTTP/1.1 301 Moved Permanently'); //设置地址被永久的重定向 301
header('Location: http://www.ithhc.cn/'); //跳转到一个新的地址
header('Refresh: 10; url=http://www.ithhc.cn/'); //延迟转向 也就是隔几秒跳转
header('X-Powered-By: PHP/6.0.0'); //修改 X-Powered-By信息
header('Content-language: en'); //文档语言
header('Content-Length: 1234'); //设置内容长度
header('Last-Modified: '.gmdate('D, d M Y H:i:s', $time).' GMT'); //告诉浏览器最后一次修改时间
header('HTTP/1.1 304 Not Modified'); //告诉浏览器文档内容没有发生改变

###内容类型### 
header('Content-Type: text/html; charset=utf-8'); //网页编码 
header('Content-Type: text/plain'); //纯文本格式 
header('Content-Type: image/jpeg'); //JPG、JPEG 
header('Content-Type: application/zip'); // ZIP文件 
header('Content-Type: application/pdf'); // PDF文件 
header('Content-Type: audio/mpeg'); // 音频文件 
header('Content-type: text/css'); //css文件
header('Content-type: text/javascript'); //js文件
header('Content-type: application/json'); //json
header('Content-type: application/pdf'); //pdf 
header('Content-type: text/xml'); //xml
header('Content-Type: application/x-shockw**e-flash'); //Flash动画 

###### 

###声明一个下载的文件###
header('Content-Type: application/octet-stream'); 
header('Content-Disposition: attachment; filename="ITblog.zip"'); 
header('Content-Transfer-Encoding: binary'); 
readfile('test.zip');
######

###对当前文档禁用缓存###
header('Cache-Control: no-cache, no-store, max-age=0, must-revalidate'); 
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT'); 
######

###显示一个需要验证的登陆对话框### 
header('HTTP/1.1 401 Unauthorized'); 
header('WWW-Authenticate: Basic realm="Top Secret"'); 
######


###声明一个需要下载的xls文件###
header('Content-Disposition: attachment; filename=ithhc.xlsx');
header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
header('Content-Length: '.filesize('./test.xls')); 
header('Content-Transfer-Encoding: binary'); 
header('Cache-Control: must-revalidate'); 
header('Pragma: public'); 
readfile('./test.xls'); 
######


?>

life by php

<?php

$days = rand(1, 9999999); //生命里有各种不测
$problems = rand(0, 9999999); //人生充满了各种问题

while ($days){ //只要活着
    $days--; //日子就要一天天走着
    (rand(0, 9999999)%2) ? $problems++ : $problems--; //或许问题总会解决
}

die(); //但总有一天你会离开
echo '时间不会给你足够的时间领悟';