全部产品

PHP 运行环境

更新时间:2019-07-04 06:38:24

函数计算支持 PHP 7.2.7(runtime = php7.2)运行环境。本文主要介绍以下 PHP 运行环境相关内容:

使用 logger

PHP 函数通过 $GLOBALS['fcLogger'] 打印的内容会被收集到创建服务时指定的日志服务 Logstore 中。函数计算内置了 logger 模块,您可以通过 $GLOBALS['fcLogger'] 使用该内置 logger。

日志级别

您可以通过 setLevel 方法改变日志级别,其中有如下几种从高到低的日志级别:

日志级别 Level 接口 描述
EMERGENCY 600 $logger->emergency 紧急日志
ALERT 550 $logger->alert 警示日志
CRITICAL 500 $logger->critical 严重警告
ERROR 400 $logger->error 出错信息
WARNING 300 $logger->warning 警告信息
NOTICE 250 $logger->notice 通知及常规日志
INFO(默认) 200 $logger->info 详细输出信息
DEBUG 100 $logger->debug 调试日志

更多有关函数日志的详情,请参阅 函数日志

logger 示例一

  1. <?php
  2. function handler($event, $context) {
  3. $logger = $GLOBALS['fcLogger'];
  4. $logger->info("hello world");
  5. return 'hello world';
  6. };

输出的日志内容为:

  1. message:2017-07-05T05:13:35.920Z a72df088-f738-cee3-e0fe-323ad89118e5 [INFO] hello world

logger 示例二

  1. <?php
  2. use Monolog\Logger;
  3. function handler($evt, $ctx) {
  4. $logger = $GLOBALS['fcLogger'];
  5. $logger->setLevel(400);
  6. $logger->error("console error 1");
  7. $logger->info("console info 1");
  8. $logger->warning("console warn 1");
  9. $logger->debug("console debug 1");
  10. $logger->setLevel(Logger::WARNING);
  11. $logger->error("console error 2");
  12. $logger->info("console info 2");
  13. $logger->warn("console warn 2");
  14. $logger->debug("console debug 2");
  15. $logger->setLevel(200);
  16. }

输出的日志内容为:

  1. 2018-08-22T09:01:26Z c19b2706-9c4d-270b-52c8-5248ed5e2315 [ERROR]: console error 1
  2. 2018-08-22T09:01:26Z c19b2706-9c4d-270b-52c8-5248ed5e2315 [ERROR]: console error 2
  3. 2018-08-22T09:01:26Z c19b2706-9c4d-270b-52c8-5248ed5e2315 [WARNING]: console warn 2

PHP 内置扩展

以下为 PHP 运行环境的内置扩展(built-in extension),能覆盖您的大部分需求:

  1. "Core", "date", "libxml", "openssl", "pcre", "zlib", "curl","filter", "hash", "readline", "Reflection",
  2. "SPL", "session","xml", "standard", "mysqlnd", "bcmath", "bz2", "calendar","ctype", "dom", "mbstring",
  3. "fileinfo", "ftp", "gettext", "gmp", "iconv","imagick", "json", "exif", "mysqli", "pcntl", "PDO",
  4. "pdo_mysql","Phar", "posix", "protobuf", "redis", "shmop", "SimpleXML", "soap","sockets", "sysvmsg", "zip", "memcached",
  5. "sysvsem", "sysvshm", "tokenizer", "xmlreader","xmlrpc", "xmlwriter"

PHP 内置扩展使用示例

以下示例代码使用 imagick 对图片做了简单处理:

  1. <?php
  2. function imageProc($event, $context) {
  3. $image = new Imagick(__DIR__ . '/lena.jpg');
  4. $image->thumbnailImage(100, 0);
  5. $image->writeImages(__DIR__ . "/thumb.jpg", true);
  6. return strval($image->getImageWidth()) . "," . strval($image->getImageHeight());
  7. }

PHP 自定义扩展

您可以在函数入口文件同级目录下创建一个名为 extension 的目录,并且将扩展对应的 .ini 和 .so 文件放在 extension 目录下,以此添加 PHP 自定义扩展。例如,以下为一个 hello 的自定义扩展,假设该扩展里有一份 hello_world 函数:

  1. .
  2. |____extension
  3. | |____hello.ini
  4. | |____hello.so
  5. |____main.php
  • hello.ini

    1. extension=/code/extension/hello.so
  • main.php

    1. <?php
    2. function handler($event, $context) {
    3. var_dump(extension_loaded('hello'));
    4. hello_world();
    5. return "ok";
    6. }

PHP 内置库

函数计算的 PHP 运行环境中自带了一些常用库(Package)供您直接引用,目前包含的内置库有:

Package 版本 内置库介绍
oss v2.3.0 对象存储 OSS SDK for PHP
tablestore v4.1.0 表格存储 TableStore SDK for PHP
mns v1.3.5.5 消息服务 MNS SDK for PHP
fc v1.1.0 函数计算 FC SDK for PHP

PHP 内置库使用示例

以下代码使用了 OSS 库中的方法上传了一份 TXT 文件:

  1. <?php
  2. use OSS\OssClient;
  3. function handler($event, $context) {
  4. $accessKeyId = $context["credentials"]["accessKeyId"];
  5. $accessKeySecret = $context["credentials"]["accessKeySecret"];
  6. $securityToken = $context["credentials"]["securityToken"];
  7. $endpoint = "oss-cn-shenzhen.aliyuncs.com";
  8. $ossClient = new OssClient($accessKeyId, $accessKeySecret, $endpoint, false, $securityToken);
  9. $bucket = "my-bucket";
  10. $object = "php.txt";
  11. $content = "Hello fc!";
  12. try {
  13. $ossClient->putObject($bucket, $object, $content);
  14. } catch (OssException $e) {
  15. print($e->getMessage());
  16. }
  17. return 'success';
  18. }

PHP 自定义库

除内置库外,您可以在上传 PHP 函数代码时打包您需要的自定义库。建议您使用 Composer 添加自定义库。

方式一:使用 Composer 添加自定义库示例

您要使用的库已经发布在 Packagist The PHP Package Repository 时,您可以使用 Composer 添加自定义库。本示例将演示通过 Fun 添加一个 Humble HTTP request library package requests 的步骤:

  1. 新建目录用于存放代码和依赖模块。

    1. mkdir /tmp/code
  2. 新建代码文件,例如 /tmp/code/index.php ,在代码中使用requests:

    1. <?php
    2. require_once __DIR__ . "/vendor/autoload.php";
    3. function handler($event, $context){
    4. $headers = array('Accept' => 'application/json');
    5. $options = array('auth' => array('user', 'pass'));
    6. $request = Requests::get('https://www.baidu.com', $headers, $options);
    7. var_dump($request->status_code);
    8. // int(200)
    9. var_dump($request->headers['content-type']);
    10. // string(31) "application/json; charset=utf-8"
    11. var_dump($request->body);
    12. // string(26891) "[...]"
    13. }
  3. 在 /tmp/code 目录下安装自定义库。

    1. 编辑一份文件取名为 composer.json

      1. {
      2. "require": {
      3. "rmccue/requests": ">=1.0"
      4. }
      5. }
    2. 执行命令 composer install --no-dev 安装依赖。

      1. cd /tmp/code
      2. composer install --no-dev
    3. 检查是否成功安装依赖。安装完成之后,/tmp/code 目录的内容应该为如下所示:

      1. ls -l /tmp/code
      2. -rw-r--r-- 1 rsong staff 72 8 22 09:54 composer.json
      3. -rw-r--r-- 1 rsong staff 2165 8 22 09:54 composer.lock
      4. -rw-r--r-- 1 rsong staff 523 8 22 09:54 index.php
      5. drwxr-xr-x 5 rsong staff 160 8 22 09:54 vendor
  4. 编写 template.yml,并发布

    • /tmp/code 编写一个名为 template.yml 的文件

      1. ROSTemplateFormatVersion: '2015-09-01'
      2. Transform: 'Aliyun::Serverless-2018-04-03'
      3. Resources:
      4. FunDemo:
      5. Type: 'Aliyun::Serverless::Service'
      6. phpdemo:
      7. Type: 'Aliyun::Serverless::Function'
      8. Properties:
      9. Handler: index.handler
      10. Runtime: php7.2
      11. CodeUri: './'

      这个 template.yml 的含义如下:声明一个名为 FunDemo 的 服务,并在这个服务下,再声明一个名为 phpdemo 的 函数,配置函数入口为 index.handler,以及函数的 runtime 为 php7.2。并且,我们指定了 CodeUri 为当前目录。在部署时,Fun 会将 CodeUri 指定的目录打包上传。更多的配置规则 请参考

    • 执行 fun deploy 进行发布

      1. fun deploy

      可以看到发布成功的日志:

      1. using region: cn-hangzhou
      2. using accountId: ***********3557
      3. using accessKeyId: ***********r3Ra
      4. using timeout: 300
      5. Waiting for service FunDemo to be deployed...
      6. Waiting for function phpdemo to be deployed...
      7. Waiting for packaging function phpdemo code...
      8. package function phpdemo code done
      9. function phpdemo deploy success
      10. service FunDemo deploy success

      登陆控制台,即可看到相关的服务、函数被创建成功,且触发执行可以可以返回正确的结果。

      这里简单介绍下 fun deploy 所做的事情:使用 Fun 部署时,Fun 会根据这个 template.yml 的配置,为我们创建一个名为 FunDemo 的服务,然后再在该服务下创建一个运行环境为 php7.2、名称为 phpdemo 的函数,并且会将当前目录(CodeUri 指定的目录)打包,作为函数的代码上传到函数计算。因为我们打包的代码包含了 vendor 目录,而我们安装的库都在这个目录中,所以函数计算就可以直接使用这个库了。

方式二:直接下载自定义库

您要使用的库没有发布在 Packagist The PHP Package Repository 时,例如 Aliyun-openapi-php-sdk日志服务 PHP SDK,您可以直接下载对应库,然后在代码中直接引用。本示例通过函数计算命令行工具 Fun 添加了日志服务 PHP SDK 自定义库:

  1. 新建目录存放代码和依赖模块。

    1. mkdir /tmp/code
  2. 新建函数文件,例如 /tmp/code/index.php ,在代码中使用日志服务的自定义库。

    1. <?php
    2. /* 使用 autoloader 类自动加载所有需要的 PHP 模块。注意使用合适的路径指向 autoloader 类所在文件 */
    3. require_once realpath(dirname(__FILE__) . '/aliyun-log-php-sdk/Log_Autoload.php');
    4. function handler($event, $context){
    5. $endpoint = 'cn-hangzhou.sls.aliyuncs.com'; // 选择与上面步骤创建 project 所属区域匹配的接入地址 Endpoint
    6. $accessKeyId = 'your_access_key_id'; // 使用您的阿里云访问秘钥 AccessKeyId
    7. $accessKey = 'your_access_key'; // 使用您的阿里云访问秘钥 AccessKeySecret
    8. $project = 'your_project'; // logProject 名称
    9. $logstore = 'your_logstore'; // LogStore 名称
    10. $client = new Aliyun_Log_Client($endpoint, $accessKeyId, $accessKey);
    11. # 列出当前 logProject 下的所有日志库名称
    12. $req1 = new Aliyun_Log_Models_ListLogstoresRequest($project);
    13. $res1 = $client->listLogstores($req1);
    14. var_dump($res1);
    15. }
  3. 在 /tmp/code 目录中下载依赖。

    1. 执行命令,下载依赖。

      1. cd /tmp/code
      2. git clone https://github.com/aliyun/aliyun-log-php-sdk.git
    2. 检查是否成功下载依赖。下载完成之后,/tmp/code 目录的内容应该为如下所示:

      1. ls -l /tmp/code
      2. drwxr-xr-x 11 rsong staff 352 8 22 10:35 aliyun-log-php-sdk
      3. -rw-r--r-- 1 rsong staff 957 8 22 10:42 index.php
  4. 编写 template.yml 并发布。

    • /tmp/code 编写一个名为 template.yml 的文件

      1. ROSTemplateFormatVersion: '2015-09-01'
      2. Transform: 'Aliyun::Serverless-2018-04-03'
      3. Resources:
      4. FunDemo:
      5. Type: 'Aliyun::Serverless::Service'
      6. phpdemo:
      7. Type: 'Aliyun::Serverless::Function'
      8. Properties:
      9. Handler: index.handler
      10. Runtime: php7.2
      11. CodeUri: './'

      这个 template.yml 的含义如下:声明一个名为 FunDemo 的 服务,并在这个服务下,再声明一个名为 phpdemo 的 函数,配置函数入口为 index.handler,以及函数的 runtime 为 php7.2。并且,我们指定了 CodeUri 为当前目录。在部署时,Fun 会将 CodeUri 指定的目录打包上传。更多的配置规则 请参考

    • 执行 fun deploy 进行发布

      1. fun deploy

      可以看到发布成功的日志:

      1. using region: cn-hangzhou
      2. using accountId: ***********3557
      3. using accessKeyId: ***********r3Ra
      4. using timeout: 300
      5. Waiting for service FunDemo to be deployed...
      6. Waiting for function phpdemo to be deployed...
      7. Waiting for packaging function phpdemo code...
      8. package function phpdemo code done
      9. function phpdemo deploy success
      10. service FunDemo deploy success

      登陆控制台,即可看到相关的服务、函数被创建成功,且触发执行可以可以返回正确的结果。

      这里简单介绍下 fun deploy 所做的事情:使用 Fun 部署时,Fun 会根据这个 template.yml 的配置,为我们创建一个名为 FunDemo 的服务,然后再在该服务下创建一个运行环境为 php7.2、名称为 phpdemo 的函数,并且会将当前目录(CodeUri 指定的目录)打包,作为函数的代码上传到函数计算。因为我们打包的代码包含了将包含了整个项目目录,而我们安装的库都在这个目录中,所以函数计算就可以直接使用这个库了。

调用外部命令

当您的 PHP 函数需要调用非 PHP 语言构建的工具,例如,例如 Shell 脚本、C++ 或者 go 编译出来的可执行文件时,您可以将工具与函数代码一起打包上传,然后在函数中通过运行外部命令的方法使用工具。常见的调用外部命令的 PHP 方法有 execsystemshell-exec

说明:使用 C、C++ 和 go 编译的可执行文件,需要与函数计算的运行环境兼容。函数计算的 PHP 运行环境采用了 Linux 4.4.24-2.al7.x86_64 内核版本和 docker pull php:7.2 基础镜像。

以下示例演示了如何使用 exec 方法调用一份 Shell 脚本:

  1. <?php
  2. function handler($evt, $ctx){
  3. $script = $_ENV['FC_FUNC_CODE_PATH'] . '/script.sh';
  4. $a = exec("bash " . $script, $out, $status);
  5. print_r($a);
  6. var_dump($out);
  7. print_r($status);
  8. }

错误处理

PHP 函数在执行过程中发生异常时,函数计算捕获异常并返回异常信息。以下示例代码返回了 oops 的异常信息:

  1. <?php
  2. function handler($event, $context) {
  3. throw new Exception("oops");
  4. }

根据以上示例代码,您调用函数时可能收到如下响应信息:

  1. {
  2. "errorMessage":"oops",
  3. "errorType":"Exception",
  4. "stackTrace":{
  5. "file":"/code/index.php",
  6. "line":3,
  7. "traceString":"#0 /var/fc/runtime/php7/src/invoke.php(67): handler('{\n "product"...', Array)
  8. #1 "
  9. }
  10. }

发生异常时,函数调用的响应的 HTTP header 中会包含 X-Fc-Error-Type: UnhandledInvocationError。更多有关函数计算的错误类型,请参阅 错误类型