函数计算支持PHP 7.2运行环境。本文介绍了PHP运行环境的相关内容,包括打印日志、错误处理、PHP扩展、自定义库和调用外部命令。

打印日志

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

  • 日志级别

    您可以通过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 调试日志

    函数日志的更多详情,请参见授予事件源访问函数计算的权限

  • 日志打印示例一
    <?php
    function handler($event, $context) {
        $logger = $GLOBALS['fcLogger'];
        $logger->info("hello world");
        return 'hello world';
    };           

    执行上述代码输出的日志内容如下所示。

    message:2017-07-05T05:13:35.920Z a72df088-f738-cee3-e0fe-323ad89118e5 [INFO] hello world            
  • 日志打印示例二
    <?php
    use Monolog\Logger;
    function handler($evt, $ctx) {
        $logger = $GLOBALS['fcLogger'];
        $logger->setLevel(400);
        $logger->error("console error 1");
        $logger->info("console info 1");
        $logger->warning("console warn 1");
        $logger->debug("console debug 1");
    
        $logger->setLevel(Logger::WARNING);
        $logger->error("console error 2");
        $logger->info("console info 2");
        $logger->warn("console warn 2");
        $logger->debug("console debug 2");
    
        $logger->setLevel(200);
    }            

    执行上述代码输出的日志内容如下所示。

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

错误处理

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

<?php
function handler($event, $context) {
  throw new Exception("oops");
}           

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

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

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

PHP内置扩展

下文介绍关于PHP运行环境内置扩展(built-in extension)的列表和应用示例。

内置扩展列表

说明 如果您需要查看并打印已安装的PHP扩展,请在目标函数的代码内写入print_r(get_loaded_extensions());
bcmath bz2 calendar Core
ctype curl date dom
exif fileinfo filter ftp
gd gettext gmp hash
iconv imagick json libxml
mbstring memcached mysqli mysqlnd
openssl pcntl pcre PDO
pdo_mysql Phar posix protobuf
readline redis Reflection session
shmop SimpleXML soap sockets
SPL standard sysvmsg sysvsem
sysvshm tokenizer xdebug xml
xmlreader xmlrpc xmlwriter Zend OPcache
zip zlib Xdebug Zend OPcache

内置扩展应用示例

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

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

安装PHP非内置扩展

当您需要使用PHP Runtime非内置扩展时,请根据以下步骤安装:

  1. 在项目目录中执行以下命令,启动进入PHP Runtime镜像,并可以将当前目录挂载到容器的/code目录:
    docker run -v $(pwd):/code -it --entrypoint=""  registry.cn-beijing.aliyuncs.com/aliyunfc/runtime-php7.2:latest  bash
    说明 如果您的电脑是Windows操作系统,可以将$(pwd)修改为具体的绝对目录。
  2. 安装MongoDB扩展,并将mongodb.so复制到/code目录。
    1. 执行以下命令,在容器内安装MongoDB扩展:
      pecl install mongodb
    2. 执行以下命令,找到ZIP扩展:
      find /usr -name "mongodb.so"
    3. 执行以下命令,复制扩展文件到/code目录即拷贝到本地机器的目录:
      cp /usr/local/lib/php/extensions/no-debug-non-zts-20170718/mongodb.so /code
    4. 执行以下命令,退出容器:
      exit
    5. 执行以下命令,确认是否存在zip.so文件,若存在则证明SO文件已经存在本地:
      ls
      预期输出:
      mongodb.so
  3. 登录函数计算控制台
  4. 在左侧导航栏,单击服务及函数
  5. 在顶部菜单栏,选择地域。
  6. 服务列表页面,单击目标服务。
  7. 在目标服务页面,单击目标函数,在函数代码页签中的函数入口文件同级目录下创建extension文件夹。
    说明 您创建的extension文件需和bootstrap在同一级别。
  8. mongodb.so文件添加至extension文件内,并在extension文件内创建mongodb.ini文件。
  9. 编写mongodb.ini文件。
    将以下内容填写至mongodb.ini文件中,并保存。
    extension=/code/extension/mongodb.so
如果需要安装其他自定义扩展,请按需下载:

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

内置库使用示例

以下代码示例演示了如何使用OSS内置库中的方法上传一个.txt文件。

<?php
use OSS\OssClient;
function handler($event, $context) {
     $accessKeyId = $context["credentials"]["accessKeyId"];
     $accessKeySecret = $context["credentials"]["accessKeySecret"];
     $securityToken = $context["credentials"]["securityToken"];
     $endpoint = "oss-cn-shenzhen.aliyuncs.com";
     $ossClient = new OssClient($accessKeyId, $accessKeySecret, $endpoint, false, $securityToken);
     $bucket = "my-bucket";
     $object = "php.txt";
     $content = "Hello fc!";
     try {
        $ossClient->putObject($bucket, $object, $content);
     } catch (OssException $e) {
        print($e->getMessage());
     }
    return 'success';
}           

PHP自定义库

安装自定义库

除内置库外,您可以在上传PHP函数代码时打包您需要的自定义库。下文介绍两种安装自定义库的方式。

  • (推荐)方式一:使用Composer添加自定义库示例

    您要使用的库已经发布在Packagist The PHP Package Repository时,您可以使用Composer添加自定义库。下文演示添加Humble HTTP request library package requests的步骤。

    1. 新建目录用于存放代码和依赖模块。
      mkdir /tmp/code       
    2. 新建代码文件,例如/tmp/code/index.php,在代码中使用requests
      <?php
      require_once __DIR__ . "/vendor/autoload.php";
      function handler($event, $context){
         $headers = array('Accept' => 'application/json');
         $options = array('auth' => array('user', 'pass'));
         $request = Requests::get('https://www.baidu.com', $headers, $options);
         var_dump($request->status_code);
         // int(200)
         var_dump($request->headers['content-type']);
         // string(31) "application/json; charset=utf-8"
         var_dump($request->body);
         // string(26891) "[...]"
      }            
    3. /tmp/code目录下安装自定义库。
      1. 创建并编辑composer.json文件,该文件内容如下所示:
        {
            "require": {
                "rmccue/requests": ">=1.0"
            }
        }           
      2. 执行以下命令,进入代码目录:
        cd /tmp/code
      3. 执行以下命令,安装依赖:
        composer install --no-dev            
  • 方式二:直接下载自定义库
    如果您要使用的库没有发布在Packagist The PHP Package Repository时,例如Aliyun-openapi-php-sdk日志服务PHP SDK,您可以直接下载对应库,然后在代码中直接引用。下文演示添加日志服务PHP SDK自定义库。
    1. 新建目录用于存放代码和依赖模块。
      mkdir /tmp/code            
    2. 新建代码文件,例如/tmp/code/index.php,在代码中使用requests
      <?php
      /* 使用autoloader类自动加载所有需要的PHP模块。注意使用合适的路径指向autoloader类所在文件。 */
      require_once realpath(dirname(__FILE__) . '/aliyun-log-php-sdk/Log_Autoload.php');
      function handler($event, $context){
         $endpoint = 'cn-hangzhou.sls.aliyuncs.com'; // 选择与上面步骤创建日志项目所属地域匹配的接入地址Endpoint。
         $accessKeyId = 'your_access_key_id';        // 使用您的阿里云访问密钥AccessKey ID。
         $accessKey = 'your_access_key';             // 使用您的阿里云访问密钥AccessKey Secret。
         $project = 'your_project';                  // 日志项目名称。
         $logstore = 'your_logstore';                // 日志库名称。
         $client = new Aliyun_Log_Client($endpoint, $accessKeyId, $accessKey);
         # 列出当前日志项目下的所有日志库名称。
         $req1 = new Aliyun_Log_Models_ListLogstoresRequest($project);
         $res1 = $client->listLogstores($req1);
         var_dump($res1);
      }            
    3. /tmp/code目录中下载依赖。
      cd /tmp/code
      git clone https://github.com/aliyun/aliyun-log-php-sdk.git           

打包上传至函数计算

打包时,需要针对文件进行打包,而不是针对代码整体目录进行打包。打包完成后,入口函数文件需要位于包内的根目录。

  • 在Windows下打包时,可以进入函数代码目录,全选所有文件以后,将文件压缩成ZIP包,即代码包。
  • 在Linux下打包时,通过调用ZIP命令时,将源文件指定为代码目录下的所有文件,实现生成部署代码包,例如zip code.zip /tmp/code/*

打包后,在函数计算控制台代码执行页面,您可以选择OSS上传或者代码包上传方式上传代码包。

调用外部命令

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

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

下文代码示例演示如何使用exec方法调用一个Shell脚本。

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