# PHP标准运行环境

# 运行环境介绍

新浪云 PHP 运行环境目前的 Web 服务器使用的是:

  • CentOS-6.x
  • Apache-2.2.x
  • PHP-5.3.x / PHP-5.6.x / PHP-7.0.x
  • Web 服务器运行在 64 位 Linux 环境下

Apache 运行在 Prefork 模式下,即每个请求都会对应一个 Apache 进程,请求结束后该进程才能服务于下一个请求。平台通过模块方式扩展了 Apache 和 PHP 的相关功能。

# 沙箱

代码和数据的隔离:每个应用在运行期间,只能“看”到自己的代码和数据,即 A 应用无法访问 B 应用的代码和数据。注意,这里提到的在 Web 服务器上的数据,往往指一些中间处理过程的临时数据,并非最终落地的数据,比如用户上传照片会临时存储到 TmpFS。

连接数的隔离:我们知道,程序写的不好,很容易导致阻塞,并进一步导致连接数的飙升。单个应用过多占用 Apache 连接数,原因往往是多方面的,应用请求外部资源被阻塞是一个最为常见的因素,另外应用页面过大浏览器下载慢也是常见因素之一。公有云平台同一时刻往往运行着大量的应用,如果某一应用出现连接数异常,最直接的后果是整个平台上的所有应用都将陷入瘫痪。新浪云平台目前有设置“应用最大 HTTP 并发连接数”,目前这个值是 500,如果应用平均单个请求处理时长是 100ms,那么该应用每秒的 HTTP 并发连接将可以到达 5000,每天的请求超过 1 亿没有问题。但如果您的应用平均每个请求处理时长 2 秒,那么该应用每秒的 HTTP 并发连接只能到达 250,每天支撑的请求数将在千万。总体而言,尽量迅速处理完请求对应用是有利的,而且也是平台所鼓励的。

内存隔离:目前新浪云平台上对单个 PHP 脚本的处理,设置了 128MB 的上限(max_memory,ini_set 不可修改),我们认为这个设置是一个相对很高的值,可以说能够满足绝大部分应用的需求。设想一台服务器 8G 内存,如果每个 PHP 处理都消耗 64M 内存,那么该服务器最多只能同时运行 128 个 PHP 脚本。新浪云引入了”应用最大并发内存数“的概念,目前的设置是 4GB。如果应用程序单个请求的内存消耗平均在 16MB,那么可同时运行 256 个请求,这和上面的并发连接数的设定是基本一致的。

CPU 隔离:这主要是通过新浪云的配额系统来达到 CPU 时间的隔离。每个应用都有 CPU 时间消耗的分钟速度限制,避免了某一应用过多非法获取 CPU 资源导致其它应用响应慢的问题。

目前新浪云平台上允许的“单请求最大存活时长”是 300 秒 。

请注意

  • 当应用并发超过限制,系统会返回 508 错误,并显示 Connections out of quota
  • 当应用内存占用超过限制,系统会返回 509 错误,并显示 Memory usage out of quota

# PHP版本支持

# PHP5.3

运行环境支持PHP5.3.x的版本,x可能会变化,PHP5.3版本支持的扩展列表为:

[PHP Modules]
bcmath
calendar
Core
ctype
curl
date
dom
ereg
exif
fileinfo
filter
gd
gettext
gmp
hash
iconv
imagick
imap
intl
json
kvclient
libxml
mbstring
mcrypt
memcache
memcached
mysql
mysqli
openssl
pcre
PDO
pdo_mysql
Phar
rank
redis
Reflection
saecounter
saeext
session
SimpleXML
soap
SPL
standard
tideways
tokenizer
xml
xmlreader
xmlwriter
yaf
yar
Zend OPcache
zip
zlib

[Zend Modules]
Zend OPcache

# PHP5.6

运行环境支持PHP5.6.x的版本,x可能会变化,PHP5.6版本支持的扩展列表为:

[PHP Modules]
bcmath
calendar
Core
ctype
curl
date
dom
ereg
exif
fileinfo
filter
gd
gettext
gmp
hash
iconv
imagick
imap
intl
ionCube Loader
json
kvclient
libxml
mbstring
mcrypt
memcache
memcached
mongodb
mysql
mysqli
mysqlnd
openssl
pcre
PDO
pdo_mysql
Phar
rank
redis
Reflection
saecounter
saeext
session
SimpleXML
soap
SPL
standard
tideways
tokenizer
xml
xmlreader
xmlwriter
yaf
yar
Zend Guard Loader
Zend OPcache
zip
zlib

[Zend Modules]
Zend Guard Loader
Zend OPcache
the ionCube PHP Loader (enabled) + Intrusion Protection from ioncube24.com (unconfigured) 

# PHP7.0

运行环境支持PHP7.0.x的版本,x可能会变化,PHP7.0.x版本支持的扩展列表为:

[PHP Modules]
bcmath
calendar
Core
ctype
curl
date
dom
exif
fileinfo
filter
gd
gettext
gmp
hash
iconv
imagick
imap
intl
json
kvclient
libxml
mbstring
mcrypt
memcached
mongodb
mysql
mysqli
mysqlnd
openssl
pcre
PDO
pdo_mysql
Phar
redis
Reflection
saeext
session
SimpleXML
soap
SPL
standard
tideways
tokenizer
xml
xmlreader
xmlwriter
Zend OPcache
zip
zlib

[Zend Modules]
Zend OPcache 

# PHP版本在线切换

PHP的版本支持在线毫秒级切换,切换用户无感知,也可以随时调整。

切换版本前须知

PHP各个版本之间存在一些BC(breaking change (opens new window)),在切换版本前需要测试程序兼容性。

# 如何切换

  1. 登陆云应用管理系统https://sae.sinacloud.com (opens new window)

  2. 点击 操作 中的 管理 进入应用的管理中心 进入云应用的管理中心

  3. 左侧导航选择运行环境管理,选择PHP版本管理进入切换功能页面,点击修改切换PHP版本:

    切换PHP版本
  4. 选择需要切换的PHP版本,点击确认切换:

    选择需要切换的PHP版本切换

# PHP禁用的类和函数

出于平台安全性考虑,我们禁用了以下函数和类,禁用的标准主要有四点:

  • 出于对安全性的考虑
  • 出于对资源管理的考虑
  • 不常用的 API
  • 我们提供更好替代方案的 API

# 禁用的类

SQLiteDatabase
SQLiteResult
SQLiteUnbuffered
SQLiteException 

# 禁用的函数

symlink
link
exec
system
escapeshellcmd
escapeshellarg
passthru
shell_exec
proc_open
proc_close
proc_terminate
proc_get_status
proc_nice
dl
pclose
popen
stream_socket_server
stream_socket_accept
stream_socket_pair
stream_wrapper_restore
mail
mb_send_mail
posix_kill
apache_child_terminate
apache_lookup_uri
apache_reset_timeout
apache_setenv
virtual
socket_create
socket_create_pair
realpath_cache_get 

# 限制

# 运行时限制

选项 默认设定 说明
error_reporting E_ALL & ~E_NOTICE 可以从程序中使用error_reporting修改
max_input_time 60 单位为秒,单个请求最多用在解析请求数据的时间
display_errors off 默认关闭,不展示错误在页面,可以从代码管理处打开设置
memory_limit 128MB 单请求的最大使用内存,这个设定不能修改
log_errors_max_len 2000 单位字节,表示错误日志最大的长度,超出此长度的字符串将会被截断
variables_order EGPCS 使用$_REQUEST时获取参数的顺序,依次从ENV, GET, POST, COOKIE和SERVER中获取
post_max_size 128MB 最大的POST的body大小
upload_max_filesize 128MB 最大的上传文件大小
allow_url_fopen 1
allow_url_include 1 允许远程文件包含
date.timezone Asia/Shanghai 默认设置的时区
mysqli.allow_persistent 0 不允许长连接
max_execution_time 120 单位为秒,单脚本最大执行时间,超出120秒的请求会被自动关闭

# 分钟配额限制

应用的服务资源使用也受限于所有者的账户等级,如果资源使用超出一分钟的限额,服务会按超出的比例随机被禁用,运行环境的限制项目有:

  • 分钟内所有HTTP请求的CPU时间消耗
  • 分钟内所有HTTPS请求的CPU时间消耗
  • 请求总次数
  • 流入流量总数量
  • 流出流量总数量
  • 访问外网总次数
  • 访问外网流入流量总数量
  • 访问外网流出流量总数量

分钟配额的详细参数请参考账户等级说明 (opens new window)

说明

提升账户等级可以增加应用的资源配额,立即提升账户等级 (opens new window)

# session

# 使用自定义session handler

新浪云PHP环境默认提供了共享的Session存储,用户可以不用任何设置直接使用PHP提供的Session系列函数进行Session相关操作。

若用户需要使用特殊的Session存储,如将Session存入MySQL,Memcached,KVDB等服务中,可以参考: PHP: session_set_save_handler - Manual (opens new window)实现自定义Session Handler。 新浪云提供了Memcached存储Session的方案,可通过如下代码将Session数据存入Memcached中。

请注意

  • \sinacloud\sae\MemcacheSessionHandler 类SAE运行环境默认已经注册,不需要单独包含文件,源码实现在MemcacheSessionHandler.php (opens new window)
  • 如果需要使用应用的Memcached服务存储session,必须先开启服务。

# PHP5.3环境使用新浪云Memcached服务作为Session存储示例

例子1:PHP5.3环境使用新浪云 Memcached 服务作为 Session 存储:

<?php
//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();
// 下面是session的操作
// $_SESSION['a'] = 'b';

# PHP5.6及以上版本环境使用新浪云Memcached服务作为Session存储示例

例子2:PHP5.6及以上环境使用新浪云 Memcached 服务作为 Session 存储:

<?php
//PHP 5.6+ 版本
$handler = new sinacloud\sae\MemcacheSessionHandler();
session_set_save_handler($handler, true);
session_start();
// 下面是session的操作
// $_SESSION['a'] = 'b';

# .htaccess

# 支持的指令列表

新浪云PHP标准运行环境支持Apache原生的.htaccess配置文件格式,你可以直接使用应用根目录下的.htaccess文件来配置服务器。目前支持的指令包括:

SetEnvIf
SetEnvIfNoCase
Header
RequestHeader
RewriteEngine
RewriteRule
RewriteCond
AddType
AddEncoding
DirectoryIndex
ErrorDocument
FilterProvider
FilterChain
AddDefaultCharset
Options
Allow
Deny
Order
Satisfy
ExpiresActive
ExpiresByType
ExpiresDefault 

请注意

.htaccess 配置文件不能和 config.yaml 配置文件里应用配置(handle 段)一起使用,如果两个一起使用,会导致配置错乱。

# 将所有的请求转发到index.php处理

RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^.*$ /index.php [L,QSA]  

# 强制使用HTTPS

RewriteEngine on  
RewriteCond %{HTTP:X-FORWARDED-PROTO} !^https$  
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=307]  

# 保护某个目录

在.htaccess文件中加上以下代码将保护test目录,所有访问test目录的请求都会403。

SetEnvIf Request_URI "(test/)" deny
Order deny,allow
Deny from env=deny
Satisfy any  

# 设置允许跨域header头

SetEnvIf Host "^(.*)$" AccessControlAllowOrigin=$0  
SetEnvIf Host "^(.*)$" Pro=http  
SetEnvIf X-FORWARDED-PROTO "https" Pro=https  
Header set Access-Control-Allow-Origin: "%{Pro}e://%{AccessControlAllowOrigin}e" env=AccessControlAllowOrigin  
Header set Access-Control-Allow-Methods: POST,GET,OPTIONS env=AccessControlAllowOrigin  
Header set Access-Control-Allow-Headers: Content-Type,Authorization,X-Requested-With,range  env=AccessControlAllowOrigin  
Header set Access-Control-Allow-Credentials: true  env=AccessControlAllowOrigin  
Header set Access-Control-Expose-Headers: Accept-Ranges,Content-Encoding,Content-Length,Content-Range  env=AccessControlAllowOrigin 

# 将/aa/路径的请求转发到aa目录下的index.php处理,其他转到根目录下的index.php处理

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

# 将所有的请求转发到public目录下的index.php处理

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

请注意

服务器的document_root不能修改。

# config.yaml

# 说明

应用可以通过应用版本目录下的config.yaml来对Apache服务器做一些配置(类似于Apache.htaccess文件)。

通过配置,开发者可以很方便的实现以下功能:

  • 目录默认页面
  • 自定义错误页面
  • 压缩
  • 页面重定向
  • 页面过期
  • 设置响应头的 content-type
  • 设置页面访问权限

说明

PHP 运行环境的 config.yaml 文件不会部署到代码目录中,而只是存在于代码仓库中。

应用配置写在config.yaml文件的handle下,例如:

name: saetest
version: 1

handle:
- rewrite: if (!-d && !-f) goto "/index.php?%{QUERY_STRING}" 

以上语法的目的是如果请求的目标不是一个存在的目录或者文件,则把请求都转交到index.php处理。

基本语法:

- OPTION: ARG1 ARG2 ...
- OPTION: if (CONDICTIONs) ACTION 

其中OPTION为配置项,ARG1ARG2为参数,CONDITIONs是一个或者多个CONDITION,多个CONDITION之间使用&&隔开。ACTIONif条件满足后执行的动作。

CONDITION 可以是以下任意一种:

  • 使用==!=运算符比较变量和字符串
  • 使用~(大小写敏感)和~*(大小写不敏感)运算符匹配变量和正则表达式。正则表达式可以包含匹配组,匹配结果后续可以使用变量$1..$9引用(正则匹配使用PCRE (opens new window)库,你可以在其主页或者Wikipedia (opens new window)找到其语法相关文档
  • 使用 >>=<<= 比较变量和数字的大小
  • 使用 -f!-f 运算符检查文件是否存在
  • 使用 -d!-d 运算符检查目录是否存在
  • 使用 -e!-e 运算符检查文件、目录是否存在

支持的变量:

  • %{REQ:HEADER_NAME} HTTP 请求头中的字段,如 %{REQ:HTTP_HOST}
  • %{RESP:HEADER_NAME} HTTP 响应头中的字段,如 %{RESP:CONTENT_ENCODING}
  • %{QUERY_STRING} 查询串,一般是url中问号后面的内容
  • %{REQUEST_URI} 请求路径,即用户请求的url去掉主机部分和查询串后剩下的部分

# 目录默认页面

可以从config.yaml中设置目录的默认页面,当访问 url 没有指定文件时,指定返回的文件。

语法:

- directoryindex: FILE [...] 

directoryindex 在 config.yaml 文件中仅有一项,例如:

- directoryindex: aaa.php bbb.html 

请请求一个目录时例如http://xxxx.applinzi.com/examples/时将优先在examples目录下找aaa.php,然后再找bbb.html,如果找不到就返回404错误。

# 自定义错误页面

语法:

- errordoc: httpcode error_file 

HTTP code是诸如404、302之类的http响应码,error_file是服务器以 HTTP code响应请求时响应的文件。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 来判断压缩是否生效。

# URL 重写

语法:

- rewrite: if (CONDITIONs) goto target_url 

在 rewrite 中,CONDITIONs 支持多个 CONDITION。除 HTTP 响应 header(没办法根据响应 header 做重定向)外都可以出现在 rewriteCONDITION 中。

target_url 表示重定向的目标 url,在 target_url 可以用 $N 的形式引用 CONDITION 中以正则匹配到的组。

以下有一些URL重写的样例。

# 强制使用 https 访问

handle:
- rewrite: if (%{REQ:X-Forwarded-Proto} != "https") goto "https://%{HTTP_HOST}%{REQUEST_URI}" 

# 当 url 匹配 urldir/(.) ,并且 输入 header referer 等于 sina 时,跳转至页面 /usr/$1,$1 表示刚刚匹配的 urldir/(.) 中的 (.*) 部分。

handle:
- rewrite: if (%{REQUEST_URI} ~ "urldir/(.*)" && %{REQ:REFERER} == "sina") goto "/url/$1"

# 当 url 匹配 urldir/(.*),并且请求的是一个目录时,跳转至 /url/$1

handle:
- rewrite: if (-d && %{REQUEST_URI} ~ "urldir/(.*)") goto "/url/$1"

# 当 url 匹配 path,并且请求的不是一个文件时,跳转至 /url/query.php

handle:
- rewrite: if (!-f && %{REQUEST_URI} ~ "path") goto "/url/query.php" 

# 当查询串等于 so,并且 url 以 zhaochou 结尾时,跳转至 /url/$1,$1 表示 query_string 匹配到的部分

handle:
- rewrite: if (%{QUERY_STRING} ~ "^(so)$" && %{REQUEST_URI} ~ "zhaochou$") goto "/url/$1"

# 当查询串不包含 sohu,并且 url 以 zhaochou 结尾时,跳转至 /url/query.php?%{QUERY_STRING},%{QUERY_STRING}表示查询串

handle:
- rewrite: if (%{QUERY_STRING} !~ "sohu" && %{REQUEST_URI} ~ "zhaochou$") goto "/url/query.php?%{QUERY_STRING}"

# 如果 url 既不是文件,也不是目录,跳转至 index.php?%{QUERY_STRING}

handle:
- rewrite: if (!-d && !-f) goto "/index.php?%{QUERY_STRING}"

# 将所有以.mp3为结尾的请求转到到mp3.php处理

handle:
- rewrite: if(path ~ "/(.*)\.mp3$") goto "/mp3.php?path=$1" 

请注意

  • 如果有形如 %{REQUEST_URI} ~ "^(.*)$"类的请求,一定要加上是否是目录或者文件,防止无穷的 rewrite
  • goto 语句中,虽然某些时候可以不以 / 开头,但是强烈建议以 / 开头。

# 指定过期时间和头信息

语法:

- expire: if (CONDITION) time seconds
- mime: if (CONDITION) type content-type 

seconds 是秒数,content-type 是表示文档类型的字符串。

# 如果请求 header referer包含sina字符串,指定过期时间为10秒

handle:
- expire: if (%{REQ:REFERER} ~ "sina") time 10

# 如果 url 请求文件的扩展名是 pdf2,设置 Content-Type 为 application/pdf

handle:
- mime: if (%{REQUEST_URI} ~ "\.pdf2$") type "application/pdf" 

# 请求 header referer 包含字符串 sina,就设置 Content-Type 为 text/plain

handle:
- mime: if (%{REQ:REFERER} ~ "sina") type "text/plain"

说明

if 语句支持单个 CONDITION。可以出现在 CONDITION 中的变量参考 Apache Docs (opens new window) ,只支持字符串和正则匹配。

# 限制IP访问

可以通过规则匹配,限制IP或者IP段的访问。

语法:

- hostaccess: if (CONDITION) deny IP
- hostaccess: if (CONDITION) allow IP 

if 语句只支持单个 CONDITION

IP 需要加引号,IP 可以是一个或多个 ip 地址、all(所有 IP 地址)、 CIDR (opens new window) (如 108.192.8.0/24),具体可以参考 Apache 配置,allow 是白名单,deny 是黑名单。

# 禁止 127.0.0.1 访问 private 目录

handle:
- hostaccess: if (%{REQUEST_URI} ~ "/private/") deny "127.0.0.1"

# 只允许 127.0.0.1 访问.conf 结尾的文件

handle:
- hostaccess: if (%{REQUEST_URI} ~ "\.conf$") allow "127.0.0.1"

# 禁止 127.0.0.1 的所有访问(这个要慎用)

handle:
- hostaccess: deny "127.0.0.1" 

# 对 cron 任务保护,防止被外部抓取,我们将 cron 任务放在 cron 目录下 (sae 中 cron 服务执行时,走的是内部网络),允许 10 打头的所有 IP

handle:
- hostaccess: if (%{REQUEST_URI} ~ "/cron/") allow "10.0.0.0/8" 

# 对于屏蔽一组 IP 地址,可以写成子网掩码形式,或者将多个 IP 之间加以空格。子网掩码形式如下

handle:
- hostaccess: if (%{REQUEST_URI} ~ "/cron/") deny "108.192.8.0/24" 

# 允许 108.134.13.24 和 108.122.122.13 这两个 IP

handle:
- hostaccess: allow "108.134.13.24 108.122.122.13"

请注意

如果设置了白名单,则其他的访问都会返回403,请谨慎使用。

# 环境变量

# $_SERVER变量

选项 说明
$_SERVER['HTTP_APPNAME'] 应用名
$_SERVER['HTTP_ACCESSKEY'] 应用的AccessKey
$_SERVER['DOCUMENT_ROOT'] 当前应用的DOCUMENT_ROOT

# 常量

常量 说明
SAE_TMP_PATH 临时目录路径,这个目录可以写入数据,请求结束后数据会被删除
SAE_ACCESSKEY 应用的AccessKey
SAE_SECRETKEY 应用的SecretKey
SAE_MYSQL_USER 共享型数据库用户名
SAE_MYSQL_PASS 共享型数据库密码
SAE_MYSQL_HOST_M 共享型数据库主库地址
SAE_MYSQL_HOST_S 共享型数据库从库地址
SAE_MYSQL_PORT 共享型数据库主库和从库的端口
SAE_MYSQL_DB 共享型数据库数据库名
SAE_Font_Sun 宋体文件的绝对路径
SAE_Font_Kai 楷体文件的绝对路径
SAE_Font_Hei 文泉驿正黑文件的绝对路径
SAE_Font_MicroHei 文泉驿微米黑文件的绝对路径

# 全局函数

# is_https()

判断客户端是以 http 还是以 https 的方式连接。

返回值说明

如果是 https 连接返回 true,否则返回 false。

# Wrappers

# 说明

PHP 自 4.3 版本以来,引入了stream流的概念,简单说,就是可以用通用的IO读写函数来操作各种资源,比如:tcp、udp、http、ftp 等等,这样做的好处是统一了接口的封装。这就像在 Unix 中将各种设备都抽象成文件,你可以使用 read/write 来操作各种设备,这样统一了操作接口,易于理解和使用。Wrappers 就是用来告诉 stream 流该如何处理(读写)特定的资源。

Wrappers 使用非常简单,比如下面就是一个最常见一个使用 Wrapper 的语句:

<?php
$c = file_get_contents("http://sae.sina.com.cn");
?> 

这里就是使用 http:// Wrapper 实现抓取远程内容并赋值给一个变量的操作。

由于新浪云的PHP标准运行环境并不提供持久性本地IO能力,所以 PHP 运行环境提供了提供了MemcachedStorageKVDBWrappers 来方便开发者迁移原有程序。

  • 如果你的原有程序中,使用了本地文件型缓存,那么你可以方便地使用 saemc:// 替换本地文件缓存的前缀。
  • 如果你的原有程序中,有文件存储的需求,你原来可能是通过 NFS 或者就是单机提供的存储服务,那么你可以方便地使用 saestor://saekv:// 来替换原来的存储前缀,注意存储的用途是用于文件落地的永久存储,任何缓存、中间临时交换数据的需求都是不适合使用 Storage 和 KVDB 存储的。

说明

使用 Wrappers 请要先初始化相应的服务。

# saekv://

saekv://是KVDB服务的wrapper封装,可以使用saekv://读取、写入kvdb中的数据,当前,应用的KVDB服务默认开启。

示例代码:

<?php
file_put_contents('saekv://a.txt', 'kvdb');
var_dump(file_get_contents('saekv://a.txt')); 

程序返回值:

string(4) "kvdb" 

# saestor://

saestor://是storage服务的wrapper封装,可以使用saestor://读取、写入、删除storage中的数据,使用前需要创建一个bucket,路径格式为:

saestor://BUCKET/OBJECT_PATH
例如:
saestor://upload/a.txt
表示upload bucket根目录下的a.txt文件

演示程序:

<?php 
file_put_contents('saestor://xhprof/a.txt', 'storage');
var_dump(file_get_contents('saestor://xhprof/a.txt'));

程序输出:

string(7) "storage" 

# saemc://

saemc://是Memcached服务的wrapper封装,可以使用saemc://读取、写入memcached中的数据,使用前需要开启Memcached服务。

示例代码:

<?php
file_put_contents('saemc://a.txt', 'memcached');
var_dump(file_get_contents('saemc://a.txt'));

程序输出:

string(9) "memcached" 

# 本地 IO

# 说明

考虑到安全和分布式问题,PHP标准运行环境对本地的IO做了限制:

  • 应用可以以只读权限访问应用目录以及PHP语言的系统库目录;
  • 可写 TmpFS
  • 为最大程度降低应用移植的难度,PHPStorageMemcachedKVDB 提供了wrapper封装,用户可以像读写文件一样来读写Memcached、Storage、KVDB。

如何使用

请参考上面的wrapper说明文档。

# 为什么不能从程序中往本地写文件

我们假定允许 从代码中往本地写入文件,使用如下代码往代码根目录下写入了a.txt文件:

<?php
file_put_contents('a.txt', 'hello');

那么会出现如下的情况:

标准环境如果本地写入文件
  1. 第一次请求时,请求正好落到包含了a.txt的机器,则返回正常;
  2. 第二次请求时,请求落到了其他的机器,由于之前写入a.txt时不在这个机器,这次请求就返回404错误

因此在分布式的环境下,如果往本地写入文件,会出现系统的错乱。

# 使用SAE_TMP_PATH临时可写目录

请注意

  • 临时文件的生存周期等同于 PHP 请求,也就是当该 PHP 请求完成执行时,所有写入 TmpFS 的临时文件都会被销毁
  • TmpFS 是本地临时文件,不是共享存储,而新浪云是全分布式环境,所以不同请求之间无法通过 TmpFS 共享操作文件
  • TmpFS 操作的文件限于 SAE_TMP_PATH 目录内,这个目录对每个应用是不一样的
  • TmpFS 的文件为纯内存存储

标准环境的本地是不能写入文件的,如果有临时的文件需要处理可以使用平台提供的SAE_TMP_PATH常量处理,以下示例将演示如何将一个远程的图片文件下载到本地,并获取图片的长宽值。

示例代码:

<?php
$image_url = 'https://img.t.sinajs.cn/t6/style/images/global_nav/WB_logo.png';
$image_content = file_get_contents($image_url);
$file_local = SAE_TMP_PATH.'wb_logo.png';
file_put_contents($file_local, $image_content);
// 此时本地的文件路径为SAE_TMP_PATH.'wb_logo.png'

$image_info = getimagesize($file_local);
var_dump($image_info);

程序输出为:

array(6) {
  [0]=>
  int(80)
  [1]=>
  int(27)
  [2]=>
  int(3)
  [3]=>
  string(22) "width="80" height="27""
  ["bits"]=>
  int(8)
  ["mime"]=>
  string(9) "image/png"
} 

# 必须要往本地写文件怎么办

PHP还支持另外一种类型的运行环境,云空间运行环境,这个环境支持往本地写入文件,请参考云空间运行环境说明文档

# 日志系统

# 访问日志

新浪云会记录所有访问的日志,从日志中心-访问日志分类可以查询日志详细,查询路径如下:

查询访问日志详情

# 错误日志

可以使用 PHP 的 error_log (opens new window)trigger_error (opens new window) 来写日志,更多使用方法请参见 PHP 官方文档:错误处理和日志记录 (opens new window)

说明

目前 PHP 运行环境单个请求最多输出 1000 条日志,超过限制的日志输出会被丢弃。

从日志中心的HTTP日志分类,选择对应版本的调试日志分类可以查询错误日志:

查询错误日志详情

# 外网访问

您可以通过以下方式来访问外网:

# 使用curl库示例

范例一,使用 curl 库抓取 http://www.sinaapp.com :

<?php
$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示例

范例二,使用 fsockopen 连接 IP 1.2.3.4 的 80 端口。

<?php
$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连接

<?php
$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

# 分钟配额

云应用的外网访问流入流量、流出流量、请求次数使用受账户等级限制,查询具体限制 (opens new window)提升账户等级 (opens new window)可以增加分钟配额。