分享一下,工作中遇见值得分享的Code,这些代码往往比较有趣、有意义、或者少见(有意义),在此我统称为:酷代码。

IP 白名单检测

场景描述:我司的一个子项目,用来处理全部的支付业务,为了安全,所有的内部请求必须经过 IP 检测(这只是其中一环),分享一下基于我老大(涛哥)思想,利用位运算完成,IP白名单检测方法,以下是核心代码

定义检测函数checkRemoteAddr()

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
/**
 * 验证 IP 白名单
 * @param $ip   string              待验证IP地址
 * @param $AllowRemoteIps array     允许的IP白名单
 * @return bool
 */
function checkRemoteAddr($ip,$AllowRemoteIps)
{
    $ipNum = ip2long($ip);
    foreach ($AllowRemoteIps as $mask)
    {
        @list($maskIp, $maskStep) = explode("/", $mask);
        $maskIpNum = ip2long($maskIp);
        $maskStep = !isset($maskStep) ? 0 : 32 - intval($maskStep);
        if( ($ipNum >> $maskStep) === ($maskIpNum >> $maskStep) )  return true;
    }
    return false;
}

调用

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
$allowRemoteIp = [
    "58.246.60.40",         // 开放指定 IP
    "192.168.10.0/24",      // 验证前3段
    "10.0.1.0/24",          // 验证前3段
    '172.19.2.2/32',        // 验证4段 写法等同于 172.19.2.2
//    '0.0.0.0/0',            // 允许所有 IP
];

$r1 = checkRemoteAddr('58.246.60.40',$allowRemoteIp);
var_dump($r1);
$r2 = checkRemoteAddr('58.246.60.41',$allowRemoteIp);
var_dump($r2);
$r3 = checkRemoteAddr('10.0.1.120',$allowRemoteIp);
var_dump($r3);
$r4 = checkRemoteAddr('10.0.2.120',$allowRemoteIp);
var_dump($r4);

结果

1
2
3
4
bool(true)
bool(false)
bool(true)
bool(false)

一个闭包写法

定义

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
class A
{
    public function test($num){
        $this->run(function ($m) use ($num){
            if ($num == $m) echo '存在 Num '. $num . PHP_EOL;
        });
        echo 'End '. date('Y/m/d H:i:s') . PHP_EOL;
    }

    public function run(callable $callback){
        for ($i=0; $i<=5; $i++){
            // to-do
            $callback($i);
        }
    }
}

调用

1
2
$a = new A;
$a->test(5);

结果

1
2
存在 Num 5
End 2019/05/30 17:48:52

有趣的复数

近日在Yii2框架中写reset api,发现资源类控制定义后的访问形式必须为复数,这个复数呢,不是简单加个s的方式,而是遵循英语的复数规则,感觉很是有意思,带着好奇找了一下Yii2是怎么实现的

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
// vendor/yiisoft/yii2/rest/UrlRule.php

public function init()
{
    $controllers = [];
    foreach ((array) $this->controller as $urlName => $controller) {
        if (is_int($urlName)) {
            $urlName = $this->pluralize ? Inflector::pluralize($controller) : $controller;
        }
        $controllers[$urlName] = $controller;
    }
    ...
}

继续查看puluralize()方法,在/vendor/yiisoft/yii2/helpers/BaseInflector.php文件中看到了真面目

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
/**
    * Converts a word to its plural form.
    * Note that this is for English only!
    * For example, 'apple' will become 'apples', and 'child' will become 'children'.
    * @param string $word the word to be pluralized
    * @return string the pluralized word
    */
public static function pluralize($word)
{
    if (isset(static::$specials[$word])) {
        return static::$specials[$word];
    }
    foreach (static::$plurals as $rule => $replacement) {
        if (preg_match($rule, $word)) {
            return preg_replace($rule, $replacement, $word);
        }
    }

    return $word;
}

从方法中可以看出,如果$word存在于静态属性$specials定义,则返回其对应的value,否则从静态数组$plurals中循环匹配复数规则,命中时根据preg_replace函数翻译成复数,这个两个数组的定义比较大,这里仅贴出部分代码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
public static $specials = [
    'atlas' => 'atlases',
    'beef' => 'beefs',
    'brother' => 'brothers',
    'cafe' => 'cafes',
    'child' => 'children',
    'cookie' => 'cookies',
    'corpus' => 'corpuses',
    'cow' => 'cows',
    'curve' => 'curves',
    'foe' => 'foes',
    ...
    ...
];
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public static $plurals = [
    '/([nrlm]ese|deer|fish|sheep|measles|ois|pox|media)$/i' => '\1',
    '/^(sea[- ]bass)$/i' => '\1',
    '/(m)ove$/i' => '\1oves',
    '/(f)oot$/i' => '\1eet',
    '/(h)uman$/i' => '\1umans',
    '/(s)tatus$/i' => '\1tatuses',
    '/(s)taff$/i' => '\1taff',
    '/(t)ooth$/i' => '\1eeth',
    '/(quiz)$/i' => '\1zes',
    '/^(ox)$/i' => '\1\2en',
    '/([m|l])ouse$/i' => '\1ice',
    '/(matr|vert|ind)(ix|ex)$/i' => '\1ices',
    '/(x|ch|ss|sh)$/i' => '\1es',
    '/([^aeiouy]|qu)y$/i' => '\1ies',
    '/(hive)$/i' => '\1s',
    '/(?:([^f])fe|([lr])f)$/i' => '\1\2ves',
    '/sis$/i' => 'ses',
    '/([ti])um$/i' => '\1a',
    '/(p)erson$/i' => '\1eople',
    '/(m)an$/i' => '\1en',
    '/(c)hild$/i' => '\1hildren',
    '/(buffal|tomat|potat|ech|her|vet)o$/i' => '\1oes',
    '/(alumn|bacill|cact|foc|fung|nucle|radi|stimul|syllab|termin|vir)us$/i' => '\1i',
    '/us$/i' => 'uses',
    '/(alias)$/i' => '\1es',
    '/(ax|cris|test)is$/i' => '\1es',
    '/(currenc)y$/' => '\1ies',
    '/s$/' => 's',
    '/^$/' => '',
    '/$/' => 's',
];

END :)