SAE上的SOAP客户端——SaeSoap开发手记

前言
=========

Sina App Engine(SAE)到目前为止还不支持SOAP,但是询问的人不少,我也是一个。对于这些提问,新浪管理员都做了认真回复,给出了解决思路,并且希望有用户能够自己去实现。

SAE文档中心关于SOAP的问题:
http://sae.sina.com.cn/?keyword=SOAP&m=search

为了响应伟大的SAE的号召,为了满足项目的小小需要,SaeSoap诞生了!

基本信息
============

应用名称:
PHP SOAP for ASE

访问方式:
http://saesoap.sinaapp.com/

应用说明:
让SAE原生支持PHP SOAP!

开发环境
============

SAE应用管理 -> 应用管理 -> 代码管理:创建版本1

$ sudo aptitude install php-pear

$ cd /path/to/sae/
$ svn checkout https://svn.sinaapp.com/saesoap/
$ cd saesoap/1

拷贝 pear SOAP, HTTP_Request, Net_Socket, Net_URL,注意目录和文件的权限都必须是755。

$ ln -s /path/to/sae/saesoap/1 /var/www/saesoap

测试代码
============

问题和解决方法
—————–

$ gvim /path/to/sae/saesoap/1/index.php
<?php
require ‘SOAP/Client.php’;
$uri = ‘http://www.5haoxue.net/ws/hello-soap.php’;
$client = new SOAP_Client($uri);
$result = $client->call(‘fetch_news’, array());
echo $result;
?>
$ lynx http://localhost/saesoap/

出现了“No Transport for http”错误。这个错误提示是在Transport.php中定义的SOAP_Transport类的getTransport方法中输出的。出错的原因是没有复制SOAP/Transport/目录,在这个目录下有HTTP.php。补上以后测试成功!

在SAE上测试
—————-

$ svn status
$ svn add HTTP/ SOAP/ Net/
$ svn commit -m ‘Test pear SOAP client’
$ lynx http://saesoap.sinaapp.com/

发现SAE不支持PEAR:

SAE_Warning: require_once(PEAR.php) [function.require-once]: failed to open stream: No such file or directory in SOAP/Base.php on line 30
SAE_Fatal_error: require_once() [function.require]: Failed opening required ‘PEAR.php’ (include_path=’.:/usr/local/sae/php/lib/php/sae_std_lib/’) in SOAP/Base.php on line 30

管理员说是出于安全性考虑:
http://sae.sina.com.cn/?m=feedback&a=view&id=3262
建议手动安装PEAR,并给出了教程的链接:
http://hi.baidu.com/mleoking/blog/item/ebe307ca55146d43f21fe792.html

直接拷贝PER文件:
$ sudo updatedb
$ locate PEAR.php
$ cp /usr/share/php/PEAR.php /path/to/sae/saesoap/1
$ svn add PEAR.php
$ svn commit -m ‘Add PEAR.php’
$ lynx http://saesoap.sinaapp.com/

出现了一些警告信息,但是数据读出来了!警告信息如下:

SAE_Warning: include_once(PEAR5.php) [function.include-once]: failed to open stream: No such file or directory in PEAR.php on line 730
SAE_Warning: include_once() [function.include]: Failed opening ‘PEAR5.php’ for inclusion (include_path=’.:/usr/local/sae/php/lib/php/sae_std_lib/’) in PEAR.php on line 730

继续拷贝PEAR5.php文件:
$ locate PEAR5.php
$ cp /usr/share/php/PEAR5.php /path/to/sae/saesoap/1
$ svn add PEAR5.php
$ svn commit -m ‘Add PEAR5.php’

现在没有错误也没有警告了,只剩下一堆SAE_Deprecated和数据了:

SAE_Deprecated: Assigning the return value of new by reference is deprecated in SOAP/WSDL.php on line 214

SAE_Deprecated: Function split() is deprecated in SOAP/Transport/HTTP.php on line 272

现在可以把代码迁移到好学网手机版中去了!

小结
=========

SaeSoap目前实现了在SAE上使用SOAP Client,实现的方式是使用了PEAR SOAP,并不需要对代码进行修改。

SAE上的SOAP Server,目前项目还没有需求,因此暂时也没有时间去实现,希望能有朋友能够抽时间来完成!

SaeSoap Demo:
http://saesoap.sinaapp.com/

基于SpeedPHP和SaeSoap实现的手机版网站:
http://m.5haoxue.net/

PHP JSON教程

PHP JSON简介
=================

参考资料
————

PHP Manual -> Function Reference -> Other Basic Extensions -> JSON
http://www.php.net/manual/en/book.json.php

JSON简介
————-

JSON是JavaScript Object Notation的缩写,是一种数据交换格式。

PHP JSON扩展简介
———————

PHP JSON扩展是PHP核心的一部分,相关函数可以直接使用。

PHP JSON函数的使用方法
=========================

json_decode(string $json)函数:解码JSON字符串,返回对象。

json_decode(string $json, TRUE)函数:解码JSON字符串,返回数组。

json_encode(mixed $value)函数:编码变量或对象,返回字符串。

注:仅支持UTF-8编码的数据。

:注: var_dump()竟然不支持中文!dump()支持中文!

PHP的json_decode函数的使用实例

基于SpeedPHP 3.1.89。

controller/admin.php文件源代码:

__getFunctions();
echo 'SOAP functions: ';
var_dump($soap_func_list); // array(1) { [0]=> string(14) "string Hello()" }
echo '
';
$soap_type_list = $client->__getTypes();
echo 'SOAP types: ';
var_dump($soap_type_list);
echo '
';
$result = $client->__soapCall('Hello', array());
echo 'Hello function call: ';
$result = json_decode($result, TRUE);
//var_dump($result); // 中文变成乱码,var_dump()不支持中文!
dump($result); // 中文正常显示
}
}

访问方式:
http://localhost/index.php?c=admin&a=soap_client_wsdl

访问结果:
结果截图参考“PHP的json_decode函数的使用实例截图_2011-12-123.png”。

非WSDL模式的PHP SoapServer类和SoapClient类使用实例

基于SpeedPHP 3.1.89。

controller/admin.php文件源代码:

$location, 'uri' => $uri));
$soap_func_list = $client->__getFunctions();
echo 'SOAP functions: ';
var_dump($soap_func_list); // 对于非WSDL模式的SOAP,返回值是NULL?
echo '
';
$soap_type_list = $client->__getTypes();
echo 'SOAP types: ';
var_dump($soap_type_list); // 对于非WSDL模式的SOAP,返回值是NULL?
echo '
';
$result = $client->__soapCall('hello', array()); // 函数名不区分大小写
echo 'Hello function call: ';
var_dump($result);
}
function soap_server() {
function hello() {
return 'Hello, kind SOAP user!';
}
$server = new SoapServer(NULL, array('uri' => 'http://www.5haoxue.net/'));
$server->addFunction('hello');
$server->handle();
}
}
?>

访问
http://localhost/5haoxue/index.php?c=admin&a=soap_server
结果为空。

访问
http://localhost/5haoxue/index.php?c=admin&a=soap_client
结果为:
SOAP functions: NULL
SOAP types: NULL
Hello function call: string(22) “Hello, kind SOAP user!”
结果截图参考“非WSDL模式的SoapServer和SoapClient使用实例截图_2011-12-23.png”。

PHP的SoapClient类使用实例

基于SpeedPHP 3.1.89。

controller/admin.php源代码:

__getFunctions();
echo 'SOAP functions: ';
var_dump($soap_func_list); // array(1) { [0]=> string(14) "string Hello()" }
echo '
';
$soap_type_list = $client->__getTypes();
echo 'SOAP types: ';
var_dump($soap_type_list);
echo '
';
$result = $client->__soapCall('Hello', array());
echo 'Hello function call: ';
var_dump($result); // string(1517804) "[{"Title":"2011\u5e74\u8d35\u5dde\u7701\u516d\u76d8\u6c34\u5e02\u5e02\u76f4\u4e8b\u4e1a\u5355\u62df\u8058\u7528\u4eba\u5458\u540d\u5355","Content":"...","Updatetime":"1324607604","TypeName":"\u8003\u8bd5\u52a8\u6001"},...]"
}
}
?>

访问方式:
http://localhost/index.php?c=admin&a=soap_extension

访问结果:
参考“SoapClient类使用实例截图_2011-12-23.png”。

利用SpeedPHP的URL Rewrite功能实现网站结构调整后的301重定向

最近要把一个网站从VPS迁移到SAE上,由于SAE还不支持web2py,因此整个网站用SpeedPHP重写。

在更新网站的过程中,对部分URL进行了重新规划,以实现更短、更容易理解的URL,例如/introduction改为/intro,/application改为/contact。熟悉SEO的朋友都知道,废弃的URL直接返回404并不是最佳选择,通过301跳转到新页面或者首页上才是首选。通过SpeedPHP的URL Rewrite功能,我们可以很容易地实现网站结构调整后的301重定向。

SpeedPHP的URL Rewrite功能的使用方法可以参考我之前写的《在SpeedPHP中启用URL Rewrite》。

主要代码如下:

$ gvim index.php &
$spConfig = array(
‘ext’ => array(
‘spUrlRewrite’ => array(
‘map’ => array(
‘introduction’ => ‘main@redirect’,
‘application’ => ‘main@’redirect’,

$ gvim controller/main.php &
function redirect() {
header(‘HTTP/1.1 301 Moved Permanently’);
header(‘Location: /’);
}

这样就实现了废弃的URL到网站首页的301跳转,而不需要借助.htaccess。SpeedPHP还是很强大的。

在SpeedPHP中启用URL Rewrite

启用Apache的.htaccess
==========================

参考资料:Ubuntu下启动Apache对.htaccess文件的支持

$ sudo a2enmod
rewrite
$ gvim /etc/apache2/sites-enabled/000-default &
AllowOverride All
$ sudo service apache2 restart

SpeedPHP的.htaccess文件
============================

参考资料:Apache的UrlRewrite伪静态htaccess设置

$ cd /var/www/ # root of SpeedPHP project
$ gvim .htaccess &

RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php?$1 [L]

在SpeedPHP中启用URL Rewrite
===============================

参考资料:SpeedPHP框架的urlrewrite伪静态实现

$ gvim index.php &
$spConfig = array(
‘launch’ => array(
‘router_prefilter’ => array(
array(‘spUrlRewrite’, ‘setReWrite’),
),
‘function_url’ => array(
array(‘spUrlRewrite’, ‘getReWrite’),
),
),
);

测试URL Rewrite的效果
=========================

$ gvim index.php &
$spConfig = array(
‘auto_display’ => TRUE,

$ gvim controller/main.php &
function test_smarty() {
$this->name = ‘chinakr’;
$this->display(‘index.html’);
}

$ gvim tpl/index.html &

Hi, {$name}!
Go to Home Page or stay here.

$ lynx localhost/index.php?c=main&a=test_smarty
$ lynx localhost/main-test_smarty.html

也即默认的URL Rewrite,得到的URL的形式是“controller-action.html”。

优化我们的URL
=================

参考资料:SpeedPHP框架的urlrewrite伪静态实现

看起来像HTML页面的形式
————————

$ gvim index.php &
$spConfig = array(
‘ext’ => array(
‘spUrlRewrite’ => array(
‘suffix’ => ”,
‘sep’ => ‘/’,
),
),

$ lynx localhost/index.php?c=main&a=test_smarty
$ lynx localhost/main/test_smarty.html

也就是说,现在的URL的形式是“controller/action.html”,目录层次和逻辑关系更加清晰。

更加简洁的形式
—————–

$ gvim index.php &
$spConfig = array(
‘ext’ => array(
‘spUrlRewrite’ => array(
‘suffix’ => NULL,
‘sep’ => ‘/’,
),
),

$ lynx localhost/index.php?c=main&a=test_smarty
$ lynx localhost/main/test_smarty

也就是说,现在的URL的形式是“controller/action”,更简洁了。

URL中的参数
===============

参考资料:SpeedPHP框架的urlrewrite伪静态实现

$ gvim tpl/index.html &

Sample for URL with arguments.

$ lynx localhost/index.php?c=main&a=test_smarty&id=0&from=baidu
$ lynx localhost/main/test_smarty/id/0/from/baidu.html

也就是说,现在的URL的形式是“controller/action/参数1/值1/参数2/值2”,不太美观。

优化我们的URL参数
====================

参考资料:SpeedPHP框架的urlrewrite伪静态实现

$ gvim index.php &
$spConfig = array(
‘ext’ => array(
‘spUrlRewrite’ => array(
‘suffix’ => NULL,
‘sep’ => ‘/’,
‘map’ => array(
‘smarty’ => ‘main@test_smarty’,
),
‘args’ => array(
‘smarty’ => array(‘id’, ‘from’),
),
),
),

$ lynx localhost/index.php?c=main&a=test_smarty&id=0&from=baidu
$ lynx localhost/smarty/0/baidu

也可以这样:

$ gvim index.php &
$spConfig = array(
‘ext’ => array(
‘spUrlRewrite’ => array(
‘suffix’ => NULL,
‘sep’ => ‘/’,
‘map’ => array(
‘smarty’ => ‘main@test_smarty’,
),
‘args’ => array(
‘smarty’ => array(‘from’, ‘baidu’),
),
),
),

$ lynx localhost/index.php?c=main&a=test_smarty&id=0&from=baidu
$ lynx localhost/smarty/baidu/0

在这里,建议把重要的参数放在前面,不重要的放在后面,这样逻辑相对更清晰一些。

能否做得更好?
=================

事实上,如果SpeedPHP的URL能够支持这样的形式可能逻辑关系会更清晰一些:
/controller/action-参数1-值1-参数2-值2.html
或者
/controller/action-值1-值2.html

这样的话,上面的URL就可以写成:
http://localhost/main/test_smarty-id-0-from-baidu.html
或者
http://localhost/main/test_smarty-0-baidu.html

这样是不是会更清楚更好看一些呢?见仁见智,权当抛砖引玉了!

Memcache和SAE云豆的消耗

新浪App Engine(SAE)是目前国内最成熟、使用最广泛的云计算服务之一。云服务的重要特性就是可伸缩的资源分配,以及按实际使用情况收费的模式。SAE的计费单位是云豆,目前1元人民币可以购买100颗云豆。

SpeedPHP是一个轻量级的PHP开发框架,具有学习快、开发快、速度快的特点。SpeedPHP原生支持SAE。

SpeedPHP内置了对Smarty模板的支持。如果要在SAE上使用Smarty模板,就必须打开Memcache。在SAE上初始化Memcache时必须设置容量,可选择的范围为1~256M。这个选择就是本文讨论的问题。目前1GB Memcache每天要消耗200云豆,如果你把Memcache设置为100M,那么即便你什么也不干,每天也会消耗大约20云豆,如果设置为200M就会消耗大约40云豆。这个是固定成本!

因此,在开发和测试阶段,Memcache的容量不要设置得太大,甚至设置为1M就可以了!

绿色SAE之旅,从合理设置Memcache容量开始:-)

SpeedPHP for SAE本地开发环境配置详细教程

开发环境的搭建
===================

:注: 操作系统为Ubuntu 11.10,已经安装好Apache2、PHP5和MySQL5

输出PHP错误信息
——————–

$ cd /etc/php5/apache2
$ sudo vim php.ini
display_errors = on
$ sudo /etc/init.d/apache2 restart

获取应用代码
—————–

在SAE“应用管理->代码管理”中创建1个版本。

$ cd /path/to/sae
$ svn checkout https://svn.sinaapp.com/5haoxue/
$ cd 5haoxue/1
$ ls
config.yaml index.php

部署SpeedPHP框架源代码
—————————

$ cd /path/to/sae/5haoxue/1
$ cp -r /path/to/speedphp-3.1.89-sae/* .
$ ls
controller index.php model SpeedPHP
$ chmod -R 755 SpeedPHP # 避免出现白屏(500 Internal Server Error)
$ chmod -R 755 controller # 避免出现路由错误(SpeedPHP无法访问controller目录)
$ mkdir tpl # 模板文件目录
$ mkdir tmp # 临时文件目录
$ chmod -R 777 tmp # 避免出现白屏(500 Internal Server Error)
$ ls
config.yaml controller index.php SpeedPHP tmp tpl

建立本地开发环境
———————

$ mkdir /var/www/5haoxue
$ cp -r /path/to/speedphp-3.1.89/* /var/www/5haoxue # 本地不使用SAE版
# 本地和SAE上的代码,只有controller目录和tpl目录的内容是相同的
$ rm -rf /var/www/5haoxue/controller
$ rm -rf /var/www/5haoxue/tpl
$ ln -s /path/to/sae/5haoxue/1/controller /var/www/5haoxue/controller
$ ln -s /path/to/sae/5haoxue/1/tpl /var/www/5haoxue/tpl
$ cd /var/www/5haoxue
$ mkdir tmp
$ ls
controller index.php SpeedPHP tmp tpl
$ lynx http://localhost/5haoxue/
Enjoy, Speed of PHP!

打开SAE上的Memcache支持
—————————-

在SAE“服务管理->Memcache”中初始化MC,容量设置为100M。

这样SAE上的SpeedPHP就已经支持Smarty了。

打开本地的Smarty支持
————————

$ cd /var/www/5haoxue
$ gvim index.php &
在$spConfig中加入:
‘auto_display’ => TRUE,
$ gvim controller/main.php &
在main中加入:
function hello() {
$this->name = ‘chinakr’;
$this->display(‘hello.html’);
}
$ gvim tpl/hello.html &
Welcome to SpeedPHP, {$name}!

测试Smarty是否能够正常使用
——————————-

$ lynx http://localhost/5haoxue/index.php?c=main&a=hello
Welcome to SpeedPHP, chinakr!

$ cd /path/to/sae/5haoxue
$ svn status
$ svn add controller/ model/ SpeedPHP/ tmp/ tpl/
$ svn commit -m ‘SpeedPHP with Smarty’
$ lynx http://5haoxue.sinaapp.com/index.php?c=main&a=hello
Welcome to SpeedPHP, chinakr!