Remove PHP 7.1 (#77)

This version of PHP is no longer supported by the PHP group as per
https://www.php.net/supported-versions.php
diff --git a/.travis.yml b/.travis.yml
index acf58e9..b9ac3f8 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -41,11 +41,6 @@
       all_branches: true
       repo: apache/openwhisk-runtime-php
   - provider: script
-    script: "./tools/travis/publish.sh openwhisk 7.1 nightly"
-    on:
-      branch: master
-      repo: apache/openwhisk-runtime-php
-  - provider: script
     script: "./tools/travis/publish.sh openwhisk 7.2 nightly"
     on:
       branch: master
diff --git a/README.md b/README.md
index bfd6b8d..f954d6b 100644
--- a/README.md
+++ b/README.md
@@ -24,7 +24,7 @@
 
 ## PHP versions
 
-This runtime provides PHP 7.3, 7.2 and 7.1.
+This runtime provides PHP 7.3 & 7.2.
 
 ### Give it a try today
 To use as a docker action
@@ -39,11 +39,6 @@
 wsk action update myAction myAction.php --docker openwhisk/action-php-v7.2:latest
 ```
 
-PHP 7.1:
-```
-wsk action update myAction myAction.php --docker openwhisk/action-php-v7.1:latest
-```
-
 This works on any deployment of Apache OpenWhisk
 
 ### To use on deployment that contains the runtime as a kind
@@ -59,28 +54,21 @@
 wsk action update myAction myAction.php --kind php:7.2
 ```
 
-PHP 7.1:
-```
-wsk action update myAction myAction.php --kind php:7.1
-```
-
 ### Local development
 ```
 ./gradlew core:php7.3Action:distDocker
 ./gradlew core:php7.2Action:distDocker
-./gradlew core:php7.1Action:distDocker
 ```
-This will produce the images `whisk/action-php-v7.3`, `whisk/action-php-v7.2` & `whisk/action-php-v7.1`
+This will produce the images `whisk/action-php-v7.3` & `whisk/action-php-v7.2`
 
 Build and Push image
 ```
 docker login
 ./gradlew core:php7.3Action:distDocker -PdockerImagePrefix=$prefix-user -PdockerRegistry=docker.io
 ./gradlew core:php7.2Action:distDocker -PdockerImagePrefix=$prefix-user -PdockerRegistry=docker.io
-./gradlew core:php7.1Action:distDocker -PdockerImagePrefix=$prefix-user -PdockerRegistry=docker.io
 ```
 
-Deploy OpenWhisk using ansible environment that contains the kinds `php:7.3`, `php:7.2` & `php:7.1`
+Deploy OpenWhisk using ansible environment that contains the kinds `php:7.3` & `php:7.2`
 Assuming you have OpenWhisk already deploy locally and `OPENWHISK_HOME` pointing to root directory of OpenWhisk core repository.
 
 Set `ROOTDIR` to the root directory of this repository.
@@ -111,10 +99,7 @@
 docker tag whisk/php7.2Action $user_prefix/action-php-v7.2
 docker push $user_prefix/action-php-v7.2
 ```
-```
-docker tag whisk/php7.1Action $user_prefix/action-php-v7.1
-docker push $user_prefix/action-php-v7.1
-```
+
 Then create the action using your the image from dockerhub
 ```
 wsk action update myAction myAction.php --docker $user_prefix/action-php-v7.3
diff --git a/core/php7.1Action/CHANGELOG.md b/core/php7.1Action/CHANGELOG.md
deleted file mode 100644
index b12368f..0000000
--- a/core/php7.1Action/CHANGELOG.md
+++ /dev/null
@@ -1,62 +0,0 @@
-<!--
-#
-# Licensed to the Apache Software Foundation (ASF) under one or more
-# contributor license agreements.  See the NOTICE file distributed with
-# this work for additional information regarding copyright ownership.
-# The ASF licenses this file to You under the Apache License, Version 2.0
-# (the "License"); you may not use this file except in compliance with
-# the License.  You may obtain a copy of the License at
-#
-#     http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
--->
-
-## Apache 1.14.0 (next release)
-Changes:
-  - Update version of PHP to 7.1.30
-  - Support getenv()
-
-## Apache 1.13.0-incubating
-Changes:
-  - Update version of PHP to 7.1.27
-
-## 1.12.0-incubating
-## 1.0.3
-Changes:
-  - Allow /run endpoint to accept more environment variables [#40](https://github.com/apache/openwhisk-runtime-php/pull/40)
-
-## 1.0.2
-Changes:
-  - Disallow re-initialization of function.
-
-## 1.0.1
-
-- Change: Update PHP to 7.1.18
-- Change: Update [guzzlehttp/guzzle](https://packagist.org/packages/guzzlehttp/guzzle) to 6.3.3
-- Change: Update [ramsey/uuid](https://packagist.org/packages/ramsey/uuid) to 3.7.3
-
-## 1.0.0
-Initial release
-
-- Added: PHP: 7.1.9
-- Added: PHP extensions in addition to the standard ones:
-    - bcmath
-    - curl
-    - gd
-    - intl
-    - mbstring
-    - mysqli
-    - pdo_mysql
-    - pdo_pgsql
-    - pdo_sqlite
-    - soap
-    - zip
-- Added: Composer packages:
-    - [guzzlehttp/guzzle](https://packagist.org/packages/guzzlehttp/guzzle): 6.3.0
-    - [ramsey/uuid](https://packagist.org/packages/ramsey/uuid): 3.6.1
diff --git a/core/php7.1Action/Dockerfile b/core/php7.1Action/Dockerfile
deleted file mode 100644
index cee9a2f..0000000
--- a/core/php7.1Action/Dockerfile
+++ /dev/null
@@ -1,63 +0,0 @@
-#
-# Licensed to the Apache Software Foundation (ASF) under one or more
-# contributor license agreements.  See the NOTICE file distributed with
-# this work for additional information regarding copyright ownership.
-# The ASF licenses this file to You under the Apache License, Version 2.0
-# (the "License"); you may not use this file except in compliance with
-# the License.  You may obtain a copy of the License at
-#
-#     http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-FROM php:7.1.30-alpine
-
-RUN \
-    apk update && apk upgrade && \
-    # install dependencies
-   apk add \
-       postgresql-dev \
-       icu \
-       icu-libs \
-       icu-dev \
-       freetype-dev \
-       libjpeg-turbo-dev \
-       libpng-dev \
-       libxml2-dev \
-   && \
-   # install useful PHP extensions
-   docker-php-ext-install \
-       opcache \
-       mysqli \
-       pdo_mysql \
-       pdo_pgsql \
-       intl \
-       bcmath \
-       zip \
-       gd \
-       soap
-
-# install composer
-RUN curl -s -f -L -o /tmp/installer.php https://getcomposer.org/installer \
-    && php /tmp/installer.php --no-ansi --install-dir=/usr/bin --filename=composer \
-    && composer --ansi --version --no-interaction
-
-# create src directory to store action files
-RUN mkdir -p /action/src
-
-# install Composer dependencies
-COPY composer.json /action
-RUN cd /action && /usr/bin/composer install --no-plugins --no-scripts --prefer-dist --no-dev -o && rm composer.lock
-
-# copy required files
-COPY router.php /action
-COPY runner.php /action
-
-# Run webserver on port 8080
-
-CMD [ "php", "-S", "0.0.0.0:8080", "-d", "expose_php=0", "-d", "html_errors=0", "-d", "error_reporting=E_ALL", "/action/router.php" ]
diff --git a/core/php7.1Action/build.gradle b/core/php7.1Action/build.gradle
deleted file mode 100644
index a6e9292..0000000
--- a/core/php7.1Action/build.gradle
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-ext.dockerImageName = 'action-php-v7.1'
-apply from: '../../gradle/docker.gradle'
diff --git a/core/php7.1Action/composer.json b/core/php7.1Action/composer.json
deleted file mode 100644
index 5ae09fb..0000000
--- a/core/php7.1Action/composer.json
+++ /dev/null
@@ -1,11 +0,0 @@
-{
-    "config": {
-        "platform": {
-            "php": "7.1"
-        }
-    },
-    "require": {
-        "guzzlehttp/guzzle": "6.3.3",
-        "ramsey/uuid": "3.7.3"
-    }
-}
diff --git a/core/php7.1Action/router.php b/core/php7.1Action/router.php
deleted file mode 100644
index db63b7d..0000000
--- a/core/php7.1Action/router.php
+++ /dev/null
@@ -1,350 +0,0 @@
-<?php
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/**
- * router.php
- *
- * This file is the API client for the action. The controller POSTs /init to set up the action and
- * then POSTs to /run to invoke it.
- */
-
-// set up an output buffer to redirect any script output to stdout, rather than the default
-// php://output, so that it goes to the logs, not the HTTP client.
-ob_start(function ($data) {
-    file_put_contents("php://stdout", $data);
-    return '';
-}, 1, PHP_OUTPUT_HANDLER_CLEANABLE | PHP_OUTPUT_HANDLER_FLUSHABLE | PHP_OUTPUT_HANDLER_REMOVABLE);
-
-const ACTION_SRC_FILENAME = 'index.php';
-const SRC_DIR  = __DIR__ . '/src';
-const ACTION_CONFIG_FILE = __DIR__. '/config.json';
-const ACTION_SRC_FILE = SRC_DIR . '/' . ACTION_SRC_FILENAME;
-const ACTION_RUNNER_FILE = __DIR__. '/runner.php';
-const TMP_ZIP_FILE = '/action.zip';
-
-// execute the revelant endpoint
-$result = route($_SERVER['REQUEST_URI']);
-
-// send response
-$body = json_encode((object)$result);
-header('Content-Type: application/json');
-header("Content-Length: " . mb_strlen($body));
-ob_end_clean();
-echo $body;
-exit;
-
-/**
- * executes the relevant method for a given URL and return an array of data to send to the client
- */
-function route(string $uri) : array
-{
-    try {
-        switch ($uri) {
-            case '/init':
-                return init();
-
-            case '/run':
-                return run();
-
-            default:
-                throw new RuntimeException('Unexpected call to ' . $_SERVER["REQUEST_URI"], 500);
-        }
-    } catch (Throwable $e) {
-        $code = $e->getCode() < 400 ? 500 : $e->getCode();
-
-        if ($code != 502) {
-            writeTo("php://stdout", 'Error: ' . $e->getMessage());
-        }
-        writeSentinels();
-
-        http_response_code($code);
-        return ['error' => $e->getMessage()];
-    }
-
-    return '';
-}
-
-/**
- * Handle the /init endpoint
- *
- * This end point is called once per container creation. It gives us the code we need
- * to run and the name of the function within that code that's the entry point. As PHP
- * has a setup/teardown model, we store the function name to a config file for retrieval
- * in the /run end point.
- *
- * @return array Data to return to the client
- */
-function init() : array
-{
-    // check that we haven't already been initialised
-    if (file_exists(ACTION_CONFIG_FILE)) {
-        writeTo("php://stdout", 'Error: Cannot initialize the action more than once.');
-        http_response_code(403);
-        return ['error' => 'Cannot initialize the action more than once.'];
-    }
-
-    // data is POSTed to us as a JSON string
-    $post = file_get_contents('php://input');
-    $data = json_decode($post, true)['value'] ?? [];
-
-    $name = $data['name'] ?? '';         // action name
-    $main = $data['main'] ?? 'main';     // function to call (default: main)
-    $code = trim($data['code'] ?? '');   // source code to run
-    $binary = $data['binary'] ?? false;  // code is binary?
-
-    if (!$code) {
-        throw new RuntimeException("Missing main/no code to execute.");
-    }
-
-    if ($binary) {
-        // binary code is a zip file that's been base64 encoded, so unzip it
-        unzipString($code, SRC_DIR);
-
-        // if the zip file didn't contain a vendor directory, move our vendor into the src folder
-        if (! file_exists(SRC_DIR . '/vendor/autoload.php')) {
-            exec('mv ' . escapeshellarg(__DIR__ . '/vendor') . ' ' . escapeshellarg(SRC_DIR . '/vendor'));
-        }
-
-        // check that we have the expected action source file
-        if (! file_exists(ACTION_SRC_FILE)) {
-            throw new RuntimeException('Zipped actions must contain ' . ACTION_SRC_FILENAME . ' at the root.', 500);
-        }
-    } else {
-        // non-binary code is a text string, so save to disk
-        file_put_contents(ACTION_SRC_FILE, $code);
-
-        // move vendor folder into the src folder
-        exec('mv ' . escapeshellarg(__DIR__ . '/vendor') . ' ' . escapeshellarg(SRC_DIR . '/vendor'));
-    }
-
-    // is action file valid PHP? run `php -l` to find out
-    list($returnCode, $stdout, $stderr) = runPHP(['-l', '-f', ACTION_SRC_FILE]);
-    if ($returnCode != 0) {
-        writeTo("php://stderr", $stderr);
-        writeTo("php://stdout", $stdout);
-
-        $message = 'PHP syntax error in ' . ($binary ? ACTION_SRC_FILENAME : 'action.');
-        throw new RuntimeException($message, 500);
-    }
-
-    // does the action have the expected function name?
-    $testCode = 'require "' . ACTION_SRC_FILENAME . '"; exit((int)(! function_exists("' . $main .'")));';
-    list($returnCode, $stdout, $stderr) = runPHP(['-r', $testCode]);
-    if ($returnCode != 0) {
-        writeTo("php://stderr", $stderr);
-        writeTo("php://stdout", $stdout);
-        throw new RuntimeException("The function $main is missing.");
-    }
-
-    // write config file for use by /run
-    $config = [
-        'file' => ACTION_SRC_FILENAME,
-        'function' => $main,
-        'name' => $name,
-    ];
-    file_put_contents(ACTION_CONFIG_FILE, json_encode($config));
-
-    return ["OK" => true];
-}
-
-/**
- * Handle the /run endpoint
- *
- * This end point is called once per action invocation. We load the function name from
- * the config file and then invoke it. Note that as PHP writes to php://output, we
- * capture in an output buffer and write the buffer to stdout for the OpenWhisk logs.
- *
- * @return array Data to return to the client
- */
-function run() : array
-{
-    if (! file_exists(ACTION_SRC_FILE)) {
-        error_log('NO ACTION FILE: ' . ACTION_SRC_FILE);
-        throw new RuntimeException('!Could not find action file: ' . ACTION_SRC_FILENAME, 500);
-    }
-
-    // load config to pass to runner
-    if (! file_exists(ACTION_CONFIG_FILE)) {
-        error_log('NO CONFIG FILE: ' . ACTION_CONFIG_FILE);
-        throw new RuntimeException('Could not find config file', 500);
-    }
-    $config = file_get_contents(ACTION_CONFIG_FILE);
-    
-    // Extract the posted data
-    $post = json_decode(file_get_contents('php://input'), true);
-    if (!is_array($post)) {
-        $post = [];
-    }
-
-    // assign environment variables from the posted data
-    $env = array_filter($_ENV, function ($k) {
-        // only pass OpenWhisk environment variables to the action
-        return stripos($k, '__OW_') === 0;
-    }, ARRAY_FILTER_USE_KEY);
-    $env['PHP_VERSION'] = $_ENV['PHP_VERSION'];
-    foreach (array_keys($post) as $param) {
-        if ($param !== "value") {
-            $envKeyName = '__OW_' . strtoupper($param);
-            $env[$envKeyName] = $post[$param];
-            putenv($envKeyName . '=' . $post[$param]);
-        }
-    }
-
-    // extract the function arguments from the posted data's "value" field
-    $args = '{}';
-    if (array_key_exists('value', $post) && is_array($post['value'])) {
-        $args = json_encode($post['value']);
-    }
-
-    // run the action
-    list($returnCode, $stdout, $stderr) = runPHP(
-        ['-d', 'error_reporting=E_ALL', '-f', ACTION_RUNNER_FILE, '--', $config],
-        $args,
-        $env
-    );
-
-    // separate the esponse to send back to the client: it's the last line of stdout
-    $pos = strrpos($stdout, PHP_EOL);
-    if ($pos == false) {
-        // just one line of output
-        $lastLine = $stdout;
-        $stdout = '';
-    } else {
-        $pos++;
-        $lastLine = trim(substr($stdout, $pos));
-        $stdout = trim(substr($stdout, 0, $pos));
-    }
-
-    // write out the action's stderr and stdout
-    writeTo("php://stderr", $stderr);
-    writeTo("php://stdout", $stdout);
-
-    $output = json_decode($lastLine, true);
-    if ($returnCode != 0 || !is_array($output)) {
-        // an error occurred while running the action
-        // the return code will be 1 if the stdout is printable to the user
-        if ($returnCode != 1) {
-            // otherwise put out a generic message and send $lastLine to stdout
-            writeTo("php://stdout", $lastLine);
-            $lastLine = 'An error occurred running the action.';
-        }
-        throw new RuntimeException($lastLine, 502);
-    }
-
-    // write sentinels as action is completed
-    writeSentinels();
-
-    return $output;
-}
-
-/**
- * Unzip a base64 encoded string to a directory
- */
-function unzipString(string $b64Data, $dir): void
-{
-    file_put_contents(TMP_ZIP_FILE, base64_decode($b64Data));
-
-    $zip = new ZipArchive();
-    $res = $zip->open(TMP_ZIP_FILE);
-    if ($res !== true) {
-        $reasons = [
-            ZipArchive::ER_EXISTS => "File already exists.",
-            ZipArchive::ER_INCONS => "Zip archive inconsistent.",
-            ZipArchive::ER_INVAL => "Invalid argument.",
-            ZipArchive::ER_MEMORY => "Malloc failure.",
-            ZipArchive::ER_NOENT => "No such file.",
-            ZipArchive::ER_NOZIP => "Not a zip archive.",
-            ZipArchive::ER_OPEN => "Can't open file.",
-            ZipArchive::ER_READ => "Read error.",
-            ZipArchive::ER_SEEK => "Seek error.",
-        ];
-        $reason = $reasons[$res] ?? "Unknown error: $res.";
-        throw new RuntimeException("Failed to open zip file: $reason", 500);
-    }
-
-    $res = $zip->extractTo($dir . '/');
-    $zip->close();
-}
-
-/**
- * Write the OpenWhisk sentinels to stdout and stderr so that it knows that we've finished
- * writing data to them.
- *
- * @return void
- */
-function writeSentinels() : void
-{
-    // write out sentinels as we've finished all log output
-    writeTo("php://stderr", "XXX_THE_END_OF_A_WHISK_ACTIVATION_XXX");
-    writeTo("php://stdout", "XXX_THE_END_OF_A_WHISK_ACTIVATION_XXX");
-}
-
-/**
- * Run the PHP command in a separate process
- *
- * This ensures that if the action causes a fatal error, we can handle it.
- *
- * @param  array  $args  arguments to the PHP executable
- * @param  string $stdin stdin to pass to the process
- * @param  array  $env   environment variables to set for the process
- * @return array         array containing [int return code, string stdout string stderr]
- */
-function runPHP(array $args, string $stdin = '', array $env = []) : array
-{
-    $cmd = '/usr/local/bin/php ' . implode(' ', array_map('escapeshellarg', $args));
-
-    $process = proc_open(
-        $cmd,
-        [
-            0 => ['pipe', 'r'],
-            1 => ['pipe', 'w'],
-            2 => ['pipe', 'w'],
-        ],
-        $pipes,
-        SRC_DIR,
-        $env
-    );
-
-    // write to the process' stdin
-    $bytes = fwrite($pipes[0], $stdin);
-    fclose($pipes[0]);
-
-    // read the process' stdout
-    $stdout = stream_get_contents($pipes[1]);
-    fclose($pipes[1]);
-
-    // read the process' stderr
-    $stderr = stream_get_contents($pipes[2]);
-    fclose($pipes[2]);
-
-    // close process & get return code
-    $returnCode = proc_close($process);
-
-    // tidy up paths in any PHP stack traces
-    $stderr = str_replace(__DIR__ . '/', '', trim($stderr));
-    $stdout = str_replace(__DIR__ . '/', '', trim($stdout));
-
-    return [$returnCode, $stdout, $stderr];
-}
-
-function writeTo($pipe, $text)
-{
-    if ($text) {
-        file_put_contents($pipe, $text . PHP_EOL);
-    }
-}
diff --git a/core/php7.1Action/runner.php b/core/php7.1Action/runner.php
deleted file mode 100644
index 0e91747..0000000
--- a/core/php7.1Action/runner.php
+++ /dev/null
@@ -1,69 +0,0 @@
-<?php
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/**
- * runner.php
- *
- * This file runs the action code provided by the user. It is executed in the PHP CLI environment
- * by router.php and will require the index.php file and call the main() function (or whatever has
- * been configured).
- *
- * The configuration information is passed in as a JSON object as the first argument to this script
- * and the OpenWhisk action argumentsare passed in as a JSON object via stdin.
- */
-
-// read config from argv[1] and assign
-if ($argc != 2) {
-    file_put_contents("php://stderr", 'Expected a single config parameter');
-    exit(1);
-}
-
-$config = json_decode($argv[1], true);
-if (!is_array($config)) {
-    file_put_contents("php://stderr", "Invalid config: {$argv[1]}.");
-    exit(1);
-}
-
-$_actionFile = $config['file'] ?? 'index.php';
-$_functionName = $config['function'] ?? 'main';
-unset($argv[1], $config);
-
-// does the action file exist?
-if (! file_exists($_actionFile)) {
-    file_put_contents("php://stderr", "Could not find action file: $_actionFile.");
-    exit(1);
-}
-
-// run the action with arguments from stdin
-require __DIR__ . '/src/vendor/autoload.php';
-require $_actionFile;
-
-$result = $_functionName(json_decode(file_get_contents('php://stdin') ?? [], true));
-
-if (is_scalar($result)) {
-    file_put_contents("php://stderr", 'Result must be an array but has type "'
-        . gettype($result) . '": ' . (string)$result . "\n");
-    file_put_contents("php://stdout", 'The action did not return a dictionary.');
-    exit(1);
-} elseif (is_object($result) && method_exists($result, 'getArrayCopy')) {
-    $result = $result->getArrayCopy();
-}
-
-// cast result to an object for json_encode to ensure that an empty array becomes "{}"
-echo "\n";
-echo json_encode((object)$result);
diff --git a/settings.gradle b/settings.gradle
index da27e32..2c53f27 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -17,7 +17,6 @@
 
 include 'tests'
 
-include 'core:php7.1Action'
 include 'core:php7.2Action'
 include 'core:php7.3Action'
 
diff --git a/tests/src/test/scala/runtime/actionContainers/Php71ActionContainerTests.scala b/tests/src/test/scala/runtime/actionContainers/Php71ActionContainerTests.scala
deleted file mode 100644
index ca9e619..0000000
--- a/tests/src/test/scala/runtime/actionContainers/Php71ActionContainerTests.scala
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package runtime.actionContainers
-
-import org.junit.runner.RunWith
-import org.scalatest.junit.JUnitRunner
-import actionContainers.ResourceHelpers.ZipBuilder
-import spray.json._
-
-@RunWith(classOf[JUnitRunner])
-class Php71ActionContainerTests extends Php7ActionContainerTests {
-
-  override lazy val phpContainerImageName = "action-php-v7.1"
-
-  override val testLargeInput = TestConfig("", skipTest = true)
-
-  it should "fail to initialize with bad code" in {
-    val (out, err) = withPhp7Container { c =>
-      val code = """
-                |<?php
-                | 10 PRINT "Hello world!"
-                | 20 GOTO 10
-            """.stripMargin
-
-      val (initCode, error) = c.init(initPayload(code))
-      initCode should not be (200)
-      error shouldBe a[Some[_]]
-      error.get shouldBe a[JsObject]
-      error.get.fields("error").toString should include("PHP syntax error")
-    }
-
-    // Somewhere, the logs should mention an error occurred.
-    checkStreams(out, err, {
-      case (o, e) =>
-        (o + e).toLowerCase should include("error")
-        (o + e).toLowerCase should include("syntax")
-    })
-  }
-
-  it should "fail gracefully on invalid zip files" in {
-    // Some text-file encoded to base64.
-    val code = "Q2VjaSBuJ2VzdCBwYXMgdW4gemlwLgo="
-
-    val (out, err) = withPhp7Container { c =>
-      val (initCode, error) = c.init(initPayload(code))
-      initCode should not be (200)
-      error shouldBe a[Some[_]]
-      error.get shouldBe a[JsObject]
-      error.get.fields("error").toString should include("Failed to open zip file")
-    }
-
-    // Somewhere, the logs should mention the failure
-    checkStreams(out, err, {
-      case (o, e) =>
-        (o + e).toLowerCase should include("error")
-        (o + e).toLowerCase should include("failed to open zip file")
-    })
-  }
-
-  it should "fail gracefully on valid zip files that are not actions" in {
-    val srcs = Seq(Seq("hello") -> """
-                | Hello world!
-            """.stripMargin)
-
-    val code = ZipBuilder.mkBase64Zip(srcs)
-
-    val (out, err) = withPhp7Container { c =>
-      c.init(initPayload(code))._1 should not be (200)
-    }
-
-    checkStreams(out, err, {
-      case (o, e) =>
-        (o + e).toLowerCase should include("error")
-        (o + e).toLowerCase should include("zipped actions must contain index.php at the root.")
-    })
-  }
-
-  it should "fail gracefully on valid zip files with invalid code in index.php" in {
-    val (out, err) = withPhp7Container { c =>
-      val srcs = Seq(Seq("index.php") -> """
-                    | <?php
-                    | 10 PRINT "Hello world!"
-                    | 20 GOTO 10
-                """.stripMargin)
-
-      val code = ZipBuilder.mkBase64Zip(srcs)
-
-      val (initCode, error) = c.init(initPayload(code))
-      initCode should not be (200)
-      error shouldBe a[Some[_]]
-      error.get shouldBe a[JsObject]
-      error.get.fields("error").toString should include("PHP syntax error in index.php")
-    }
-
-    // Somewhere, the logs should mention an error occurred.
-    checkStreams(out, err, {
-      case (o, e) =>
-        (o + e).toLowerCase should include("error")
-        (o + e).toLowerCase should include("syntax")
-    })
-  }
-}