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),在切换版本前需要测试程序兼容性。

如何切换

  1. 登陆云应用管理系统https://sae.sinacloud.com

  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时间消耗
  • 请求总次数
  • 流入流量总数量
  • 流出流量总数量
  • 访问外网总次数
  • 访问外网流入流量总数量
  • 访问外网流出流量总数量

分钟配额的详细参数请参考账户等级说明

说明

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

session

使用自定义session handler

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

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

请注意

  • \sinacloud\sae\MemcacheSessionHandler 类SAE运行环境默认已经注册,不需要单独包含文件,源码实现在MemcacheSessionHandler.php
  • 如果需要使用应用的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';

.appconfig

支持的指令列表

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

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

请注意

云空间运行环境不支持config.yaml文件,和标准的.htaccess文件有差异。

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

.htaccess文件的格式为:

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

需要将以上的文件到http://htaccess.applinzi.com加上应用名翻译为.appconfig,如图所示:

将.htaccess翻译为.appconfig

例如应用名为lazyweixin(请替换为自己的应用名),翻译后的文件内容为:

RewriteBase /
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^/data1/www/htdocs/641/lazyweixin/1/.*$ /index.php [L,QSA]

将以上的文件放到根目录下的.appconfig文件。

请注意

请不要直接复制,需要将内容加上自己的应用名范围为对应的.appconfig文件。

强制使用HTTPS

.htaccess文件的格式为:

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

需要将以上的文件到http://htaccess.applinzi.com加上应用名翻译为.appconfig,如图所示:

将.htaccess翻译为.appconfig

例如应用名为lazyweixin(请替换为自己的应用名),翻译后的文件内容为:

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

请注意

请不要直接复制,需要将内容加上自己的应用名范围为对应的.appconfig文件。

保护某个目录

.appconfig文件中加上以下代码将保护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处理

.htaccess文件的内容为:

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]   

需要将.htaccess的文件内容在http://htaccess.applinzi.com加上应用名翻译为.appconfig文件:

将.htaccess翻译为.appconfig

翻译后的文件内容为:

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

请将以上的文件放到根目录下的.appconfig文件中。

请注意

请不要直接复制,需要将内容加上自己的应用名范围为对应的.appconfig文件。

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

.htaccess文件内容为:

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

需要将.htaccess的文件内容在http://htaccess.applinzi.com加上应用名翻译为.appconfig文件:

将.htaccess翻译为.appconfig

翻译后的文件内容为:

RewriteBase /
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^/data1/www/htdocs/641/lazyweixin/1/(.*)$ aa/index.php [L]

请注意

  • 请不要直接复制,需要将内容加上自己的应用名范围为对应的.appconfig文件;
  • 服务器的document_root不能修改。

默认首页

设置.appconfig

DirectoryIndex index.htm index.html index.php

设置后默认的首页文件顺序依次为index.htmindex.htmlindex.php

环境变量

$_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 运行环境提供了提供了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

说明

云空间运行环境的文件系统采用网络磁盘,因此本地可以写入文件。

使用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 的 error_logtrigger_error 来写日志,更多使用方法请参见 PHP 官方文档:错误处理和日志记录

说明

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

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

查询错误日志详情

外网访问

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

  • PHP curl 库 访问外网的 HTTP 资源。所有 curl 访问的日志可以在日志中心查看。
  • fsockopen 通过 TCP Socket 访问外网资源。

使用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

分钟配额

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

和标准环境的差异

项目 云空间环境 标准环境
空间计费模式 按开启的容量包月计费 普通用户100MB免费,企业用户5GB免费,上限5GB
代码管理方式 FTP、在线编辑器 SVN、Git、在线编辑
支持版本管理
支持代码回滚
本地可以写入文件
开发、移植难度
并发支持 极高
配置支持 支持.appconfig语法 支持.htaccess和config.yaml
多版本支持
支持服务套餐