诚实的小猴子

Hey man, just code it!!!


  • 首页

  • 归档

  • 标签

  • 关于
诚实的小猴子

提升效率(或让编码变得愉悦)的phpstorm常用功能

发表于 2019-05-21

写在开头

当初入行的时候写 ASP 用的是 DreamWeaver ,后来转到 PHP 以后在网上找到了 phpstorm ,用了一段时间以后就彻底喜欢上这款工具了。
除了需要电脑配置高点意外,其它的各种功能都让我的开发过程变得愉悦。并且 phpstorm 是 jetbrains 这家公司开发的,他们家的还有很多其它语言的 IDE,操作快捷键那些都是可以照搬,用熟悉一款以后,当需要其它语言来开发的时候也可以快速上手让 IDE 帮我们完成很多事情。

功能列举(顺序不分先后)

下面我就来列举,一些我在编码过程中,一些给我带来便捷或者好玩儿的 phpstorm 功能。

导航

double-shift

开发过程中经常会遇到需要打开某一个文件修改,在不适用快捷键的情况下一般都是用鼠标去目录树点开对应的文件。
当文件少的时候还好,但是文件比较多目录比较深的时候找起来还是比较麻烦,更好的办法是让 phpstorm 来帮我们找。

  • 万能的 Double Shift

这个应该是我使用 phpstorm 最开始很喜欢用的一个功能了(因为其它的我也不知道呀😂)。
只需要按两下 shift 键就可以呼出搜索框,搜索我们想要的关键字。这个搜索可以搜索很多东西。
比如你输入的可以是文件名,这样就可以不用鼠标点击打开文件了;输入的也可以是方法名,或者函数名

  • 通过类名查找

Double Shift 虽然还是挺好用的,但是当我只想找根据类名找的时候用它会出来很多干扰我的选择项,就像这样:

因为什么类型的结果都有,我需要移动方向键来选择到我的结果,然而要是结果是这样,我就不用移动方向键就能得到我要得结果了。

MacOS Windows
⌘ + O Ctrl + N
  • 通过文件名查找

find-replace

MacOS Windows
⌘ + ⇧ + O Ctrl + ⇧ + N

当我们需要打开某一个文件的时候这个方法很有用,并且可以通过添加 / 来表示目录,这样可以将光标直接定位到该目录在项目目录树的位置。
再比如一个项目中有多个 IndexController,一个在 A 目录下,一个在 B 目录下,可以通过输入 A/IndexController 可以找到 A 目录下的那个文件, B 目录那个一样的道理。

  • 通过记号(Symbol)查找

MacOS Windows
⌘ + ⌥ + O Ctrl + Alt + ⇧ + N

当需要通过某一个函数名来找到对应的文件时候可以考虑使用此功能。
Home::index 这个表达式可以搜索到 HomeController 的 index 方法。
testMethodName 或者这样可以匹配到所有这个方法或函数的文件。

这个搜索有不仅限于函数名, 也能匹配类名,类属性名,以及常量名。如果配置了数据库链接甚至可以匹配到表名,直接打开对应的表。

查找/替换

虽然这个只是一个很普通的功能,但是配合上正则表达式真的可以发挥大作用。

操作 MacOS Windows
查找 ⌘ + F Ctrl + F
替换 ⌘ + R Ctrl + R
全局查找 ⌘ + ⇧ + F Ctrl + ⇧ + F
全局替换 ⌘ + ⇧ + R Ctrl + ⇧ + R

工作中经常会用到 查找/替换 。
举个例子比如有个这样的场景,我需要把某个项目(或者目录)的所有html文件里面形如 {$xxxxxx}, {:xxxxxx},外面再套层花括号,这种情况下如果一个个去改肯定是不可能的,需要用到全局查找替换。

配合上正则表达式和文件名的后缀过滤,可以轻松搞定,如图:
①勾选上启用正则模式
②选择*.html表示在html进行操作
③限制某一个目录(或者也可以直接选择 In Project 从真个项目中操作)。

find-replace

主动呼出代码提示

当我们敲代码的时候会出来自动提示,这时可以选择提示里面的某一个选项。但是有时候我想在不删除我的代码重新敲的情况下呼出这个提示就可以用这个快捷键了。

MacOS Windows
⌃ + Space Ctrl + Space

这本来是个很好用的功能,但是它默认的快捷键和系统输入法切花的冲突了导致无法使用。
可以在设置里面 Keymap->Main menu->Code->Completion->Basic 设置一个不冲突的快捷键就行了我设置的是 ⌃ + ⇧ + C。

测试代码片段

工作中我经常会遇到,需要写一段测试代码,比如我现在有段序列化的字符串,我想发序列化看看里面的内容,并对内容做点修改,然后再序列化回去。
而我又不想把这个代码写到项目文件里面去,这个时候 phpstorm 这个乱写一通的这个功能就派上用场了。按住一下组合键呼出:

MacOS Windows
⌘ + ⇧ + N Ctrl + Alt + Shift + Insert

写好代码直接运行用运行快捷键运行就可以快速的测试我们的代码了。

MacOS Windows
⌃ + ⇧ + R Ctrl + Shift + F10

显示意图行动和快速修复

MacOS Windows
⌥ + ↩ Alt + Enter

phpstorm 当中按住以上组合键可以快速弹出意图动作选项,比如我可以通过此方式快速的创建一个不存在的方法,移除掉没有被使用的命名空间,生成方法注释头(不仅限于这些操作,取决于按下组合键时光标的位置)。

实时模板(Live template)

MacOS Windows 操作
⌘ + ⌥ + J Ctrl + Alt + J 用模板包围
⌘ + J Ctrl + J 插入模板

自带模板

phpstorm 针对不同的文件类型已经预定义了一些模板,通过按上面的快捷键呼出以后,然后输入相应的缩写代码来插入模板。
比如在php类文件中,pubf 可以生成公有的方法, prof 生成保护方法,prif 生成私有方法等等。

自定义模板

我有时候需要记录一下一段代码的执行时间到日志,每次都写觉得比较麻烦,所以我准备定义一个模板。
Settings->Editor->Live Templates

①、点击新增按钮
②、填入缩写,和描述语
③、填入模板内容。
④、声明模板可以使用的文件类型,这里勾选php文件就行。

当中 $XXX$ 都是占位符, $SELECTION$ 最终会被替换成选中的代码块,$VAR0$ 也是个占位符,到时候光标会自动定位到这里要求输入。
如果需要手动输入的不止一个可以用 $VAR0$, $VAR1$ 依次排下去。

最终效果:

该功能不仅限于PHP,其它任何文件类型都可以使用。比如 thinkphp 模板引擎里面的 volist 我们可以这样申明。

<volist name="$VAR0$" id="$VAR1$">
$SELECTION$
</volist>

最终效果:

用模板包围和插入模板的区别在于声明的时候是否使用了 $SELECTION$ 占位符。

文件选项卡间切换

我们在编码(或阅读源码)的时候经常会同时打开很多个文件,需要在各个文件间切换,如果不想每次都拿鼠标点可以考虑以下几个快捷方式。

  • 选中上(下)一个选项卡

当打开了很多个文件的时候可以按住下面的组合键来在文件间切换。

MacOS Windows
⌘ + ⇧ + [ Alt + →
⌘ + ⇧ + ] Alt + ←
  • 显示最近打开过的文件

选项卡间的切换可能并不是那么快捷,有可能中间会经历好几个不是我想要切换到的选项卡。
这种情况可以考虑使用显示最近打开过的文件来跳到特定的文件。

MacOS Windows
⌘ + E Ctrl + E

  • 回退(前进)

有时候其实我们并不需要的是在文件间大跨度切换,而是需要回到(前进)我刚刚光标所在的地方。
这时就可以使用如下操作。

MacOS Windows
⌘ + [ Ctrl + Alt + →
⌘ + ] Ctrl + Alt + ←

windows 下面这个快捷键不知道是不是冲突了,不生效可以尝试修改下成其他的。

版本控制系统

如果你使用了 phpstorm 自带的版本控制系统的图形界面,记住这两个快捷键可能会对你的效率有所提升。

MacOS Windows 操作
⌘ + K Ctrl + K 提交
⌘ + T Ctrl + T 更新
⌘ + ⇧ + T Ctrl + Shift + T 推送(git)

上面除了推送的只对git有效,其它两个 svn 和 git 都是一样的快捷键。

分享个炫酷的插件(active-power-mode)

可以在File->Settings->Plugins里面安装插件,然后重启。

最终效果大概是这样,纯属好玩儿,看个人喜欢了,我用了下就禁用了,看着眼花😂。

结束语

以上是我使用的一些心得,大家如果有在使用过程中用到觉得不错的功能,欢迎补充~

诚实的小猴子

嗯!我自己写(东拼西凑)了个"框架"

发表于 2019-02-14

为什么要自己「写」框架?

我们都知道在 github 有很多优秀的开源项目,有的项目是专注于某一项事情的库。比如某某项目是一个 HTTP 客户端,某某项目是一个处理图片的库,某某项目是一个模板引擎等等。
它们在自己所处领域都是非常优秀的,于是我奔着学习使用这些库的目的,挑选自己喜欢的把他们组成一个简易的自定义框架。

框架的构成

一个完善的框架有很多特性,现在我准备取一些基础必须的来一个个拆解,并选择我喜欢用的扩展包。
我这次组装的框架将会包括如下特性:

  • IOC容器
  • 数据库访问
  • 路由
  • 日志
  • 模板引擎
  • 配置文件加载
  • 调试

元件组装

IOC容器php-di/php-di

框架存在的目的是为了让我们更方便的来开发我们的项目,让我们更容易的写出松耦合便于维护的代码。要起到解耦合作用框架常常是采用的一个依赖注入容器来处理。不管是大名鼎鼎的 spring 还是我们的 laravel 都是这样。所以我来到 packagist.org 找对应的扩展包。分别看了排名前几个的最终挑选了 php-di 。它吸引我的地方是容易上手,并且可以通过注解的方式来注入对象,非常方便。

php-di

  • 创建容器
1
2
3
4
5
6
7
8
<?php

AnnotationRegistry::registerLoader(array($loader, "loadClass"));
$builder = new ContainerBuilder();
$builder->useAnnotations(true);
$container = $builder->build();

//bootstrap.php
  • 通过注解注入对象
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
33
34
35
<?php


namespace App\Controller;


use App\Repository\SettingRepo;
use DI\Annotation\Inject;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Response;

class SettingController
{
/**
* @Inject()
* @var SettingRepo
*/
private $settingRepo;

public function set($params)
{
$this->settingRepo->set($params['key'], $params['value']);
return new Response("Set \"{$params['key']}\" value to \"{$params['value']}\"");
}

public function get($params)
{
return new Response(app(SettingRepo::class)->get($params['key']));
}

public function all()
{
return new JsonResponse($this->settingRepo->all());
}
}

可以看到我并没有对 SettingController 的 settingRepo 属性做任何赋值,我只是给他加了 @Inject() 注解,我就可以使用它了,是不是很方便。

数据库操作doctrine/dbal

要开发项目必然要存取数据,数据库操作免不了,又来到 packagist.org 找到了强大的 doctrine/dbal

doctrine-dbal

  • 先在容器中加入一个数据库连接
1
2
3
4
5
6
7
8
9
10
11
12
13
<?php

$builder->addDefinitions([
'db' => function (\DI\Container $c) {
$config = $c->get('config');
$connectionParams = ['url' => $config['db.url']];

$configuration = new Configuration();
$configuration->setSQLLogger($c->get(\App\Doctrine\DBAL\Logging\QueryFileLogger::class));

return DriverManager::getConnection($connectionParams, $configuration);
}
]);
  • 操作数据库
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
33
34
35
36
37
38
39
40
41
42
<?php

namespace App\Repository;

use DI\Annotation\Inject;
use Doctrine\DBAL\Connection;

class SettingRepo
{
/**
* @Inject("db")
* @var Connection
*/
private $db;

public function get($key)
{
$result = $this->db->createQueryBuilder()->select('value')
->from('setting')
->where("key = :key")
->setParameter('key', $key)
->setMaxResults(1)
->execute()->fetch();
return $result['value'];
}

public function set($key, $value, $description = '')
{
if ($this->get($key)) {
$this->db->update('setting', ['value' => $value], ['key' => $key]);
} else {
$this->db->insert('setting', ['key' => $key, 'value' => $value, 'description' => $description]);
}
}

public function all()
{
return $this->db->createQueryBuilder()->select('*')
->from('setting')
->execute()->fetchAll();
}
}

路由symfony/routing

和以上操作一样,最终挑选了 symfony/routing 作为路由组件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php

$routes = new RouteCollection();
$routes->add('home', new Route("/", ['_controller' => [\App\Controller\WelcomeController::class, 'home']]));

$request = \Symfony\Component\HttpFoundation\Request::createFromGlobals();
$context = new RequestContext();
$matcher = new UrlMatcher($routes, $context);
$parameters = $matcher->matchRequest($request);

$controller = $parameters['_controller'];
if ($controller instanceof Closure) {
$response = $controller($parameters);
} else {
$response = app($controller[0])->{$controller[1]}($parameters);
}
$response->send();

访问首页最终看到 “Welcome!” ,路由成功。

其它组件

其它组件都是按照如上操作一个个加入进来的就不一一介绍详细介绍了。

  • 日志
    开发过程中常常会记录日志,最终选用monolog来记录日志。

  • 模板引擎
    模板引擎必不可少,选用twig

  • 配置文件加载
    使用 hassankhan/config

  • 调试
    写代码肯定会时不时写错,默认的报错信息不方便跟踪,选用 symfony/debug, symfony/var-dumper 调试。

小结

通过一步步的加入各个扩展包可以帮助学习对应的扩展包。也可以更深入的理解到框架都由哪些构成。这里是例子代码。此代码只是做抛砖引玉的作用,感兴趣的朋友可以根据自己的喜好,加入自己喜欢用的扩展包来实现想要的功能。

诚实的小猴子

PHP位运算使用

发表于 2019-01-11

位运算符


如果你正准备看下去,你应该先搞懂各个位运算符的作用。 以下是官网的一个介绍。

例子 名称 结果
$a & $b And(按位与) 将把 $a 和 $b 中都为 1 的位设为 1。
$a | $b Or(按位或) 将把 $a 和 $b 中任何一个为 1 的位设为 1。
$a ^ $b Xor(按位异或) 将把 $a 和 $b 中一个为 1 另一个为 0 的位设为 1。
~ $a Not(按位取反) 将 $a 中为 0 的位设为 1,反之亦然。
$a << $b Shift left(左移) 将 $a 中的位向左移动 $b 次(每一次移动都表示“乘以 2”)。
$a >> $b Shift right(右移) 将 $a 中的位向右移动 $b 次(每一次移动都表示“除以 2”)。

详情请点击【这里】了解。

平常开发需要用位运算吗?


注:以下所有说到第几位都是从0位开始数,所有2进制都是抹去了高位为0的位只保留了用于对比的那几位。

之前我一直以为对我平常开发来说我并不需要用到位运算符,我觉得这东西需要做很复杂的操作才会用到。
但是在我用了很多次json_encode($array, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT)这个函数以后,我开始好奇为什么只用一个参数就可以控制json字符串输出的两个设置,既能格式化输出还能不把内容编码成\uXXXX可以更直观的看到中文是什么内容。

于是我分别打印了JSON_UNESCAPED_UNICODE和JSON_PRETTY_PRINT的值,他们分别是256和128,开始十进制开不出个所以然。于是我对比了他们的二级制值。

常量 二进制数值 10进制数值
JSON_UNESCAPED_UNICODE 0b100000000 256
JSON_PRETTY_PRINT 0b010000000 128
JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT 0b110000000 384

可以看到从有往左数256第8位是1,而128第七位是1,通过按位或运算以后7位和8位都成了1。那函数内部就可以只需要判断json_encode的第二个参数的二级制数第8位如果是1就是JSON_UNESCAPED_UNICODE为真,第7位如果是1就是JSON_PRETTY_PRINT为真了。

于是我又回想了还有哪个地方用了位操作符,一下又想起了这个函数error_reporting(E_ALL ^ E_WARNING ^ E_NOTICE),对比下二进制值。

注意一下 E_ALL 在 5.4.x 版本以后为32767,早期版本为30719。下面的举例假设 PHP >= 5.4.x

常量 二进制数值 10进制数值
E_ALL 0b111111111111111 32767
E_WARNING 0b000000000000010 2
E_NOTICE 0b000000000001000 8
E_ALL ^ E_WARNING ^ E_NOTICE 0b111111111110101 32757

竖着直观的看下,运算以后第1位和第3位变为0了,也就是说E_WARNING、E_NOTICE被排除掉了。

很酷,一定要用!


到此忽然觉得这玩意儿太酷了,试想一下。比方说我现在要实现一个函数来初始化我家里的灯的开关状态。

错误示范

控制灯还不简单嘛,声明一个函数,一个参数控制一个开关。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?php
function showLight($masterRoom = 0, $livingRoom = 0, $diningRoom = 0, $secondLie = 0, $kitchen = 0) {
echo '主卧', "\t";
echo '客厅', "\t";
echo '餐厅', "\t";
echo '次卧', "\t";
echo '厨房', "\t", PHP_EOL;

echo $masterRoom, "\t";
echo $livingRoom, "\t";
echo $diningRoom, "\t";
echo $secondLie, "\t";
echo $kitchen, "\t";
echo PHP_EOL;
}

//只开主卧灯
showLight(1);
//只开厨房灯
showLight(0, 0, 0, 0, 1);
//开所有灯
showLight(1, 1, 1, 1, 1);

可以看到我当我需要控制厨房灯的时候却要传其它四个参数,太不科学了,要是能要开哪个就传那个参数就好了。
用哪个传那个参数?用数组参数不就行了?

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
<?php
function showLight(array $control)
{
$control = array_merge([
'masterRoom' => 0,
'livingRoom' => 0,
'diningRoom' => 0,
'secondLie' => 0,
'kitchen' => 0,
], $control);

echo '主卧', "\t";
echo '客厅', "\t";
echo '餐厅', "\t";
echo '次卧', "\t";
echo '厨房', "\t", PHP_EOL;

echo $control['masterRoom'], "\t";
echo $control['livingRoom'], "\t";
echo $control['diningRoom'], "\t";
echo $control['secondLie'], "\t";
echo $control['kitchen'], "\t";
echo PHP_EOL;
}

//只开主卧和厨房
showLight(['masterRoom' => 1, 'kitchen' => 1]);

嗯,选择开启了。那我要排除厨房呢?
showLight(['masterRoom' => 1, 'kitchen' => 0, 'livingRoom' => 1, 'diningRoom' => 1, 'secondLie => 1])`,又得输入全部参数了。
我现在还只有5盏灯,要是我是要控制一栋楼的所有灯只排除某一盏灯呢?我去……

再来看看用了位操作以后

先声明一个灯光控制的类,用5个位来表示5盏灯的开关为1则表示开灯。

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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
<?php

class LightControl
{
const TURN_ON_ALL = 0b11111;
const KITCHEN = 0b10000;
const SECOND_LIE = 0b01000;
const DINING_ROOM = 0b00100;
const LIVING_ROOM = 0b00010;
const MASTER_ROOM = 0b00001;

private $options;

public function __construct($options = 0)
{
$this->options = $options;
echo '主卧', "\t";
echo '客厅', "\t";
echo '餐厅', "\t";
echo '次卧', "\t";
echo '厨房', "\t", PHP_EOL;
}

public function getOptions()
{
return $this->options;
}

public function setOptions($options)
{
$this->options = $options;
}

public function showOptions()
{
echo self::getOption($this->options, self::MASTER_ROOM), "\t";
echo self::getOption($this->options, self::LIVING_ROOM), "\t";
echo self::getOption($this->options, self::DINING_ROOM), "\t";
echo self::getOption($this->options, self::SECOND_LIE), "\t";
echo self::getOption($this->options, self::KITCHEN);
}

//获取指定灯的开关状态
private static function getOption($options, $option)
{
return intval(($options & $option) > 0);
}
}
//LightControl.php

我们来看看getOption这个方法。因为我们用了五个位来表示每盏灯的开关状态。
可以看到从右边左数0-4位分别是1的是MASTER_ROOM主卧灯、LIVING_ROOM客厅、DINING_ROOM餐厅、SECOND_LIE次卧、KITCHEN厨房。
所以我们只需要一个方法来获取$options指定位上是否为1就可以确定开关的状态了。
因为$option一定只有个位上是1其它的都是0,所以$options和$option按位与以后如果他们的值大于0的话,它们肯定有一个相同的位都为1,也就是$option的那个位上。

举个例子:

1
2
3
0b11111 $options 5个位上都是1
0b10000 KITCHEN
0b10000 $options & KITCHEN

可以看到0b10000是一定大于0的。

  • 全部关闭
1
2
3
<?php
$lightControl = new LightControl();
$lightControl->showOptions();

输出结果:

1
2
主卧	客厅	餐厅	次卧	厨房	
0 0 0 0 0

  • 全部打开
1
2
3
<?php
$lightControl = new LightControl(LightControl::TURN_ON_ALL);
$lightControl->showOptions();

输出结果:

1
2
主卧	客厅	餐厅	次卧	厨房	
1 1 1 1 1
  • 排除厨房
1
2
3
<?php
$lightControl = new LightControl(LightControl::TURN_ON_ALL ^ LightControl::KITCHEN);
$lightControl->showOptions();

输出结果:

1
2
主卧	客厅	餐厅	次卧	厨房	
1 1 1 1 0

常量 二进制数值
LightControl::TURN_ON_ALL 0b11111
LightControl::KITCHEN 0b10000
LightControl::TURN_ON_ALL ^ LightControl::KITCHEN 0b01111

LightControl::TURN_ON_ALL ^ LightControl::KITCHEN的值为0b01111除了第4位(也就是厨房灯)其它都是1,成功排除厨房。

  • 厨房和餐厅
1
2
3
<?php
$lightControl = new LightControl(LightControl::KITCHEN | LightControl::DINING_ROOM);
$lightControl->showOptions();

输出结果:

1
2
主卧	客厅	餐厅	次卧	厨房	
0 0 1 0 1

常量 二进制数值
LightControl::KITCHEN 0b10000
LightControl::DINING_ROOM 0b00100
LightControl::KITCHEN | LightControl::DINING_ROOM 0b10100

LightControl::KITCHEN | LightControl::DINING_ROOM的值为0b10100,第2、4位(也就是厨房灯、客厅灯)都是1,选择打开了厨房灯、客厅灯。

可以看到用位操作以后可以灵活的控制灯了,如果要开的灯太多可以用排除法,要开的少可以用选择法。

总结


当需要用一个参数来控制众多只有true、false选项的时候可以考虑用到位运算来实现,可以用来简化参数的传递并且更为灵活。

诚实的小猴子

ubuntu16设置环回网络

发表于 2018-05-10

系统默认带有一个127.0.0.1的换回网络,想添加更多有如下两种方法。

1. 第一种直接通过ifconfig命令添加

添加以前先使用ifconfig -a查看目前都有哪些网络,可以看到分别有eth0,eth1,lo。

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
eth0      Link encap:Ethernet  HWaddr 08:00:27:ac:ea:bc  
inet addr:10.0.2.15 Bcast:10.0.2.255 Mask:255.255.255.0
inet6 addr: fe80::a00:27ff:feac:eabc/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:1037 errors:0 dropped:0 overruns:0 frame:0
TX packets:690 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:93176 (93.1 KB) TX bytes:78768 (78.7 KB)

eth1 Link encap:Ethernet HWaddr 08:00:27:49:50:84
inet addr:192.168.10.12 Bcast:192.168.10.255 Mask:255.255.255.0
inet6 addr: fe80::a00:27ff:fe49:5084/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
TX packets:18 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:0 (0.0 B) TX bytes:1596 (1.5 KB)

lo Link encap:Local Loopback
inet addr:127.0.0.1 Mask:255.0.0.0
inet6 addr: ::1/128 Scope:Host
UP LOOPBACK RUNNING MTU:65536 Metric:1
RX packets:22 errors:0 dropped:0 overruns:0 frame:0
TX packets:22 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:2373 (2.3 KB) TX bytes:2373 (2.3 KB)

然后执行如下命令添加lo:0。

1
ifconfig lo:0 192.168.10.1 netmask 255.255.255.255 up

再使用ifconfig -a查看就回发现多了一个lo:0了, 说明配置成功了。

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
eth0      Link encap:Ethernet  HWaddr 08:00:27:ac:ea:bc  
inet addr:10.0.2.15 Bcast:10.0.2.255 Mask:255.255.255.0
inet6 addr: fe80::a00:27ff:feac:eabc/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:1037 errors:0 dropped:0 overruns:0 frame:0
TX packets:690 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:93176 (93.1 KB) TX bytes:78768 (78.7 KB)

eth1 Link encap:Ethernet HWaddr 08:00:27:49:50:84
inet addr:192.168.10.12 Bcast:192.168.10.255 Mask:255.255.255.0
inet6 addr: fe80::a00:27ff:fe49:5084/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
TX packets:18 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:0 (0.0 B) TX bytes:1596 (1.5 KB)

lo Link encap:Local Loopback
inet addr:127.0.0.1 Mask:255.0.0.0
inet6 addr: ::1/128 Scope:Host
UP LOOPBACK RUNNING MTU:65536 Metric:1
RX packets:22 errors:0 dropped:0 overruns:0 frame:0
TX packets:22 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:2373 (2.3 KB) TX bytes:2373 (2.3 KB)

lo:0 Link encap:Local Loopback
inet addr:192.168.10.11 Mask:255.255.255.255
UP LOOPBACK RUNNING MTU:65536 Metric:1

通过ifconfig在系统重启之后会丢失,可以尝试第二种方式。

2. 编辑/etc/network/interfaces配置文件

用vi打开该配置文件找到如下几行

1
2
3
# The loopback network interface
auto lo
iface lo inet loopback

进行编辑后保存退出,然后重启service networking restart服务,再用ifconfig -a查看,将会看到和第一步配置以后一样的内容

1
2
3
4
5
6
auto lo lo:0
iface lo inet loopback

iface lo:0 inet static
address 192.168.10.11
netmask 255.255.255.255

诚实的小猴子

音乐站跨页面添加歌曲实现

发表于 2017-05-26

今天突然想听听伍佰的歌,就搜了一个音乐站。

然后来到了虾米音乐,找了找到伍佰的歌点了播放,然后新开了一个页面。

新页面就是播放器页面,然后我还想添加更多的歌曲到播放器,然后我把歌曲列表翻页,选中了点击播放,然后播放器那个页面就自动播放了我选中的歌曲。

好神奇,怎么实现的?

我网上搜了搜有说用ajax轮询。

歌曲列表页点击播放的时候发送一个异步请求到服务端。播放器页面轮询是否有新歌曲进来。

但是我查看了网络监控里面点添加歌曲的时候,歌曲列表页并没有网络请求。播放器页面也没有ajax轮询请求。

然后我想到了是不是用了websocket,但是网络ws那一栏也没有信息。

后来看到有人说可能是通过cookie来实现的。

歌曲列表在点击播放的时候修改cookie。播放器页定时检测cookie的变更。

然后我分别获取了,点击播放前后cookie值,果然不一样。

那么,来段测试代码吧。

歌曲列表页面代码:

1
2
3
4
5
6
7
8
9
<script type="text/javascript">
window.onload = function() {
var btn = document.getElementById('playBtn');
btn.addEventListener('click', function() {
document.cookie = 'play_list='+encodeURIComponent('1,2,3');
})
}
</script>
<div id="playBtn">播放选中</div>

播放器页面代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
<script type="text/javascript">
window.onload = function() {
var m = document.cookie.match(/play_list=(.+?)(?=;|$)/),
playList = m ? decodeURIComponent(m[1]) : '';
setInterval(function() {
var m = document.cookie.match(/play_list=(.+?)(?=;|$)/),
newList = m ? decodeURIComponent(m[1]) : '';
if (newList !== playList) {
//@todo 播放列表变更
}
}, 500);
}
</script>

实际实现肯定比这个复杂,涉及到歌曲排序,新增等等问题。示例代码就不实现这些了。

12…8
诚实的小猴子

诚实的小猴子

36 日志
31 标签
© 2019 诚实的小猴子
由 Hexo 强力驱动
主题 - NexT.Mist