函数计算支持PHP 7.2.7(Runtime=php7.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)的相关内容,这些能满足您的大部分需求。

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

内置扩展应用示例

以下示例代码使用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自定义扩展

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

.
|____extension
| |____hello.ini
| |____hello.so
|____main.php           
  • hello.ini
    extension=/code/extension/hello.so            
  • main.php
        <?php
        function handler($event, $context) {
            var_dump(extension_loaded('hello'));
            hello_world(); 
            return "ok";
        }                    

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. 执行命令composer install --no-dev安装依赖。
        cd /tmp/code
        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'; // 选择与上面步骤创建project所属区域匹配的接入地址Endpoint
         $accessKeyId = 'your_access_key_id';        // 使用您的阿里云访问密钥AccessKeyId
         $accessKey = 'your_access_key';             // 使用您的阿里云访问密钥AccessKeySecret
         $project = 'your_project';                  // logProject名称
         $logstore = 'your_logstore';                // LogStore名称
         $client = new Aliyun_Log_Client($endpoint, $accessKeyId, $accessKey);
         # 列出当前logProject下的所有日志库名称
         $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

说明 使用C、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);
}