一个更改 hosts 的 PHP 脚本

有这样一个需求,我有多个网址希望在不同的时候对应不同的 ip,如果一个个配 hosts,这工作显得有些繁琐。写了如下脚本来批量更改。

<?php

define('HOST_FILE', 'C:\Windows\System32\drivers\etc\hosts');

$hm = new HostManage(HOST_FILE);

$env = $argv[1];
if (empty($env)) {
        $hm->delAllGroup();
} else {
        $hm->addGroup($env);
}

class HostManage {

        // hosts 文件路径
        protected $file;
        // hosts 记录数组
        protected $hosts = array();
        // 配置文件路径,默认为 __FILE__ . '.ini';
        protected $configFile;
        // 从 ini 配置文件读取出来的配置数组
        protected $config = array();
        // 配置文件里面需要配置的域名
        protected $domain = array();
        // 配置文件获取的 ip 数据
        protected $ip = array();

        public function __construct($file, $config_file = null) {
                $this->file = $file;
                if ($config_file) {
                    $this->configFile = $config_file;
                } else {
                    $this->configFile = __FILE__ . '.ini';
                }
                $this->initHosts()
                        ->initCfg();
        }

        public function __destruct() {
                $this->write();
        }

        public function initHosts() {
                $lines = file($this->file);
                foreach ($lines as $line) {
                        $line = trim($line);
                        if (empty($line) || $line[0] == '#') {
                                continue;
                        }
                        $item = preg_split('/\s+/', $line);
                        $this->hosts[$item[1]] = $item[0];
                }
                return $this;
        }

        public function initCfg() {
                if (! file_exists($this->configFile)) {
                        $this->config = array();
                } else {
                        $this->config = (parse_ini_file($this->configFile, true));
                }
                $this->domain = array_keys($this->config['domain']);
                $this->ip = $this->config['ip'];
                return $this;
        }

        /**
         * 删除配置文件里域的 hosts 
         */
        public function delAllGroup() {
                foreach ($this->domain as $domain) {
                        $this->delRecord($domain);
                }
        }

        /**
         * 将域配置为指定 ip
         * @param type $env
         * @return \HostManage
         */
        public function addGroup($env) {
                if (! isset($this->ip[$env])) {
                        return $this;
                }
                foreach ($this->domain as $domain) {
                        $this->addRecord($domain, $this->ip[$env]);
                }
                return $this;
        }

        /**
         * 添加一条 host 记录
         * @param type $ip
         * @param type $domain
         */
        function addRecord($domain, $ip) {
                $this->hosts[$domain] = $ip;
                return $this;
        }

        /**
         * 删除一条 host 记录
         * @param type $domain
         */
        function delRecord($domain) {
                unset($this->hosts[$domain]);
                return $this;
        }

        /**
         * 写入 host 文件
         */
        public function write() {
                $str = '';
                foreach ($this->hosts as $domain => $ip) {
                        $str .= $ip . "\t" . $domain . PHP_EOL;
                }
                file_put_contents($this->file, $str);
                return $this;
        }

}

示例配置文件如下:

# 域名
[domain]
a.example.com=1 # 请无视这个 =1,因为使用了 parse_ini_file 这个函数来解析,如果后面不带值,就获取不到这条记录了
b.example.com=1
c.example.com=1

# ip 记录
[ip]
local=127.0.0.1
dev=192.168.1.100

使用方法:

php hosts.php local # 域名将指向本机 127.0.0.1
php hosts.php dev # 域名将指向开发机 192.168.1.100
php hosts.php # 删除域名的 hosts 配置

写完后,发现,这明明就是只需要一次查找替换就能完成的工作嘛

小心 foreach 中使用引用,否则可能数据出错

有实例代码如下:

// 有一个产品分为标准版和高级版
$products = [
	'standard' => [
		'price' => 100, // 原价
		'discount' => 0.8, // 折扣
	],
	'advanced' => [
		'price' => 200,
		'discount' => 0.7,
	],
]; 

// 算出节省的金额
foreach ($products as &$product) {
	$product['save'] = $product['price'] * (1 - $product['discount']);
}

// 输出产品价格信息
foreach ($products as $product) {
	print_r($product);
}

 

运行,输出的结果却是:

$ php test_foreach.php
Array
(
    [price] => 100
    [discount] => 0.8
    [save] => 20
)
Array
(
    [price] => 100
    [discount] => 0.8
    [save] => 20
)

 

不知道看官您看出问题没有,找这个问题花了我老长时间,foreach 里面对值使用使用后,loop 结束,值还存在,所以在 foreach 循环结束后应手动 unset 该变量。

改正后的 PHP 代码为:

$products = [
	'standard' => [
		'price' => 100,
		'discount' => 0.8,
	],
	'advanced' => [
		'price' => 200,
		'discount' => 0.7,
	],
]; 

foreach ($products as &$product) {
	$product['save'] = $product['price'] * (1 - $product['discount']);
}
unset($product); // ******** <--注意这里!!! **********

foreach ($products as $product) {
	print_r($product);
}

 

php.net 上关于 foreach 里面使用引用的警告说明如下:

Reference of a $value and the last array element remain even after the foreach loop. It is recommended to destroy it by unset().

新置 vps

前两天将博客放在 SAE 上,速度实在是感觉太慢了。昨晚准备写文一篇时,后台编辑文章竟然都加载不出来,于是购买了一年的 vps,199 元每年,256M 内存,感觉速度还不错。

选择系统的时候安装的是 centos5.5,源里自带的 php mysql,我觉得版本太低,于是,下载源码编译安装,折腾了 2 个小时,最终还是没搞定。编译安装 nginx 倒是很简单。第一次编译 php 时,默认 ./configure 没带任何参数,成功编译安装完成,后想到,需要 enable-fpm 选项啊,于是重新编译。再一次编译的时候 enable 其它一些东西,就出现一些库和依赖,搞烦我了。而下载 mysql 也启动不起来。最终换了 ubuntu 12.04 系统。ubuntu 多爽,一条命令装好所有需要的东西:

sudo apt-get install mysql-server nginx php5 php5-fpm php5-cli php5-gd php5-mcrypt php5-curl

接下来就是配置 nginx 了。先将博客所有文件复制过来,数据库导入,为了方便,网站根目录我放在了 /root/vhosts/upiu.net 下,访问,提示没权限。将 upliu.net 文件夹所有者改为 www-data 问题依旧。再将 upliu.net 权限改为 777 ,还是提示没权限。G 上搜索了一会,后来终于找到问题所在,upliu.net 文件夹权限设置没有问题,但是 www-data 用户没有访问上一层文件夹的权限,/root 和 /root/vhosts 这两个文件夹所有者为 root,www-data 当然无法读取了。

您觉得新主机速度怎么样?

update:2013-11-08 15:27:24 >>>>>>>>>>>

发现如果多刷新几下博客,就会响应很慢很慢,甚至出现 504 gateway time out 错误,php5-fpm 占用内存过大,导致系统反应慢,就连 ssh 都几乎没有响应。将 php5-fpm service 重启一下就好了。暂时解决办法:加了个定时任务,每隔一小时重启一下 php5-fpm 服务。

博客转移到 SAE 上来了

使用的 vps 经常出问题。今天又断开了,于是将博客迁到 SAE 上来了。还有3000多云豆,博客也没什么流量,免费用上一年应该没什么问题吧。

我是这样迁移的:

1、在 SAE 里新建应用。在 wordpress.org 上下载 wordpress 压缩包,解压后,再压缩成没有二级目录 zip 包,这步是为了将 wordpress 文件放在根目录下。上传至 SAE 应用。

2、复制 wp-config-sample.php 为 wp-config.php,编辑设置数据库常量。

3、管理数据库,将原主机备份的 sql 文件通过 phpmyadmin 导入。

4、绑定域名到 SAE 应用,这样可以使用独立域名访问博客了。

5、访问后台试试?已经ok了。

6、利用 svn 将原博客的文件(主题,插件什么的)上传到 SAE

我并没有使用 wordpress4sae ,而是使用 wp 官方包。目前博客一切正常,但应该存在不能更新,不能在后台安装主题和插件。不能使用 wordpress 的上传附件功能的问题。具体能否使用,我还没测试。

【2013-10-29 10:52:32】话说,SAE绑定独立域名后,走日本节点,感觉速度有点慢啊~,访问博客的时候使用 g o A g e n t 明显快多了。

开始使用 NetBeans

暂时抛开 eclipse 了。

需要习惯新的快捷键,界面有稍许不适应,慢慢就好了。

一、快捷键:

  • Ctrl + E 删除行
  • Alt + Shift + F 格式化代码
  • Ctrl + G 转到行
  • Alt + Shift + O 查找文件
  • Ctrl + U L(S) 转换为小(大)写字母

刚才看到 NetBeans 快捷键设置里面可以设置使用 Eclipse 的快捷键。呵呵,不用再去熟悉一个新的 IDE 了,NetBeans 很不错,这两天使用起来很爽(2013-10-29 10:51:54)

NetBeans 真是好用的爆啊,可以关联 ftp,本地文件更改后自动上传 ftp。更奇妙的是,你复制新文件到本地目录,它也会自动给上传到 ftp。不得不说,真是太方便了。

Linux 下更改 BCD 启动项设置

我的笔记本上装有 Windows 7 和 Ubuntu 两个系统,在 win 下使用 EasyBcd 软件给 Ubuntu 增加引导项。

手贱将启动项等待时间设置为跳过,并且默认系统为 Ubuntu,结果 win 进不去了。在 Google 上搜索 linux bcd 关键字想找到可以在 linux 下直接编辑 win 启动设置的方法,无果。折腾了 5 分钟左右,我想到,可以在 ubuntu 下启动一个虚拟机运行 win 7,然后在虚拟机里面运行 EasyBcd 软件更改 Windows 7 所在硬盘的 BCD 文件,更改完成后再覆盖原来的 BCD 文件。果然,问题解决。

其实我还想到一个办法,用 wine 运行 EasyBcd 然后编辑 BCD 文件。此法没做尝试。

深圳电信8M开通,电脑能拨号,但路由器不能拨号连接上解决方法

之前 3 个月的时间都是在蹭网。今天终于开通宽带啦。

昨天晚上在网上填申请单,在淘宝上付款 800 多元。同时在京东商城上购买了一个ADSL猫一个无线路由器。宽带情况是 7 个月,ADSL 8M,下行 8 M,上行 512K。今天下午师傅过来安装,正好下午京东商城买的东西也到了。装好后,师傅用笔记本连接,PPPOE拨号,一切正常上网。然后用路由器,发现拨号不能正确连接。师傅说,可以上网说明网络是好的,路由器连不上可能是路由器的问题,你自己慢慢解决吧。然后,他就走了。尝试多次确定无线路由器不能拨号成功,但是我笔记本电脑确实可以拨号上网,于是拿来室友笔记本,接上,同样拨号连接不上。网上搜索了一下,提到可能是账号与 MAC 地址绑定。于是再次设置路由器,将我的笔记本网卡 MAC 地址,克隆到路由器里(TP-Link 路由器很方便就可以设置,它会提示)。果然,这次拨号就成功了。

说是 8M 的网络,但是还是感觉不快。迅雷下载的时候实打实能够达到 1Mb/s 的速度,但是打开网页感觉不怎么快。师傅测试的时候给我笔记本安装了一个叫做 1000 管家的软件,用这货测速,测出来的也是 8M。实际使用效果还不如大学时 4M 联通的感觉好。用一段时间感觉下再说吧。

以上,完全是一篇水文~

我靠,老子无语了,这像是 8M 的网吗?????(2013-10-14 20:10:46)

网络测试

python2 与 python3 使用 pickle 的区别

这两天晚上在看 python 教程,地址是:http://woodpecker.org.cn/abyteofpython_cn/chinese/index.html

因为之前已经将这个教程看过一遍,所以这次看得很快,基本就是将示例程序下载下来,在本机上跑一遍,没有任何问题,分分钟理解。

python 目前有两个大的分支版本,我电脑(ubuntu)上的版本是 python 2.7.4 和 python 3.3.1 。我喜欢新的,所以选择学习 python3 。(其实两个版本差别也没多大)现在接触到的主要是 print 使用上的不同。刚才看到这里:http://woodpecker.org.cn/abyteofpython_cn/chinese/ch12s02.html ,将这个例程下载下来后,照例先使用 2to3 命令将代码转换为 python3 版本的,然后运行,发现出错,在网上搜索一番后,终于可以跑起来了。代码贴在下面,首先是教程下载下来的脚本:

#!/usr/bin/python
# Filename: pickling.py

import cPickle as p
#import pickle as p

shoplistfile = 'shoplist.data'
# the name of the file where we will store the object

shoplist = ['apple', 'mango', 'carrot']

# Write to the file
f = file(shoplistfile, 'w')
p.dump(shoplist, f) # dump the object to a file
f.close()

del shoplist # remove the shoplist

# Read back from the storage
f = file(shoplistfile)
storedlist = p.load(f)
print storedlist

接下来是我先 2to3 命令处理转换完,然后作出细微更改后的脚本:

#!/usr/bin/env python
# Filename: pickling.py

import pickle as p
#import pickle as p

shoplistfile='shoplist.data'
# the name of the file where we will store the object

shoplist=['apple','mango','carrot']

# Write to the file
# 这里由 python2 的 file 改为 open,python3 运行提示错误 file not defined
# 文件打开方式改为以二进制的方式打开 wb 模式
# 之前 w 模式打开会提示错误 TypeError: must be str, not bytes
f=open(shoplistfile,'wb')
p.dump(shoplist,f) # dump the object to a file
f.close()

del shoplist # remove the shoplist

# Read back from the storage
# 同样的 file 改为 open
# 同样的 这里也是以二进制的方式打开 rb 模式
f=open(shoplistfile, 'rb')
storedlist=p.load(f)
print(storedlist)

position:relative 是个好东西

现在所在的这家公司没有人专门做前端,目前只有PHP开发和一个产品经理一个美工,公司的前台页面工作外包出去了。此为背景。

外包出去的做好的页面发回给我,然后进行页面嵌套,我感觉这样不是很方便,有些细节往往需要再次进行调整,而写好的页面,根本就不敢乱动其 DOM 结构,因为一旦加一个 div ,减一个 span 都经常导致样式出现问题,外包回来的的页面很多 css 样式都与结构层次有关,导致我后面进行改的时候比较困难,所以我尽量不去动原先的结构,而添加 style 标签加入特定样式。当当当挡~ 主角出场,因为经常涉及的元素位置的改动,所以我现在都是用 position:relative 然后再用 top left 等将其移动我需要的位置,这样就可以做到既不更改 DOM 结构,又可以很快捷不会出差错的完成任务,每次这个时候我都会感谢发明这个样式标签的人。

至于公司为什么没有招聘专门做前端的人,我的猜想是,多请一个人需要多开一个人的工资,但是我们的一个产品一般也不会超过10个页面,并且每个页面相似度很高,这就会导致前端的工作比较少,花较多的钱雇一个前端不如将前端的活外包出去,按需付钱咯。

这样做导致的问题就是,我们很痛苦,外包出去给回来的 html 页面,并不一定满足要求,往往我要加一个 span 将特定的元素找出来,给个 class,然后就可以写 javascript 了,问题来了,发现新加进去的 span 已经有了样式,每每这个时候,老子都无比抓狂,我勒个去啊,为毛要给 span 标签指定样式啊,你直接给你需要指定样式的元素给个 class 不行么?同理,页面的很多样式都是与结构相关,比如经常会出现这样的代码

form div.error {/*这里面有一些样式*/}

为了保持页面的一致性,有可能其它地方也要用到 error 的样式,这个时候问题来了,我直接加 class=”error” 是不行的,因为必须是 form 下的 div 的 class=”error” 才能有这个样式,我勒个去去去啊~直接写成 .error 不行么,在前面加这一串后,样式没有通用性啊。

还有的问题就是,一个页面经常要引入 6 个左右的外部 css 文件,再加上 jqueyui 的 css 文件,得有 7 个了,性能什么的不用考虑,估计公司用的服务器资源还很充足,不用考虑连接数,不用考虑压缩 js css ,不用考虑页面大小,因为经常可以看到大段大段的注释在 html 源文件里面。

好的,吐槽完了~

MySQL

一、查看 MySQL 设置的变量值:SHOW VARIABLES; 详情见 SHOW 语法:http://dev.mysql.com/doc/refman/5.5/en/show.html

二、windows 下忘记 root 密码的解决方法:
1、关闭 MySQL 服务
2、cmd 进入 mysqld 所在文件夹
3、cmd 下运行 mysqld –skip-grant-tables # 跳过授权表启动 MySQL 服务
4、打开新的 cmd 窗口
运行 mysql -uroot -p
提示输入密码继续回车 # 此时 root 的密码为空
use mysql # 选择 mysql 库
UPDATE user SET Password=PASSWORD(‘newpassword’) where USER=’root’; # 更改 root 密码
FLUSH PRIVILEGES; # 刷新权限,至此,完成

三、查看 MySQL-Server 的详细信息(加载配置文件的路径)
cmd 模式进入这个目录 \Program Files\MySQL\MySQL Server5.6\bin
运行:mysqld –verbose –help
在输出里面查找 “Default options are read from the following files in the given order” 就可以找到配置文件的路径。
或者,在计算机管理-服务里面找到 MySQL 的服务,查看服务属性,你就会看到加载的配置文件了。

四、开启慢查询

set global long_query_time=2;
set global slow_query_log=1;

五、mysql按编码计算字符长度而不是字节数, varchar(100) 表示容量为100个utf-8编码的字符个数,不是100个字节,插入超过容量长度的字符会被自动截断。如果varchar设定了utf8编码,varchar(2) => 两个汉字,比如[你好]。

六、As of MySQL 5.1.29, the –log-slow-queries option is deprecated and is removed (along with the log_slow_queries system variable) in MySQL 5.6. Instead, use the –slow_query_log option to enable the slow query log and the –slow_query_log_file=file_name option to set the slow query log file name.

七、In MySQL, JOIN, CROSS JOIN, and INNER JOIN are syntactic equivalents (they can replace each other). In standard SQL, they are not equivalent. INNER JOIN is used with an ON clause, CROSS JOIN is used otherwise.

八、重命名表语句:ALTER TABLE `旧表名` RENAME TO `新表名`;

九、今天出现错误:Illegal mix of collations (utf8_general_ci,IMPLICIT) and (utf8_unicode_c,IMPLICIT) for operation ‘=’。两个表在 JOIN 查询时,因为表的编码不一样,导致出现此错误,请将编码改为 utf8_general_ci 即可(2013-11-08 18:29:03)