应用可以通过应用版本目录下的 config.yaml 来对 Apache 服务器做一些配置(类似于 Apache 的 htaccess 文件)。
通过配置,开发者可以很方便的实现以下功能:
注解
PHP 运行环境的 config.yaml 文件不会部署到代码目录中,而只是存在于代码仓库中。
应用配置写在 config.yaml 文件的 handle 下,例如::
name: saetest
version: 1
handle:
- rewrite: if (!-d && !-f) goto "/index.php?%{QUERY_STRING}"
基本语法:
- OPTION: ARG1 ARG2 ...
- OPTION: if (CONDICTIONs) ACTION
其中 OPTION 为配置项,ARG1,ARG2 为参数,CONDITIONs 是一个或者多个 CONDITION,多个 CONDITION 之间使用 &&
隔开。ACTION 是 if 条件满足后执行的动作。
CONDITION 可以是以下任意一种:
==
和 !=
运算符比较变量和字符串;~
(大小写敏感)和 ~*
(大小写不敏感)运算符匹配变量和正则表达式。正则表达式可以包含匹配组,匹配结果后续可以使用变量 %1..%9 引用(正则匹配使用 PCRE 库,你可以在其主页或者 Wikipedia 找到其语法相关文档);>
、 >=
、 <
、 <=
比较变量和数字的大小;-f
和 !-f
运算符检查文件是否存在;-d
和 !-d
运算符检查目录是否存在;-e
和 !-e
运算符检查文件、目录是否存在;appconfig 支持的变量:
%{REQ:HEADER_NAME}
HTTP 请求头中的字段,如 %{REQ:HTTP_HOST}%{RESP:HEADER_NAME}
HTTP 响应头中的字段,如 %{RESP:CONTENT_ENCODING}%{QUERY_STRING}
查询串,一般是 url 中问号后面的内容%{REQUEST_URI}
请求路径,即用户请求的 url 去掉主机部分和查询串后剩下的部分当访问 url 没有指定文件时,指定返回的文件。
语法:
- directoryindex: FILE [...]
directoryindex 在 config.yaml 文件中仅有一项
例子:
- directoryindex: aaa.php bbb.html
语法:
- errordoc: httpcode error_file
httpcode 是诸如 404、302 之类的 http 响应码,error_file 是服务器以 httpcode 响应请求时响应的文件。errordoc 在 config.yaml 中可以配置多项。
例子:
- errordoc: 404 /path/404.html
- errordoc: 403 /path/403.html
语法:
- compress: if (CONDICTIONs) compress
在 compress 中,CONDITIONs 只能有一个 CONDITION。
例子:
- compress: if (%{RESP:Content-Length} >= 10240) compress
- compress: if (%{REQ:Referer} == "gphone") compress
- compress: if (%{REQUEST_URI} ~ "/big/") compress
注解
通常情况,我们根据响应头 Content-length,判断是否需要压缩,例如:if (%{RESP:Content-Length} >= 10240) compress,这个静态页面,如 js,css,html 都是没有问题的。但是对 php 脚本,响应 header 中没有 Content-length 这个头,它使用 Transfer-Encoding: chunked, 这个头表示页面输出用 chunked 编码。此时要实现压缩,可以通过应用配置,同时在 PHP 脚本中输出相应头的方式实现。
例如在应用配置中写 if (%{RESP:Use-Compress} == “1”) compress,在需要压缩的 PHP 脚本中写 header(“Use-Compress: 1”)。
开发者可以通过检查是不是输出了响应头:Content-Encoding: gzip 来判断压缩是否生效。
语法:
- rewrite: if (CONDITIONs) goto target_url
在 rewrite 中,CONDITIONs 支持多个 CONDITION。除 HTTP 响应 header(没办法根据响应 header 做重定向)外都可以出现在 rewrite 的 CONDITION 中。
target_url 表示重定向的目标 url,在 target_url 可以用 %N 的形式引用 CONDITION 中以正则匹配到的组。
例子:
# 强制使用 https 访问
- rewrite: if (%{REQ:X-Forwarded-Proto} != "https") goto "https://%{HTTP_HOST}%{REQUEST_URI}"
# 当 url 匹配 urldir/(.*) ,并且 输入 header referer 等于 sina 时,跳转至页面 /usr/%1,%1 表示刚刚匹配的 urldir/(.*) 中的 (.*) 部分。
- rewrite: if (%{REQUEST_URI} ~ "urldir/(.*)" && %{REQ:REFERER} == "sina") goto "/url/%1"
# 当 url 匹配 urldir/(.*),并且请求的是一个目录时,跳转至 /url/%1
- rewrite: if (-d && %{REQUEST_URI} ~ "urldir/(.*)") goto "/url/%1"
# 当 url 匹配 path,并且请求的不是一个文件时,跳转至 /url/query.php
- rewrite: if (!-f && %{REQUEST_URI} ~ "path") goto "/url/query.php"
# 当查询串等于 so,并且 url 以 zhaochou 结尾时,跳转至 /url/%1,%1 表示 query_string 匹配到的部分。
- rewrite: if (%{QUERY_STRING} ~ "^(so)$" && %{REQUEST_URI} ~ "zhaochou$") goto "/url/%1"
# 当查询串不包含 sohu,并且 url 以 zhaochou 结尾时,跳转至 /url/query.php?%{QUERY_STRING},%{QUERY_STRING}表示查询串。
- rewrite: if (%{QUERY_STRING} !~ "sohu" && %{REQUEST_URI} ~ "zhaochou$") goto "/url/query.php?%{QUERY_STRING}"
# 如果 url 既不是文件,也不是目录,跳转至 index.php?%{QUERY_STRING}
- rewrite: if (!-d && !-f) goto "/index.php?%{QUERY_STRING}"
警告
语法:
- expire: if (CONDITION) time seconds
- mime: if (CONDITION) type content-type
seconds 是秒数,content-type 是表示文档类型的字符串。
例子:
- expire: if (%{REQ:REFERER} ~ "sina") time 10
# 如果 url 请求文件的扩展名是 pdf2,设置 Content-Type 为 application/pdf
- mime: if (%{REQUEST_URI} ~ "\.pdf2$") type "application/pdf"
- mime: if (%{REQUEST_URI} ~ "\.pdf2$") type "application/pdf"
# 只要请求 header referer 包含字符串 sina,就设置 Content-Type 为 text/plain
- mime: if (%{REQ:REFERER} ~ "sina") type "text/plain"
if 语句支持单个 CONDITION。可以出现在 CONDITION 中的变量参考 Apache Docs ,只支持字符串和正则匹配。
语法:
- hostaccess: if (CONDITION) deny IP
- hostaccess: if (CONDITION) allow IP
if 语句只支持单个 CONDITION。
IP 需要加引号,IP 可以是一个或多个 ip 地址、all(所有 IP 地址)、 CIDR (如 108.192.8.0/24),具体可以参考 Apache 配置,allow 是白名单,deny 是黑名单。
例子::
# 禁止 127.0.0.1 访问 private 目录
- hostaccess: if (%{REQUEST_URI} ~ "/private/") deny "127.0.0.1"
# 只允许 127.0.0.1 访问.conf 结尾的文件
- hostaccess: if (%{REQUEST_URI} ~ "\.conf$") allow "127.0.0.1"
# 禁止 127.0.0.1 的所有访问(这个要慎用)
- hostaccess: deny "127.0.0.1"
# 对 cron 任务保护,防止被外部抓取,我们将 cron 任务放在 cron 目录下 (sae 中 cron 服务执行时,走的是内部网络)
- hostaccess: if (%{REQUEST_URI} ~ "/cron/") allow "10.0.0.0/8" 允许 10 打头的所有 IP
# 对于屏蔽一组 IP 地址,可以写成子网掩码形式,或者将多个 IP 之间加以空格。子网掩码形式如下:
- hostaccess: if (%{REQUEST_URI} ~ "/cron/") deny "108.192.8.0/24" 屏蔽 108.192.8 打头的所有 IP
# 允许 108.134.13.24 和 108.122.122.13 这两个 IP
- hostaccess: allow "108.134.13.24 108.122.122.13"
语法:
- passwdaccess: passwd "USERNAME:PASSWORD..."
- passwdaccess: if (CONDITION) passwd "USERNAME:PASSWORD..."
例子:
# 所有访问都要密码,允许用户 writer 用密码 123zxc 访问
- passwdaccess: passwd "write:123zxc"
# 访问 secret 目录需要密码,允许用户 test 用密码 123qwe 访问,用户 coder 用密码 123asd 访问
- passwdaccess: if (%{REQUEST_URI} ~ "/secret/") passwd "test:123qwe coder:123asd"
# 访问.text 结尾的文件需要密码,允许用户 writer 用密码 123zxc
- passwdaccess: if (%{REQUEST_URI} ~ "\.text$") passwd "writer:123zxc"
# 用户的网站后台程序都放在 admin 目录下,需要对 admin 目录做密码保护
- passwdaccess: if (%{REQUEST_URI} ~ "/admin/") passwd "admin:admin123"
if 语句中只支持单个 CONDITION ,%{REQ:HEADER_NAME}, %{REQUEST_URI}可以出现在 CONDITION 中,只支持字符串和正则匹配。
新浪云 PHP 环境默认提供了共享的 Session 存储,用户可以不用任何设置直接使用 PHP 提供的 Session 系列函数进行 Session 相关操作。
若用户需要使用特殊的 Session 存储,如将 Session 存入 MySQL,Memcached,KVDB 等服务中,可以参考: PHP: session_set_save_handler - Manual 实现自定义 Session Handler。 新浪云 提供了 Memcached 存储 Session 的方案,可通过如下代码将 Session 数据存入 Memcached 中。
例子:使用新浪云 Memcached 服务作为 Session 存储
//PHP 5.3 版本
$handler = new sinacloud\sae\MemcacheSessionHandler();
session_set_save_handler(
array($handler, 'open'),
array($handler, 'close'),
array($handler, 'read'),
array($handler, 'write'),
array($handler, 'destroy'),
array($handler, 'gc')
);
session_start();
...
//PHP 5.6 版本
$handler = new sinacloud\sae\MemcacheSessionHandler();
session_set_save_handler($handler, true);
session_start();
...
您可以通过以下方式来访问外网:
范例一,使用 curl 库抓取 http://www.sinaapp.com 。
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'http://www.sinanapp.com');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
范例二,使用 fsockopen 连接 IP 1.2.3.4
的 80
端口。
$fp = fsockopen("tcp://1.2.3.4:80", 13, $errno, $errstr);
if (!$fp) {
echo "ERROR: $errno - $errstr\n";
} else {
fwrite($fp, "\n");
echo fread($fp, 26);
fclose($fp);
}
?>
范例三,使用 Socket 与外部网站进行 SSL 连接。
$fp = fsockopen("ssl://passport.baidu.com", 443, $errno, $errstr);
if (!$fp) {
echo "ERROR: $errno - $errstr \n";
} else {
$str="GET /?login HTTP/1.1\r\n";
$str.="User-Agent: curl/7.19.6 (i686-pc-linux-gnu) libcurl/7.19.6 OpenSSL/0.9.8b zlib/1.2.3 libidn/0.6.5\r\n";
$str.="Host: passport.baidu.com\r\n";
$str.="Accept: */*\r\n\r\n";
fwrite($fp, $str);
while (!feof($fp)) {
echo fgets($fp, 128);
}
fclose($fp);
}
?>
注解
外网访问出口 IP 列表:
如果你需要给访问的外部接口添加 IP 访问授权,建议添加以下四个 CIDR 规则:
由于出口IP会变动,建议您使用 域名解析方式 获取出口IP地址:
新浪云提供了实时的日志查询功能,方便开发者在线调试分析。
PHP 运行环境中输出的日志类型有: Apache 的访问日志,PHP 的错误日志,可以在日志中心面板中查看。
你可以使用 PHP 的 error_log 、 trigger_error 来写日志,更多使用方法请参见 PHP 官方文档: 错误处理和日志记录 。
注解
目前 PHP 运行环境单个请求最多输出 1000 条日志,超过限制的日志输出会被丢弃。
新浪云 PHP 运行环境支持 Apache 原生的 htaccess 配置文件格式,你可以直接使用应用根目录下的 .htaccess 文件来配置服务器。目前支持的指令包括:
警告
.htaccess 配置文件不能和 config.yaml 配置文件里应用配置(handle 段)一起使用,如果两个一起使用,会导致配置错乱。