Xcode5中Objective-c项目嵌入Python

Xcode4中在objc项目中使用Python,只需用引入Python.framework即可。
Xcode5已不能使用Python.framework了,需用通过下边的步骤添加对Python的支持。

$ python2.7-config --cflags
-I/System/Library/Frameworks/Python.framework/Versions/2.7/include/python2.7 -I/System/Library/Frameworks/Python.framework/Versions/2.7/include/python2.7 -fno-strict-aliasing -fno-common -dynamic -arch x86_64 -arch i386 -g -Os -pipe -fno-common -fno-strict-aliasing -fwrapv -mno-fused-madd -DENABLE_DTRACE -DMACOSX -DNDEBUG -Wall -Wstrict-prototypes -Wshorten-64-to-32 -DNDEBUG -g -fwrapv -Os -Wall -Wstrict-prototypes -DENABLE_DTRACE

embedding-python

把-I参数后边的路径复制到Build Settings/Header Search Paths中

$python2.7-config --ldflags
-L/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/config -ldl -framework CoreFoundation -lpython2.7

embedding-python

在Finder中打开-L参数后边的路径,把libpython2.7.dylib 拖拽到Xcode项目的Framework中,在确认框中选择链接文件而不是复制。

可以改用phpredis连接Redis了

之前PHP连接Redis一直用PRedis,现在phpredis被收录进PECL了。
这是phpredis的PECL仓库地址:http://pecl.php.net/package/redis
这说明phpredis已经比较成熟稳定,支持的redis命令更加全面。
并且phpredis是用C写的,效率应该比predis高不少。是改用phpredis的时候了。

安装phpredis

wget http://pecl.php.net/get/redis-2.2.4.tgz
tar zxvf redis-2.2.4.tgz && cd redis-2.2.4
phpize
./configure [--enable-redis-igbinary]
make && make install

用sentry管理PHP错误日志

Sentry是个很好用的错误日志服务器,可以将程序错误的详细情况集中捕获,并提供一个很漂亮的Web界面来浏览错误。
如果你还没用确定Sentry是否适合你,可以先试用Sentry的在线服务https://getsentry.com/welcome/
Sentry本身是用python写的,但它支持记录Python、PHP、Ruby、iOS、Javascript、Java、Node.js等多种语言的错误。
在浏览器中就可以查看项目的错误

Sentry 1

记录了错误调用栈信息,很快定位到错误

Sentry 2

记录了错误发生时浏览器信息,同时还可以记录自定义信息

Sentry 3

安装 Sentry

Sentry需要Python2.5以上版本。注意Sentry并不支持Python3。
$ sudo easy_install -UZ sentry
Sentry默认使用sqlite记录错误日志,生产环境下建议使用MySQL。使用以下命令安装Python的MySQL支持。

$ sudo easy_install -UZ sentry[mysql]

如果安装出错,可以用 sudo easy_install -UZ MySQL-python 命令直接安装Python的MySQL支持,
如果安装MySQL-python出错,安装下列安装包后在进行安装MySQL-python

sudo yum install python-devel
sudo yum install mysql-devel
sudo yum install libxslt-devel libxml2-devel

配置 Sentry

安装好之后使用以下命令建立配置文件:
$ mkdir ~/.sentry
$ sentry init ~/.sentry/sentry.conf.py
然后打开~/.sentry/sentry.conf.py,参照下边配置修改。

daemon: True, 使sentry以daemon模式运行。
sentry默认使用9000端口,我的9000端口已经被php-fpm占用,因此改用9111端口。
SENTRY_URL_PREFIX = ‘http://sentry:9111

SENTRY_WEB_HOST = '0.0.0.0'
SENTRY_WEB_PORT = 9111
SENTRY_WEB_OPTIONS = {
    'daemon':True,
    'workers': 3,  # the number of gunicorn workers
    'secure_scheme_headers': {'X-FORWARDED-PROTO': 'https'},
}

在mysql服务器上新建sentry数据库,修改mysql相关配置
DATABASES = {
‘default’: {
‘ENGINE’: ‘django.db.backends.mysql’,
‘NAME’: ‘sentry’,
‘USER’: ‘sentry’,
‘PASSWORD’: ‘sentry’,
‘HOST’: ‘192.168.100.1’,
‘PORT’: 3306,
}
}
修改好配置文件后,在用户根目录执行sentry start 启动服务器即可。第一次运行需要建立初始化数据,如新建管理员等操作,按照提示操作即可。
使用 sentry help查看更多使用方法。sentry send_fake_data 发生测试数据。
我在第一次启动sentry时遭遇下边的错误提示

DatabaseError: (1071, 'Specified key was too long; max key length is 1000 bytes')

产生这个错误的原因是我的MySQL数据库默认存储引擎是MyISAM。MyISAM存储引擎在创建索引的时候,索引键长度是有一个较为严格的长度限制的,所有索引键最大长度总和不能超过1000,而且不是实际数据长度的总和,而是索引键字段定义长度的总和。

sentry中有些表的索引键长度超过了1k,设置存储引擎为InnoDB即可解决问题。
连接mysql执行下面命令,设置默认存储引擎为InnoDB

SET global storage_engine=InnoDB;

使用Nginx作为前端代理服务器

location / {
  proxy_pass         http://sentry-ipaddress:9111;
  proxy_redirect     off;
  proxy_set_header   Host              $host;
  proxy_set_header   X-Real-IP         $remote_addr;
  proxy_set_header   X-Forwarded-For   $proxy_add_x_forwarded_for;
  proxy_set_header   X-Forwarded-Proto $scheme;
}

使用Sentry的PHP客户端

安装Raven-PHP

  1. Raven-PHP GitHub下载项目,把raven-php/lib/Raven复制到你的项目中
  2. 初始化Raven Cient

    require('/path/to/raven/Autoloader.php');
    Raven_Autoloader::register();
    $dsn = "https://96320c73c71946f2b1499b511a5311bf:ee9dcc397c354942b5295dd00c284e06@sentry:9111/2";
    $client = new Raven_Client();
    
  3. 设置PHP 错误处理的handler

    $error_handler = new Raven_ErrorHandler($client);
    set_error_handler(array($error_handler, 'handleError'));
    set_exception_handler(array($error_handler, 'handleException'));
    

以上代码有些fatal错误是无法记录的,详细原因见这里
下边是我为Codeigniter写的记录Log的Library。

相关资料

Sentry的官方文档 http://sentry.readthedocs.org/en/latest/
Sentry的PHP客户端 https://github.com/getsentry/raven-php
Sentry的Ruby客户端 https://github.com/getsentry/raven-ruby

修改Mavericks默认字体

Macbook Pro升级到Mavericks之后,发现字体严重变虚。在weiphone上发现很多人遇到了同样的问题,都是通过修改字体解决的。retina屏的用户没有此问题。
通过下边的步骤我将STHeitiSC(华文黑体)改成冬青黑体(Hiragino Sans GB)
打开终端 , 修改字体配置文件DefaultFontFallbacks.plist 和 DefaultFontFallbacks.plist

plist是苹果专有的二进制格式,需要支持此格式的编辑器修改。推荐用TextMate或者Xcode修改。
cd /System/Library/Frameworks/ApplicationServices.framework/Frameworks/CoreText.framework/Resources/

mate DefaultFontFallbacks.plist
# 将 STHeitiSC-Light 替换为 HiraginoSansGB-W3

mate CTPresetFallbacks.plist
将 STHeitiSC-Light 替换为 HiraginoSansGB-W3
将 STHeitiSC-Medium 替换为 HiraginoSansGB-W6

重启机器后,系统字体就变成了冬青黑体。
如果遇到字体显示问题,可以打开终端,运行下边命令清除字体缓存。

sudo atsutil databases -remove

PHP错误处理

程序一定会有错误的时候

天才的程序员说,我的程序绝对OK,没有错误,我从不处理错误。的确,天才程序员的程序从来没有错误,但程序只要运行,就会有错误!不管你信不信,我反正是信的。
错误不只是来自于程序逻辑,更多的来自于环境:

  • 数据库连接数太多,程序连接数据库失败
  • 网络连接故障,连接缓存服务器失败
  • Redis缓存已满,无法写入
  • 合作方接口异常,无法正常响应

……
太多了,变化的环境总是挑战我的想象力。

真正的程序员很重视错误处理,让程序固若金汤。真正的PHP程序员哪怕是Notice也不放过 :)
首先,要注意和错误有关的PHP配置变量。以下是PHP.ini中的默认值。

display_errors = Off
error_reporting = E_ALL & ~E_NOTICE & ~E_STRICT & ~E_DEPRECATED

这是建议开发环境中设置的值:

display_errors= On
error_reporting = E_ALL

这是建议服务器环境中设置的值:

display_errors= Off
error_reporting = E_ALL & ~E_DEPRECATED & ~E_STRICT

PHP中错误报告的级别

以下列出常用的错误报告的级别,所有错误报告的级别点这里

常量 描述
1 E_ERROR 致命的运行时错误。后果是导致脚本终止不再继续运行。
2 E_WARNING 运行时警告 (非致命错误)。仅给出提示信息,但是脚本不会终止运行。
4 E_PARSE 编译时语法解析错误。解析错误仅仅由分析器产生。
8 E_NOTICE 运行时通知。
256 E_USER_ERROR 致命的用户生成的错误。这类似于程序员使用 PHP 函数 trigger_error() 设置的 E_ERROR。
512 E_USER_WARNING 非致命的用户生成的警告。这类似于程序员使用 PHP 函数 trigger_error() 设置的 E_WARNING。
1024 E_USER_NOTICE 用户生成的通知。这类似于程序员使用 PHP 函数 trigger_error() 设置的 E_NOTICE。
2048 E_STRICT (integer) 启用 PHP 对代码的修改建议,以确保代码具有最佳的互操作性和向前兼容性。
4096 E_RECOVERABLE_ERROR 可捕获的致命错误。类似 E_ERROR,但可被用户定义的处理程序捕获。
8192 E_DEPRECATED 运行时通知。启用后将会对在未来版本中可能无法正常工作的代码给出警告。
8191 E_ALL 所有错误和警告,除级别 E_STRICT 以外。

E_WARNING

php -r 'error_reporting(E_ERROR);include "test.php";'
//Warning: include(test.php): failed to open stream: No such file or directory in Command line code on line 1

E_NOTICE

php -r 'error_reporting(E_ERROR);echo $a[0];'
//Notice: Undefined variable: a in Command line code on line 1

E_PARSE

php -r 'error_reporting(E_ERROR);echo 0'
//Parse error: syntax error, unexpected end of file, expecting ',' or ';' in Command line code on line 1

E_USER_WARNING

php -r 'error_reporting(E_ERROR);trigger_error("Value must be 1",E_USER_WARNING);'
//Warning: Value must be 1 in Command line code on line 1

E_USER_ERROR

php -r 'error_reporting(E_ERROR);trigger_error("Value must be 1",E_USER_ERROR);'
//Fatal error: Value must be 1 in Command line code on line 1

E_USER_NOTICE

php -r 'error_reporting(E_ERROR);trigger_error("Value must be 1",E_USER_NOTICE);'
//Notice: Value must be 1 in Command line code on line 1

E_ERROR

php -r 'error_reporting(E_ERROR);$a=null;echo $a->method();'
//Fatal error: Call to a member function method() on a non-object in Command line code on line 1

php -r 'error_reporting(E_ERROR);require_once("test.php");'
//Warning: require_once(test.php): failed to open stream: No such file or directory in Command line code on line 1
//Fatal error: require_once(): Failed opening required 'test.php' in Command line code on line 1

用set_error_handler捕获错误

set_error_handler:设置一个用户的函数来处理脚本中出现的错误。
使用set_error_handler捕获程序错误
php -r ‘error_reporting(E_ERROR);
set_error_handler(function($errno, $errstr, $errfile, $errline){var_dump($errno, $errstr, $errfile, $errline);});
require(“test.php”);
echo “finished!”;’

Php Error 1

从截图中看到,有var_dump的信息,说明set_error_handler捕获到了错误。并且没有看到我需要输出的finished!,说明程序在遇到错误终止了,没有继续运行。
set_error_handler不是万能的,也有捕获不到的时候

php -r 'error_reporting(E_ERROR);
set_error_handler(function($errno, $errstr, $errfile, $errline){var_dump($errno, $errstr, $errfile, $errline);});
$a=null;$a->method();
echo "finished!";'

Php Error 1

截图中没有看到var_dump信息,说明set_error_handler没有捕获到错误。这时候要用register_shutdown_function+error_get_last来捕获

php -r 'error_reporting(E_ERROR);
function logerror() {if (($error = error_get_last())) {var_dump($error);}}
register_shutdown_function('logerror');
$a=null;$a->method();
echo "finished!";'

Php Error 1

错误日志集中管理

错误日志集中管理的平台很多,我们无需多造个轮子。我的选择是Sentry

Sentry是一个的错误日志服务器,可以将程序错误的详细情况集中捕获,并提供一个很漂亮的Web界面来浏览错误。 Sentry本身是用python写的,但它支持记录python、php、ruby、iOS等多种语言的错误。

解决Bundle时的权限问题

在rails项目运行bundle命令时提示输入root密码

Ruby Bundle Install Prividge 1

怎么装个gem也需要root,如果这般以后发布项目都需要root了。
一通Google后发现原来gem要装到ruby目录下,由于ruby是系统管理员用root安装的,那个目录普通用户是没有写权限的。
运行 gem env 查看INSTALLATION DIRECTORY ,这个目录我是没有写权限的。

Ruby Bundle Install Prividge 1

系统管理员太忙了,我们自己搞定吧。

#指定gem安装的路径
bundle install --path vendor/bundle

Ruby Bundle Install Prividge 1

如果在rails new时遇到这个提示可以用--skip-bundle解决此问题。

Viewport & Media Queries

Viewport

viewport字面意思为视图窗口,在移动 web 开发中使用。表示将设备浏览器宽度虚拟成一个特定的值(或计算得出),这样利于移动 web 站点跨设备显示效果基本一致。

width=device-width

<meta name="viewport" content="width=device-width">



@viewport {
width: device-width;
}  

viewport target-densitydpi

现在基本上就不需要target-densitydpi了

target-densitydpi的值有 device-dpi, high-dpi, medium-dpi, low-dpi.

manifest文件

减少请求数,js/css压缩,gzipping,尽量减少DOM操作

在iOS中有两个meta值,apple-mobile-web-app-capable和apple-mobile-web-app-status-bar-style,这两个会让网页内容以应用程序风格显示,并使状态栏透明。

<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">

Home 界面上的图标

  iOS用rel=”apple-touch-icon”,android 用rel=”apple-touch-icon-precomposed”。这样就能在用户把网页存为书签时,在手机HOME界面创建应用程序样式的图标。
  

解决CentOS的yum不能使用问题

解决CentOS的yum不能使用问题

运行yum,出现下图的提示。

Yum No Modules

yum是用Python写的,出现这个错误是因为Python版本装的不正确导致。最简单的方法当然是重装。

# 使用CentOS对应的Python安装包
wget http://mirror.centos.org/centos/6.4/os/x86_64/Packages/python-2.6.6-36.el6.x86_64.rpm

rpm -iv --replacepkgs python-2.6.6-36.el6.x86_64.rpm

PS:附上手动安装yum过程,推荐使用rpm重新安装python来恢复yum。手动安装方式只是备用方案。

wget http://yum.baseurl.org/download/3.4/yum-3.4.3.tar.gz
tar xzfv yum-3.4.3.tar.gz
cd yum-3.4.3

python yummain.py install yum

yum check-update
yum update
yum clean all

服务器下载文件Http头的设置

服务器下载文件Http头的设置

网站提供下载服务时经常需要实现一个强制下载功能(即强制弹出下载对话框),并且文件名保持和用户之前上传时相同。
效果如下图:
Content Disposition

Content-Disposition

使用 HTTP Header 的 Content-Disposition: attachment 可以实现下载时强制弹出下载对话框。

由于HTTP协议规定,通信内容使用US ASCII编码,就是只能使用英文字符集。若要使用其他字符集,必须根据RFC3986使用百分号将字符串编码。

Content-Disposition: attachment; filename=filename.ext
Content-Disposition: attachment; filename*=charset'lang'encoded-filename.ext

如果不进行编码会出现,用户保存文件文件名会是乱码。如下图:

Content Disposition

不过关于Content-Disposition的RFC6266规范是2011年6月才纳入HTTP标准。
浏览器方面我测试了主流浏览器,Firefox 、 Chrome 、 Opera 、 Safari ,都支持新标准规定的 filename,不出意料,万恶的IE并不支持这个规范。不过我还是很吃惊,IE10竟然也不支持filename

按照规范输出Content-Disposition的PHP代码如下:

if ( strpos ( $_SERVER [ 'HTTP_USER_AGENT' ], "MSIE" ) > 0 )
{
     header ( 'Content-Disposition: attachment; filename="' . rawurlencode ( $originfile ) . '"' );
}
else
{
     header( 'Content-Disposition: attachment; filename*=UTF-8\'\'' . rawurlencode ( $originfile ) );
}

注意编码时使用rawurlencode而不是urlencode,二者的区别在于前者把空格编码为%20,而后者是+。在stackoverflow上关于这两个函数有更详细的讨论,PHP - urlencode vs rawurlencode?

Content-Type

浏览器对已知类型的文件(如jpg、pdf、txt等)直接在浏览器内打开,我们通过设置http头中的 Content-Type 来改变浏览器认知的文件类型。 这里把Content-Type 设置为octet-stream,也就是二进制文件流。这样浏览器就会直接打开文件,而不是在浏览器内打开。

Content-Type: application/octet-stream

完整PHP代码

$filename = '中文文件名.txt';
header('Content-Type: application/octet-stream');
if ( strpos ( $_SERVER [ 'HTTP_USER_AGENT' ], "MSIE" ) > 0 )
{
     header ( 'Content-Disposition: attachment; filename="' . rawurlencode ( $originfile ) . '"' );
}
else
{
     header( 'Content-Disposition: attachment; filename*=UTF-8\'\'' . rawurlencode ( $originfile ) );
}

header('Expires: 0');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header("Content-Transfer-Encoding: binary");
header('Pragma: public');
header("Content-Length: ".filesize($filename));

参考资料

Test Cases for HTTP Content-Disposition header field (RFC 6266) and the Encodings defined in RFCs 2047, 2231 and 5987

使用PHP内置的Web服务器

使用PHP内置的Web服务器

PHP从5.4.0开始已经内置了Web服务器,开发测试环境再也不需要安装Apache/Nginx了,也不需要为每个项目配置站点,使用起来也非常简单,只需要在终端中进入你站点的根目录,执行以下的命令:

$ php -S localhost:8000

然后就可以在浏览器里访问了。

如果你想指定站点的根目录,使用-t参数

$ php -S localhost:8000 -t /home/me/public_html

如果需要使用本地测试域名在浏览器中访问,启动web服务器时使用本地ip(127.0.0.1)

$ php -S 127.0.0.1:8000

我一直使用Codeigniter开发PHP项目,经过一个项目的实践,PHP内置的Web服务器完全胜任,以后开发环境就用他了。