Category Archives: PHP & Python & etc

磨刀不误砍柴工-搭建yii2环境

yii2framework

yii2最近的版本稳定度逐渐不错了,准备用yii2小试牛刀。俗话说,磨刀不误砍柴工,搭建一个好的yii2环境,能提高工作效率。

1. OS环境

windows就别了,还是Linux吧,要不各种各样的php lib一来会烦死人。我推荐用VirtuaBox新建一个Linux虚拟机,然后在Linux虚拟机中跑yii环境,并且在Linux中开启Samba共享,这样在PC上就能通过Samba访问yii2的工作目录,直接写代码,全程用vim暂时还做不到。设置Samba的时候,注意要给目录777的权限,否则会有小麻烦。

2. 安装Yii2

这年头,php早就脱离了直接下载framwork放到html目录直接上的阶段,都推荐包管理工具。composer也是yii2推荐的安装工具。下载composer后,在html目录安装Composer Asset Plugin:

php composer.phar global require "fxp/composer-asset-plugin:1.0.0"

在国内用composer也是坑货,以后写一个专门介绍下composer,推荐用国内的源:http://pkg.phpcomposer.com/,否则下载国外源的时候会气死人的。

然后开始下载yii2(官方有basic版本和advance版本,还是用basic版本起步吧):

php composer.phar create-project yiisoft/yii2-app-basic basic 2.0.3

对,下载的很慢很慢,喝杯茶的功夫后,你就能看到yii2 basic模板已经下载完成了。

安装完毕后,建议再安装yii2 JUI组件,参考 http://www.yiiframework.com/doc-2.0/ext-jui-index.html,添加

"yiisoft/yii2-jui": "~2.0.0"

增加后,composer.json的require-dev节点,看起来应该是这个样子

 "require-dev": {
        "yiisoft/yii2-codeception": "*",
        "yiisoft/yii2-debug": "*",
        "yiisoft/yii2-gii": "*",
        "yiisoft/yii2-faker": "*",
        "yiisoft/yii2-jui": "~2.0.0"
    },

[VIM插件系列] Emmet 插件

Emmet是一个很好用的多编辑器插件,我准备给VIM安装这个插件.

1. 下载最新版的Vim-emmet

wget http://www.vim.org/scripts/script.php?script_id=2981

2. 解压并安装

#unzip emmet-vim.zip 
#cd emmet-vim
#cp plugin/emmet.vim ~/.vim/plugin/
#cp autoload/emmet.vim ~/.vim/autoload/
#cp -a autoload/emmet ~/.vim/autoload/

3. 如何用

用vim新建一个html文件,

#vim index.html

在文件内,用insert模式,输入

html:5

然后(ctrl+y ,)(首先一起按下ctrl和y,然后按下”,”键),你能看到,神器起作用了.

更多的用法,请参考 https://raw.githubusercontent.com/mattn/emmet-vim/master/TUTORIAL

bitnami提供的gitlab安装包如何重启

Gitlab
从开始安装Gitlab是一件让人发疯的事情,尤其是不太熟悉Ruby和redis的Linux管理员(据说有Linux系统管理员会花一天时间来安装Gitlab)。bitnami提供了一个gitlab的安装包,非常好用。但是上次安装后一直正常的Gitlab今天登录的时候,web页面打不开了。登录Linux用netstat查看服务,mysql和httpd都没有启动,看起了是gitlab的环境挂了,要重启了。但是好像bitnami并没有在Centos的/etc/init.d/目录下提供服务,所以只能自己手工了。

后来找到了,在gitlab的安装目录下(我的是安装在/opt/gitlab-6.9.2-1)有个ctlscript.sh 直接用ctlscript.sh restart就可以重启gitlab服务,就一切OK了。

抛弃phpmyadmin转投Adminer

phpmyadmin是一个很好的mysql web端管理工具,但是缺点也越来越明显了: 体积太大。目前phpmyadmin的体积已经有30多MB了,并且拷贝的时候上千个小文件导致copy到web项目的时候速度很慢。每次为web项目增加phpmyadmin都变成了一件很痛苦的事情。

Adminer是phpmyadmin很好的替代者,只有一个单独的php文件就能管理mysql数据库,大小才200多kb,并且能够实现phpmyadmin本身的大部分功能。用的时候也很简单,把admier.php拷贝到web目录即可。

优点:
1. 安装简单,文件小
2. 运行速度快

缺点:
1. 功能没有phpmyadmin那么多

db

Database内容预览

select

数据显示

schema

数据结构

sql

SQL命令

Adminer:http://www.adminer.org/

sublime text 排除目录

目前的sublime目录下有几个目录不想在根目录下出现,例如admin目录不想出现,原sublime-project文件如下

{
	"folders":
	[
		{
			"path": "/Users/alexlee/Desktop/project"
		}
	]

}

加入一行 “folder_exclude_patterns”: [“admin”],把admin目录排除出project

{
	"folders":
	[
		{
			"path": "/Users/alexlee/Desktop/project",
			"folder_exclude_patterns": ["admin"]
		}
	]

}

网上的其他page建议的格式有问题,把”folder_exclude_patterns”: [“admin”]放到了“folders”平行的格式,是错误的,是不起作用的。

参考:http://www.sublimetext.com/docs/2/projects.html

最近需要给一个Ecshop的网站添加QQ登录的功能。大致的需求是这样的:

  • 用户点击“QQ登录”并正确授权后,用户能简单的输入信息就能注册Echsop的会员账户
  • 如果用户已经是Echsop的会员的话,可以绑定QQ账户
  • 在注册或绑定的时候,用户输入信息会有提醒邮箱或手机号是否已经被注册过
  • 用户如果用QQ登录绑定过以后,再次点击QQ登录后,会直接登录已绑定的用户的用户中心

你需要做的:

  • 第一步:在QQ conect注册账户,并获得appid和appkey。
  • 第二步:下载我提供的QQ登录模块文件,并保存在网站的根目录中。例如你的网站是www.site.com,QQ登录模块放在www.site.com/oauth中。
  • 第三步:在模块提供的安装部分中,输入你获取的appid和appkey,以及网站的名称和url。
  • 第四步:在Ecshop的首页,放置QQ登录图标并且添加授权链接,格式如下(请把[你的appid]和[你的网站域名]进行响应替换)。用户可以通过这个图标进行登录授权
  • https://graph.qq.com/oauth2.0/authorize?response_type=code&client_id=[你的appid]&redirect_uri=http://[你的网站域名]/oauth/qqlogin.php

安装模块:
文件上传后,在url中打开:/oauth/install/

1

需要填写你的appid和appkey,以及网站的名称和网站url,填写后根据提示删除install文件夹即可。

用户使用:
在用户点击你放在网站首页的QQ登录图标后,会跳转到授权页面:
2

用户接受授权并填写信息后,会返回网站的callback页面中,要求注册:

3

如果用户已经网站用户,直接点击下面的按钮就可以对老用户进行绑定:

5

Ecshop-QQ登录模块下载:oauth.tar

PHP是web的正确方向吗?

php_frameworks
本文的前提是n年前je的那篇神帖《PHP框架的繁荣是正确的发展方向吗?》,09年正是各种rails层出不穷的时间,老旧的PHP也搭上了rails思潮的末班车,各种各样的框架终于让php这颗老树终于发了新芽。

old style的phper认为越来越多的PHP framwork违法了php的发展:quick and dirty,并且冗长臃肿的PHP framwork不仅导致PHP不那么quick,修改起来更加dirty(你说的是Zend framwork吗?或者是Magento吧?呵呵)。在old style的phper的眼里,读取mysql就应该直接用mysqli lib,中framwork提供的DB fucntion太麻烦;各个php页面应该就是直接可以访问的,用index.php做整个framwork的url入口路由看起来是古怪和反人类的。

我是这么认为的:php的framwork化,正好解决了PHP本身的一些问题,是非常不错的趋势;不过那些过于复杂和臃肿的framwork的确是反人类的(Zend framwork,就是说你哪)。在JE的神帖中说的很清楚,PHP相对于Python和Ruby之类的web环境,每次PHP的运行都是独立的一个进程,每次运行完毕后,php-cgi会释放所有本次运行中牵扯到的资源;而Ruby和Python的GGI则不是这样,臃肿的PHP framwork会极大的损害PHP的这个优势。只有那些充分发挥PHP优势,并且能够在Framwork层面对很多调用(db、session等等)进行封装的Framwork才是正道。

Python异步网络探索(一)

异步网络据说能极大的提高网络server的连接速度,所以打算写一个专题,来学习和了解异步网络.因为Python有个非常出名的异步Lib:Twisted,所以就用Python来完成.

OK,首先写一个pythone socket的server段,对开放三个端口:10000,10001,10002.krondo的例子中是每个server绑定一个端口,测试的时候需要分别开3个shell,分别运行.这太麻烦了,就分别用三个Thread来运行这些services.

import optparse
import os
import socket
import time
from threading import Thread
import StringIO

txt = '''1111
2222
3333
4444
'''

def server(listen_socket):
    while True:
        buf = StringIO.StringIO(txt)
        sock, addr = listen_socket.accept()
        print 'Somebody at %s wants poetry!' % (addr,)
        while True:
                try:
                    line = buf.readline().strip()
                    if not line:
                        sock.close()
                        break
                    sock.sendall(line)  # this is a blocking call
                    print 'send bytes to client:%s' % line
                    #sock.close()
                except socket.error:
                    sock.close()
                    break
                time.sleep(1)  #server和client连接后,server会故意每发送一个单词后等待一秒钟后再发送另一个单词


def main():
    ports = [10000, 10001, 10002]
    for port in ports:
        listen_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        listen_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        addres = (str('127.0.0.1'), port)
        listen_socket.bind(addres)
        listen_socket.listen(5)
        print "start listen at:%s" % (port,)
        worker = Thread(target = server, args = [listen_socket])
        worker.setDaemon(True)
        worker.start()


if __name__ == '__main__':
    main()
    while True:
        time.sleep(0.1) #如果不sleep的话,CPU会被Python完全占用了
        pass

下面是一个client,没有才用异步网络,连接这个三个端口的server:

import socket


if __name__ == '__main__':
    ports = [10000, 10001, 10002]
    for port in ports:
        address = (str('127.0.0.1'), port)
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock.connect(address)
        poem = ''
        while True:
            data = sock.recv(4)
            if not data:
                sock.close()
                break
            poem += data
        print poem

Server端运行后:
屏幕快照 2013-03-17 下午11.15.26

在server.py中,故意在每次发送单词后server段等待1秒钟(代表现实情况中可能发生的各种延迟,例如数据库读取、硬盘IO等),这样的话,每个socket连接都需要4秒钟,分别读取三次就需要12秒钟。

sync

但若是在异步socket的情况下,工作的流程就是这样:

async

下面用异步的client来读取,代码如下:

import datetime, errno, optparse, select, socket

def connect(port):
    """Connect to the given server and return a non-blocking socket."""
    address = (str('127.0.0.1'), port)
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.connect(address)
    sock.setblocking(0)
    return sock

def format_address(address):
    host, port = address
    return '%s:%s' % (host or '127.0.0.1', port)

if __name__ == '__main__':
    ports = [10000, 10001, 10002]
    start = datetime.datetime.now()

    sockets = map(connect, ports)
    poems = dict.fromkeys(sockets, '') # socket -> accumulated poem 

    # socket -> task numbers
    sock2task = dict([(s, i + 1) for i, s in enumerate(sockets)])
    sockets = list(sockets) # make a copy

    while sockets:
        #运用select来确保那些可读取的异步socket可以立即开始读取IO
        #OS不停的搜索目前可以read的socket,有的话就返回rlist
        rlist, _, _ = select.select(sockets, [], [])
        for sock in rlist:
            data = ''
            while True:
                try:
                    new_data = sock.recv(1024)
                except socket.error, e:
                    if e.args[0] == errno.EWOULDBLOCK:
                        break
                    raise
                else:
                    if not new_data:
                        break
                    else:
                        print new_data
                        data += new_data

            task_num = sock2task[sock]
            if not data:
                sockets.remove(sock)
                sock.close()
                print 'Task %d finished' % task_num
            else:
                addr_fmt = format_address(sock.getpeername())
                msg = 'Task %d: got %d bytes of poetry from %s'
                print  msg % (task_num, len(data), addr_fmt)

            poems[sock] += data

    elapsed = datetime.datetime.now() - start
    print 'Got poems in %s' %  elapsed

结果只需要4秒就完成了读取任务。效率是刚才同步socket的三倍。对客户端的异步改造主要有两点:

  • 同步模式下,客户端分别创建socket;而在异步模式下,client开始就创建了所有的socket。
  • 通过“sock.setblocking(0)”设置socket为异步模式。
  • 通过Unix系统的select俩返回可读取IO
  • 最为核心的是26行和29行。尤其是29行的select操作返回待读取socket的列表。

参考:
1. An Introduction to Asynchronous Programming and Twisted
2. Python 2.7 Socket Document
3. Python 2.7 select

关于PHP的register_globals的问题

register_globals容易引起PHP的安全性问题,例如下面的例子是用来检验用户是否已经登录,如果登录后就显示数据的php:

<?php
// 检查用户是否登录,如果登录就把$authorized设置为true
if (authenticated_user()) {
    $authorized = true;
}

// 但是因为我们没有在开始的时候设置$authorized为false,
//恶意用户可以通过访问auth.php?authorized=1达到通过验证的效果
// 这样的话,每个人都能看到数据了
if ($authorized) {
    include "/highly/sensitive/data.php";
}
?>

在PHP 4.2.0以后的版本中,register_globals是默认被设置为OFF的,如果有老的代码需要使用register_globals,可以在index中做如下处理:

<?php
// Emulate register_globals on
if (!ini_get('register_globals')) {
    $superglobals = array($_SERVER, $_ENV,
        $_FILES, $_COOKIE, $_POST, $_GET);
    if (isset($_SESSION)) {
        array_unshift($superglobals, $_SESSION);
    }
    foreach ($superglobals as $superglobal) {
        extract($superglobal, EXTR_SKIP);
    }
}
?>

参考:
http://www.php.net/manual/en/security.globals.php
http://www.php.net/manual/en/faq.misc.php#faq.misc.registerglobals

QR代码API

http://phpqrcode.sourceforge.net 是一个开源的php qr代码生成lib,不过本身也奉献了一个QR的api:

http://phpqrcode.sourceforge.net/qrsample.php?data=test&ecc=H&matrix=10

把data=XXX换成你想要的文本即可。