OpenShift 编译 Nginx + PHP 5.5

wget http://ar2.php.net/distributions/php-5.5.30.tar.gz
tar -zxvf php-5.5.30.tar.gz
cd php-5.5.30.tar.gz
./configure \
--prefix=$OPENSHIFT_DATA_DIR/php \
--with-config-file-path=$OPENSHIFT_DATA_DIR/php/etc \
--with-mysql=mysqlnd \
--with-mysqli=mysqlnd \
--with-pdo-mysql=mysqlnd \
--enable-embedded-mysqli=shared \
--enable-pdo \
--enable-fpm \
--with-zlib \
--enable-xml \
--enable-bcmath \
--with-curl \
--with-gd \
--enable-zip \
--enable-mbstring \
--enable-sockets \
--enable-ftp \
--with-iconv-dir \
--with-freetype-dir \
--with-jpeg-dir \
--with-png-dir \
--with-libxml-dir=/usr \
--disable-rpath \
--enable-shmop \
--enable-sysvsem \
--enable-sysvshm \
--enable-sysvmsg \
--enable-inline-optimization \
--enable-mbregex \
--enable-gd-native-ttf \
--with-openssl \
--with-mhash \
--enable-pcntl \
--enable-sockets \
--enable-ftp \
--enable-calendar \
--enable-exif \
--enable-zip \
--with-gettext \
--with-xmlrpc \
--with-xsl \
--with-bz2 \
--enable-wddx \
--enable-opcache \
--without-pear 

make
make install
cp php.ini-production $OPENSHIFT_DATA_DIR/php/etc/php.ini
cp $OPENSHIFT_DATA_DIR/php/etc/php-fpm.conf.default $OPENSHIFT_DATA_DIR/php/etc/php-fpm.conf

vi $OPENSHIFT_DATA_DIR/php/etc/php-fpm.conf
listen = var/run/php-fpm.sock
listen.owner = 561620207628e1c367000169
listen.group = 561620207628e1c367000169
其中 561620207628e1c367000169 为你的当前用户名

vi $OPENSHIFT_DATA_DIR/conf/nginx.conf.template
...
        root /var/lib/openshift/561620207628e1c367000169/app-root/repo/diy;

        location / {
            index  index.php index.html index.htm;
        }
...
location ~ \.php$ {
            try_files $uri =404;
            fastcgi_pass   unix:/var/lib/openshift/561620207628e1c367000169/app-root/data/php/var/run/php-fpm.sock;
            fastcgi_index  index.php;
            fastcgi_param  SCRIPT_FILENAME $document_root$fastcgi_script_name;
            fastcgi_param  SCRIPT_FILENAME $OPENSHIFT_REPO_DIR/diy$fastcgi_script_name;
            include        fastcgi_params;
        }
其中 /var/lib/openshift/561620207628e1c367000169/app-root/data 为 $OPENSHIFT_DATA_DIR
在本地程序目录中修改
vi .openshift/action_hooks/start
追加:
nohup $OPENSHIFT_DATA_DIR/php/sbin/php-fpm > $OPENSHIFT_DIY_LOG_DIR/server.log 2>&1 &

vi .openshift/action_hooks/stop
追加:
killall php-fpm

OpenShift 编译安装 Nginx

在 OpenShift 中创建 DIY 应用

cd $OPENSHIFT_TMP_DIR
wget http://nginx.org/download/nginx-1.2.2.tar.gz
tar zxf nginx-1.2.2.tar.gz

cd $OPENSHIFT_TMP_DIR
wget ftp://ftp.csx.cam.ac.uk/pub/software/programming/pcre/pcre-8.31.tar.bz2
tar jxf pcre-8.31.tar.bz2

cd nginx-1.2.2
./configure --prefix=$OPENSHIFT_DATA_DIR/nginx --with-pcre=$OPENSHIFT_TMP_DIR/pcre-8.31

make install
vi $OPENSHIFT_DATA_DIR/nginx/conf/nginx.conf
http {
    …
    server {
        listen       $OPENSHIFT_DIY_IP:$OPENSHIFT_DIY_PORT;
        server_name  localhost;
        … 
        }
    …
    }

mv $OPENSHIFT_DATA_DIR/nginx/conf/nginx.conf $OPENSHIFT_DATA_DIR/nginx/conf/nginx.conf.template
在本地程序目录中修改
vi .openshift/action_hooks/start
#!/bin/bash
# The logic to start up your application should be put in this
# script. The application will work only if it binds to
# $OPENSHIFT_INTERNAL_IP:8080
# nohup $OPENSHIFT_REPO_DIR/diy/testrubyserver.rb $OPENSHIFT_INTERNAL_IP $OPENSHIFT_REPO_DIR/diy > $OPENSHIFT_DIY_LOG_DIR/server.log 2>&1 &
# replace the $OPENSHIFT_INTERNAL_IP and $OPENSHIFT_INTERNAL_PORT before starting up the server
sed -e "s/`echo '$OPENSHIFT_IP:$OPENSHIFT_PORT'`/`echo $OPENSHIFT_INTERNAL_IP:$OPENSHIFT_INTERNAL_PORT`/" $OPENSHIFT_DATA_DIR/nginx/conf/nginx.conf.template > $OPENSHIFT_DATA_DIR/nginx/conf/nginx.conf
nohup $OPENSHIFT_DATA_DIR/nginx/sbin/nginx > $OPENSHIFT_DIY_LOG_DIR/server.log 2>&1 &

vi .openshift/action_hooks/stop
追加:
killall nginx
git commit -a -m "start nginx when starting up the app"
git push

或在服务端重启应用使修改生效
ctl_all restart

Phalcon Framework的MVC结构及启动流程分析

入口文件为public/index.php,简化后一共5行,包含了整个Phalcon的启动流程,以下将按顺序说明。

require __DIR__ . '/../config/services.php';
$application = new Phalcon\Mvc\Application();
$application->setDI($di);
require __DIR__ . '/../config/modules.php';
echo $application->handle()->getContent();

DI注册阶段

Phalcon的所有组件服务都是通过DI(依赖注入)进行组织的,这也是目前大部分主流框架所使用的方法。通过DI,可以灵活的控制框架中的服务:哪些需要启用,哪些不启用,组件的内部细节等等。因此Phalcon是一个松耦合可替换的框架,完全可以通过DI替换MVC中任何一个组件。

require __DIR__ . '/../config/services.php';

这个文件中默认注册了Phalcon\Mvc\Router(路由)、Phalcon\Mvc\Url(Url)、Phalcon\Session\Adapter\Files(Session)三个最基本的组件。同时当MVC启动后,DI中默认注册的服务还有很多,可以通过DI得到所有当前已经注册的服务:

$services = $application->getDI()->getServices();
foreach($services as $key => $service) {
        var_dump($key);
        var_dump(get_class($application->getDI()->get($key)));
}

打印看到Phalcon还注册了以下服务:

dispatcher : Phalcon\Mvc\Dispatcher 分发服务,将路由命中的结果分发到对应的Controller
modelsManager : Phalcon\Mvc\Model\Manager Model管理
modelsMetadata : Phalcon\Mvc\Model\MetaData\Memory ORM表结构
response : Phalcon\Http\Response 响应
cookies : Phalcon\Http\Response\Cookies Cookies
request : Phalcon\Http\Request 请求
filter : Phalcon\Filter 可对用户提交数据进行过滤
escaper : Phalcon\Escaper 转义工具
security : Phalcon\Security 密码Hash、防止CSRF等
crypt : Phalcon\Crypt 加密算法
annotations : Phalcon\Annotations\Adapter\Memory 注解分析
flash : Phalcon\Flash\Direct 提示信息输出
flashSession : Phalcon\Flash\Session 提示信息通过Session延迟输出
tag : Phalcon\Tag View的常用Helper

而每一个服务都可以通过DI进行替换。接下来实例化一个标准的MVC应用,然后将我们定义好的DI注入进去。

$application = new Phalcon\Mvc\Application();
$application->setDI($di);

模块注册阶段

与DI一样,Phalcon建议通过引入一个独立文件的方式注册所有需要的模块:

require __DIR__ . '/../config/modules.php';

这个文件的内容如下:

$application->registerModules(array(
    'frontend' => array(
        'className' => 'Eva\Frontend\Module',
        'path' => __DIR__ . '/../apps/frontend/Module.php'
    )
));

可以看到Phalcon所谓的模块注册,其实只是告诉框架MVC模块的引导文件Module.php所在位置及类名是什么。

MVC阶段

$application->handle()是整个MVC的核心,这个函数中处理了路由、模块、分发等MVC的全部流程,处理过程中在关键位置会通过事件驱动触发一系列application:事件,方便外部注入逻辑,最终返回一个Phalcon\Http\Response。整个handle方法的过程并不复杂,下面按顺序介绍:

基础检查

首先检查DI,如果没有任何DI注入,会抛出错误:

A dependency injection object is required to access internal services

然后从DI启动EventsManager,并且通过EventsManager触发事件application:boot。

路由阶段

接下来进入路由阶段,从DI中获得路由服务router,将uri传入路由并调用路由的handle()方法。

路由的handle方法负责将一个uri根据路由配置,转换为相应的Module、Controller、Action等,这一阶段接下来会检查路由是否命中了某个模块,并通过Router->getModuleName()获得模块名。

如果模块存在,则进入模块启动阶段,否则直接进入分发阶段。

注意到了么,在Phalcon中,模块启动是后于路由的,这意味着Phalcon的模块功能比较弱,我们无法在某个未启动的模块中注册全局服务,甚至无法简单的在当前模块中调用另一个未启动模块。这可能是Phalcon模块功能设计中最大的问题,解决方法暂时不在本文的讨论范围内,以后会另开文章介绍。

模块启动

模块启动时首先会触发application:beforeStartModule事件。事件触发后检查模块的正确性,根据modules.php中定义的className、path等,将模块引导文件加载进来,并调用模块引导文件中必须存在的方法。

Phalcon\Mvc\ModuleDefinitionInterface->registerAutoloaders ()
Phalcon\Mvc\ModuleDefinitionInterface->registerServices (Phalcon\DiInterface $dependencyInjector)

registerAutoloaders()用于注册模块内的命名空间实现自动加载。registerServices ()用于注册模块内服务,在官方示例中registerServices ()注册并定义了view服务以及模板的路径,并且注册了数据库连接服务db并设置数据库的连接信息。

模块启动完成后触发 application:afterStartModule事件,进入分发阶段。

分发阶段(Dispatch)

分发过程由Phalcon\Mvc\Dispatcher(分发器)来完成,所谓分发,在Phalcon里本质上是分发器根据路由命中的结果,调用对应的Controller/Action,最终获得Action返回的结果。

分发开始前首先会准备View,虽然View理论上位于MVC的最后一环,但是如果在分发过程中出现任何问题,通常都需要将问题显示出来,因此View必须在这个环节就提前启动。Phalcon没有准备默认的View服务,需要从外部注入,在多模块demo中,View的注入官方推荐在模块启动阶段完成的。如果是单模块应用,则可以在最开始的DI阶段注入。

如果始终没有View注入,会抛出错误:

Service 'view' was not found in the dependency injection container

导致分发过程直接中断。

分发需要Dispatcher,Dispatcher同样从DI中取得。然后将router中得到的参数(NamespaceName / ModuleName / ControllerName / ActionName / Params),全部复制到Dispatcher中。

分发开始前,会调用View的start()方法。具体可以参考View相关文档,其实Phalcon\Mvc\View->start()就是PHP的输出缓冲函数ob_start的一个简单封装,分发过程中所有输出都会被暂存到缓冲区。

分发开始前还会触发事件application:beforeHandleRequest。

正式开始分发会调用Phalcon\Mvc\Dispatcher->dispatch()。

Dispatcher内的分发处理

进入Dispatcher后会发现Dispatcher对整个分发过程进行了进一步细分,并且在分发的过程中会按顺序触发非常多的分发事件,可以通过这些分发事件进行更加细致的流程控制。部分事件提供了可中断的机制,只要返回false就可以跳过Dispatcher的分发过程。

由于分发中可以使用Phalcon\Mvc\Dispatcher->forward()来实现Action的复用,因此分发在内部会通过循环实现,通过检测一个全局的finished标记来决定是否继续分发。当以下几种情况时,分发才会结束:

Controller抛出异常
forward层数达到最大(256次)
所有的Action调用完毕

渲染阶段 View Render

分发结束后会触发application:afterHandleRequest,接下来通过Phalcon\Mvc\Dispatcher->getReturnedValue()取得分发过程返回的结果并进行处理。

由于Action的逻辑在框架外,Action的返回值是无法预期的,因此这里根据返回值是否实现Phalcon\Http\ResponseInterface接口进行区分处理。

当Action返回一个非Phalcon\Http\ResponseInterface类型

此时认为返回值无效,由View自己重新调度Render过程,会触发application:viewRender事件,同时从Dispatcher中取得ControllerName / ActionName / Params作为Phalcon\Mvc\View->render()的入口参数。

Render完毕后调用Phalcon\Mvc\View->finish()结束缓冲区的接收。

接下来从DI获得resonse服务,将Phalcon\Mvc\View->getContent()获得的内容置入response。

当Action返回一个Phalcon\Http\ResponseInterface类型

此时会将Action返回的Response作为最终的响应,不会重新构建新的Response。

返回响应

通过前面的流程,无论中间经历了多少分支,最终都会汇总为唯一的响应。此时会触发application:beforeSendResponse,并调用

Phalcon\Http\Response->sendHeaders()
Phalcon\Http\Response->sendCookies()

将http的头部信息先行发送。至此,Application->handle()对于请求的处理过程全部结束,对外返回一个Phalcon\Http\Response响应。

发送响应

HTTP头部发送后一般把响应的内容也发送出去:

echo $application->handle()->getContent();

这就是Phalcon Framework的完整MVC流程。

流程控制

分析MVC的启动流程,无疑是希望对流程有更好的把握和控制,方法有两种:

自定义启动

按照上面的流程,我们其实完全可以自己实现$application->handle()->getContent()这一流程,下面就是一个简单的替代方案,代码中暂时没有考虑事件的触发。

//Roter
$router = $di['router'];
$router->handle();

//Module handle
$modules = $application->getModules();
$routeModule = $router->getModuleName();
if (isset($modules[$routeModule])) {
    $moduleClass = new $modules[$routeModule]['className']();
    $moduleClass->registerAutoloaders();
    $moduleClass->registerServices($di);
}

//dispatch
$dispatcher = $di['dispatcher'];
$dispatcher->setModuleName($router->getModuleName());
$dispatcher->setControllerName($router->getControllerName());
$dispatcher->setActionName($router->getActionName());
$dispatcher->setParams($router->getParams());

//view
$view = $di['view'];
$view->start();
$controller = $dispatcher->dispatch();
//Not able to call render in controller or else will repeat output
$view->render(
    $dispatcher->getControllerName(),
    $dispatcher->getActionName(),
    $dispatcher->getParams()
);
$view->finish();

$response = $di['response'];
$response->setContent($view->getContent());
$response->sendHeaders();
echo $response->getContent();

MVC事件

Phalcon作为C扩展型的框架,其优势就在于高性能,虽然我们可以通过上一种方法自己实现整个启动,但更好的方式仍然是避免替换框架本身的内容,而使用事件驱动。

下面梳理了整个MVC流程中所涉及的可被监听的事件,可以根据不同需求选择对应事件作为切入点:

DI注入
    application:boot 应用启动
路由阶段
    模块启动
        application:beforeStartModule 模块启动前
        application:afterStartModule 模块启动后
分发阶段
    application:beforeHandleRequest进入分发器前
    开始分发
        dispatch:beforeDispatchLoop 分发循环开始前
        dispatch:beforeDispatch 单次分发开始前
        dispatch:beforeExecuteRoute Action执行前
        dispatch:afterExecuteRoute Action执行后
        dispatch:beforeNotFoundAction 找不到Action
        dispatch:beforeException 抛出异常前
        dispatch:afterDispatch 单次分发结束
        dispatch:afterDispatchLoop 分发循环结束
    application:afterHandleRequest 分发结束
渲染阶段
    application:viewRender 渲染开始前
发送响应
    application:beforeSendResponse 最终响应发送前

Adobe 安装报错

安装时出现
Exit Code: 34
FATAL: Payload 'Microsoft Visual C++ 2008 Redistributable Package (x86) 9.0.0.65 {9C4AA28F-AC6B-11E0-8997-00215AEA26C9}'

解决方法:
删除或者重命名这个目录
C:\Program Files (x86)\Common Files\Adobe\caps

OpenShift install PIL with freetype2

This is what worked for PIL with jpeg, zlib and freetype2 support:

> ln -s /usr/lib64/libz.so $VIRTUAL_ENV/lib/
> ln -s /usr/lib64/libfreetype.so $VIRTUAL_ENV/lib/
> ln -s /usr/lib64/libjpeg.so $VIRTUAL_ENV/lib/

>pip install PIL

Result:

*** TKINTER support not available
— JPEG support available
— ZLIB (PNG/ZIP) support available
— FREETYPE2 support available
*** LITTLECMS support not available

Ubuntu 设置无线热点

点击桌面右上角网络图标 - 编辑链接 - 添加,类型选择 Wi-Fi

名称填入 ynfap,在常规里把“可用时自动连接到这个网络”去掉

在 SSID 里填入 ynfap,模式选择 架构,设备 MAC 地址里选择你的网卡

在 Wi-Fi 安全性选择 WPA及WPA2个人,密码填入你的密码

在 IPv4 设置里,方法选择与其他计算机共享
在 IPv6 设置里,方法选择忽略

cd /etc/NetworkManager/system-connections
sudo gedit ynfap
将 [wifi] 节点里的
mode=adhoc
改为
mode=ap


点击桌面右上角网络图标 - 连接到隐藏的WiFi网络
WiFi适配器里选择你的网卡,连接选择 ynfap,点击 连接

此时已经可以正常使用无线热点

Mac OS X与Windows双系统产生“时差”的原因和解决办法

电脑内部都有一个记录时间的RTC,这个实时时钟系统会自动运转,即使你已经关闭电源。 IBM PC兼容机大多采用DS12887这个芯片,内部存储时间,包括年,月,日,时,分。。,还包括世纪,解决了千年虫的问题,但是这个系统没有关于时区的记录,时区的处理由操作系统才完成。

从这里已经可以引申出,产生双系统时间不一致的原因,Mac OS X采用UTC,就是说在电脑RTC系统中保存时间,Mac认为时UTC时间,OS X再根据系统的时区转换成本地时间给你看。
Windows使用Local time,就是说Windows系统认为RTC系统内保存的时间就是本地时间,不再根据时区作相应的转换了。Windows为什么不使用UTC,这个问题微软官方也做过说明,因为早在DOS时代,就是使用本地时间,一直为了兼容性保留下来(历史的包袱)
从上面的说明可以看出,如果是北京时间东8区,就会相差8个小时了。

PS: 很多其他的Unix Like的系统可以设置采用UTC还是Local Time

windows虽然为了兼容性,一直沿用本地时间,但是在注册表中有一个设置可以让它使用UTC,这个正是我们需要的,修改注册表就好了

让windows也使用Universal Time,这样即使在不联网的情况下,时钟仍然是正确的
特别注意的是:取消Boot Camp的Apple时间服务, 不然时不时Apple时间服务会
把时间调正回去.
1)开始菜单,运行regedit
2)打开TimeZoneInformation,位置
HKEY_LOCAL_MACHINE – SYSTEM – CurrentControlSet – Control – TimeZoneInformation
3)右击->创建一个新的DWORD. 数值名称 RealTimeIsUniversal 数值数据 1 (十
六进制) 注意大小写,修改完成确定并且把regedit关了.
4)打开时间设置, 取消 “自动与Internet时间服务器同步”
5)取消Apple时间服务. 控制面板->管理工具->服务. 里面有个”Apple时间服务”, 右
击它然后选择属性.启动类型选择禁用, 停止.