php拾遗

php中的三目运算

// 可省略第一个,写了这么长时间的php,还真不知道,以前只知道C#的双问号,还特地搜过php是否有类似的操作符的
// laravel uri.php 56行
trim($uri, '/') ?: '/';

array_diff

两个单元仅在 (string) $elem1 === (string) $elem2 时被认为是相同的。

不直接使用 array_filter,是因为它默认根据元素的bool值来的

<?php
if(0) {
    var_dump((string)true); // "1"
    var_dump((string)false); // ""
}
// laravel uri.php 102行
array_diff($segments, array(''));</code></pre><h3>php对象链</h3><pre class="prettyprint"><code class="language-php">// php5.3会出错, 5.4正常:(new A())->a();
// laravel helpers.php 499行
function with($object) {
    return $object;
}

with(new A()) -> a();

svn的authz配置

设置svn目录

/project
├── trunk
├── tags
├── branches

  • super 最大级别的比如boss,不需要开发,所有的给读的权限
  • manager 项目的管理员,有读写权限,有打tag的权限
  • developer 开发人员,有读写权限,没打tag的权限
  • packer 打包人员,只有读tag的权限

[groups]
g_super = aaa
g_manager = bbb
g_developer = ccc,ddd
g_packer = eee


[project:/]
@g_super = r
@g_manager = rw
* =

[project:/trunk]
@g_super = r
@g_manager = rw
@g_developer = rw
* =

[project:/branches]
@g_super = r
@g_manager = rw
@g_developer = rw
* =

[project:/tags]
@g_super = r
@g_manager = rw
@g_developer = r
@g_packer = r
* =

php中正确获取程序路径

首先说说$_SERVER['DOCUMENT_ROOT']这个变量,这个变量是由apache的配置文件里面的documentroot决定的(当然你要是用的不是apache,那就是由其他服务器决定的),一般情况之下还是可靠的,近日,用到了apache中的alias,然后alias的目录不在virtualhost的documentroot中,于是alias的目录中用$_SERVER['DOCUMENT_ROOT']来计算文件的引入路径就出错了。

看看几个较流行的框架

Slim

public function root() {
        return rtrim($_SERVER['DOCUMENT_ROOT'], '/') . rtrim($this->request->getRootUri(), '/') . '/';
    }

Laravel

if ( ! defined('DS')) {
	define('DS', DIRECTORY_SEPARATOR);
}

$GLOBALS['laravel_paths']['base'] = __DIR__.DS;

于是slim嗝屁之,laravel依然风骚。由于laravel要求最低是php5.3,而__DIR__是5.3才有的,那在php之前的版本下使用dirname(__FILE__)就好了。

expect的使用

expect可以自动化执行需要交互的命令。

安装

yum -y install expect

使用

创建一个文件test.exp

#!/usr/bin/env expect
spawn php go-pear.phar
expect ":"
send "\n"
interact
expect test.exp

python版本

pip install pexpect
#!/usr/bin/env python
# encoding: utf-8
import pexpect

cmd = 'php go-pear.phar'
child = pexpect.spawn(cmd, timeout=None)
child.expect(':')
child.sendline('')
# child.interact()
child.close()

cocoa项目中嵌入python

添加链接库:
A5C731A1-10A4-46B3-AF78-A32AB4AF9CA2.png

文件结构:
QQ20130107-1.png

代码

main.m

#import 
#import 

int main(int argc, char *argv[]) {
    Py_Initialize();
    // 这里面自己定义一个hello函数(python的)好了
    PyRun_SimpleString("import hmac, binascii\n"
                       "from itertools import izip, cycle\n");
    int result = NSApplicationMain(argc, (const char **)argv);
    Py_Finalize();
    return result;
}

控制器里面直接使用就好了

- (IBAction)runpy:(id)sender {
    PyObject *m, *f, *a, *k;

    m = PyImport_ImportModule("__main__");
    f = PyObject_GetAttrString(m, "hello");
    a = PyTuple_Pack(1, PyString_FromString([[_email stringValue] UTF8String]));
    k = PyObject_CallObject(f, a);
    
    // NSLog(@"%@", [NSString stringWithUTF8String:PyString_AsString(k)]);
    [_keycode setStringValue:[NSString stringWithUTF8String:PyString_AsString(k)]];
    
    Py_DECREF(k);
    Py_DECREF(a);
    Py_DECREF(f);
    Py_DECREF(m);
}

JSONKit杂记

记录

写程序时遇到的几个问题,记录一下。

  • 加到project里面的时候不能加在子目录下
  • 编译的时候需要指定JSONKit.m的compiler flags "-fno-objc-arc"

吐槽

object-c的函数长度真心是伤不起的,截图留念!

sublime text2 svn sftp plugin keygen

第一次写object-c程序,写了好几个小时才写出来这么个小东西。

  1. 反编译pyc文件,查看算法
  2. 写出key生成的算法
  3. 将python嵌入Object-C,得到最终的程序

将email和生成的key写入插件目录下的sublime-setting文件。

"email": "your email",
"product_key": "the key",

下载地址:svn-sftp-keygen.app.zip

2013-03-19 增加 邮件获取注册码

发送任何邮件到 bot[at]tool.lu, 系统会自动生成注册码,并回复到你的发件邮箱

2013-04-24 增加 osx10.7支持,优化算法机

QQ20130424-1.png
下载地址:svn-keygen.app.zip

mac chrome下input submit样式

input submit指定样式不起作用

QQ20121209-1.png

There will be three new appearance constants for buttons. They are push-button, bevel-button and button. input will be using push-button by default. This constant maps to the Aqua system button. When this appearance constant is specified, the only way to disable the Aqua look will be by setting the appearance constant to none (which will give you a completely blank slate on which to build your own button look) or by specifying your own background and border properties. Specifying the background/border will result in the Aqua appearance being disabled and a more platform-neutral look being used.

来自:https://www.webkit.org/blog/28/buttons/

解决办法

/* 指定border或者background就可以了 */
input[type=submit] {
	border: 1px solid #CCC;
	/* background: red; */
}

自动禁用连接数高的IP

python代码

由于最近服务器受到大流量的攻击,于是写了该脚本。

#! /usr/bin/env python
# encoding: utf-8

import os, time

command = "/bin/netstat -antp|grep :80|awk ' ''{print $5}'|awk -F: '{print $1}'|sort -r|uniq -c|sort -n -k1 -r"
maxconn = 150

handle = os.popen(command)
for line in handle:
	fields = line.strip().split(' ')
	if len(fields) != 2:
		continue
	conn, ip = fields
	# 由于我们使用的是nginx作为反向代理,访问apache,所以这里要加上127.0.0.1
	if int(conn) > maxconn and ip != '127.0.0.1':
		print(time.strftime('%Y-%m-%d %X'), conn, ip)
		# 这里只会临时将ip拒绝,当重新启动iptables服务的时候失效
		os.system('/sbin/iptables -I INPUT -s %s/24 -j DROP' % ip)
handle.close()

定时执行

将python文件加入crontab中即可。

SimpleIni修改my.cnf

为了实现自动化修改mysql的配置文件,使用C++程序对改ini样式的文件进行修改

#include "SimpleIni.h"
int main(int argc, char* argv[]) {
	CSimpleIniA ini;
	const char* filename = "/etc/my.cnf";
	ini.SetUnicode();
	ini.LoadFile(filename);
	ini.SetValue("mysqld", "server-id", "1");
	ini.SetValue("mysqld", "binlog-do-db", "mydb");
	ini.SaveFile(filename, false);

	return 0;
}

使用python Queue创建文档格式转换服务

该项目比较简单,只具备文件排队转换,不具有查看队列的功能,使用openoffice和unoconv作为转换程序,程序不提供建立系统服务

使用场景,一般使用thrift建立服务,将该程序挂载在里面。

#!/usr/bin/env python
#encoding: utf-8
#author: xiaozi <245565986@qq.com>
import Queue, threading, os, subprocess
import time

class Converter(object):
	def __init__(self):
		self._tasks = Queue.Queue()
		self._docs = Queue.Queue()
		self._pdfs = Queue.Queue()

		self._toPdfThreads = []
		self._toSwfThreads = []
		self._toPdfThreadNum = 4
		self._toSwfThreadNum = 4

		self._dispatchThread = threading.Thread(target=self.dispatchTask)
		self._dispatchThread.setDaemon(True)

		for i in range(self._toPdfThreadNum):
			pdfThread = threading.Thread(target=self.toPdf)
			pdfThread.setDaemon(True)
			self._toPdfThreads.append(pdfThread)

		for i in range(self._toSwfThreadNum):
			swfThread = threading.Thread(target=self.toSwf)
			swfThread.setDaemon(True)
			self._toSwfThreads.append(swfThread)
		
		self._dispatchThread.start()
		for i in range(self._toPdfThreadNum):
			self._toPdfThreads[i].start()
		for i in range(self._toSwfThreadNum):
			self._toSwfThreads[i].start()

	def addTask(self, path):
                print(path)
		self._tasks.put(path)

	def dispatchTask(self):
		while True:
			path = self._tasks.get()
			if not path:
				break
			ext = os.path.splitext(path)[1][1:].lower()
			if ext == 'doc' or ext == 'docx':
				self._docs.put(path)
			elif ext == 'pdf':
				self._pdfs.put(path)

	def toPdf(self):
		while True:
			path = self._docs.get()
			if not path:
				break
			subprocess.Popen(
				['python', 'unoconv', '-f', 'pdf', path]
			).communicate()
			newpath = os.path.splitext(path)[0] + '.pdf'
			self._pdfs.put(newpath)

	def toSwf(self):
		while True:
			path = self._pdfs.get()
			if not path:
				break
			# do something
			path_target = os.path.splitext(path)[0]
			if not os.path.exists(path_target):
				os.makedirs(path_target)
			subprocess.Popen(['pdf2swf', path, '-o', path_target + '/page%.swf', '-f', '-T', '9', '-t', '-s', 'storeallcharacters']).communicate()
			print(path)

	def close(self):
		self._tasks.put(None)
		self._dispatchThread.join()

		for i in range(self._toPdfThreadNum):
			self._docs.put(None)
		for i in range(self._toPdfThreadNum):
			self._toPdfThreads[i].join()
		# '转换为pdf'线程都结束之后,再结束'转换为swf'的线程
		for i in range(self._toSwfThreadNum):
			self._pdfs.put(None)
		for i in range(self._toSwfThreadNum):
			self._toSwfThreads[i].join()

if __name__ == '__main__':
	converter = Converter()
	converter.addTask('docs/paper.docx')

	time.sleep(3)

	converter.close()

下面是使用thrift建立的service

#!/usr/bin/env python
# encoding: utf-8

import sys, time

import convert

from thrift.transport import TSocket, TTransport
from thrift.protocol import TBinaryProtocol
from thrift.server import TServer

sys.path.append('thrift/gen-py')

from ConvertApi import *
from ConvertApi.ttypes import *

class ServerHandle(ConvertApi.Iface):
	def __init__(self):
		self.converter = convert.Converter()

	def addTask(self, path):
		self.converter.addTask(path)
		return True

def main(port = 1990):
	processor = ConvertApi.Processor(ServerHandle())
	transport = TSocket.TServerSocket(port = port)
	tfactory = TTransport.TBufferedTransportFactory()
	pfactory = TBinaryProtocol.TBinaryProtocolFactory()

	server = TServer.TThreadedServer(processor, transport, tfactory, pfactory)
	print('Starting server on :%d' % port)
	server.serve()

if __name__ == '__main__':
	port = 110110
	main(port)

thrift文件如下(该接口提供的比较简单):

service ConvertApi {
	bool addTask(1: string path);
}

discuzx模块数据调用

后台 > 门户 > 模块管理 > 数据调用

语法

DIY模块模板语法详解

[loop]...[/loop]标签来循环显示模块中的数据;如果要在模板中多处循环可以使用[loop1]...[/loop1]的方式扩展(只能扩展1-9个)

[order=N]...[/order]标签来替代默认loop中的第N个位置数据的展示,你可以使用[order1=N]...[/order1]的方式来扩展(对应[loop1],只能扩展1-9个)

其中:[order=odd]为奇数行,[order=even]为偶数行
[index=N]...[/index]的方式来指定第N条数据的展示方式,由该语法指定的数据不再在loop循环中出现,可以通过[index1]...[/index1]的方式来扩展(只能扩展1-9个)以实现同一条数据多处展示

实例

[index=1]

{title}

{summary} [详细]

[/index]

Openfire + err

安装Openfire

Openfire是IM的服务器端,下载地址:http://www.igniterealtime.org/downloads/index.jsp,选择linux rpm包下载

rpm -ivh openfire-3.7.1-1.i386.rpm

配置Openfire

首先在mysql里面建立一个数据库openfire,访问9090端口,进入网页进行初始化配置,增加一个用户,下面的err配置的时候会用到

安装err

err是一个IM机器人,项目地址:https://github.com/gbin/err,最简单的安装方法easy_install

easy_install err

建立几个需要用到的文件夹

mkdir -p /var/www/err
mkdir -p /var/log/err
mkdir -p /var/lib/err

chmod -R 777 /var/www/err
chmod -R 777 /var/log/err
chmod -R 777 /var/lib/err

cp /usr/lib/python2.6/site-packages/err-1.6.7-py2.6.egg/errbot/config-template.py /var/www/err/config.py

cd /var/www/err/

# 按照自己的要求修改吧,把上面配置的用户加到这个里面
vi config.py

其他

err.py命令启动的时候,可能还会报其他的模块不存在,直接easy_install好了

关于机器人插件的开发,可以参考项目主页上的wiki