Skip to content

Commit 7dfed39

Browse files
authored
feat(functions): add custom extension sample (GoogleCloudPlatform#1371)
1 parent 685257c commit 7dfed39

File tree

10 files changed

+293
-0
lines changed

10 files changed

+293
-0
lines changed
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# Files from phpize
2+
ext/Makefile.global
3+
ext/acinclude.m4
4+
ext/aclocal.m4
5+
ext/autom4te.cache/
6+
ext/config.guess
7+
ext/config.h.in
8+
ext/config.sub
9+
ext/configure
10+
ext/configure.ac
11+
ext/install-sh
12+
ext/ltmain.sh
13+
ext/missing
14+
ext/mkinstalldirs
15+
ext/run-tests.php
16+
17+
# Files from ./configure
18+
ext/Makefile
19+
ext/Makefile.fragments
20+
ext/Makefile.objects
21+
ext/config.h
22+
ext/config.log
23+
ext/config.nice
24+
ext/config.status
25+
ext/libtool
26+
27+
# Files from make
28+
ext/.libs/
29+
ext/modules/
30+
ext/my_custom_extension.la
31+
ext/my_custom_extension.lo
32+
33+
# The custom PHP extension
34+
my_custom_extension.so
35+
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"require": {
3+
"google/cloud-functions-framework": "^0.7.1"
4+
},
5+
"scripts": {
6+
"gcp-build": "cd ext && phpize --clean && phpize && ./configure && make && cp modules/my_custom_extension.so ..",
7+
"start": [
8+
"@gcp-build",
9+
"Composer\\Config::disableProcessTimeout",
10+
"FUNCTION_TARGET=helloBuildExtension php -d 'extension=./my_custom_extension.so' -S localhost:${PORT:-8080} vendor/bin/router.php"
11+
]
12+
}
13+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
PHP_ARG_ENABLE(my_custom_extension, Whether to enable the MyCustomExtension extension, [ --enable-my-custom-extension Enable MyCustomExtension])
2+
3+
if test "$MY_CUSTOM_EXTENSION" != "no"; then
4+
PHP_NEW_EXTENSION(my_custom_extension, my_custom_extension.c, $ext_shared)
5+
fi
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// include the PHP API itself
2+
#include
3+
// include the extension header
4+
#include "my_custom_extension.h"
5+
6+
// register the "helloworld_from_extension" function to the PHP API
7+
zend_function_entry my_custom_extension_functions[] = {
8+
PHP_FE(helloworld_from_extension, NULL)
9+
{NULL, NULL, NULL}
10+
};
11+
12+
// some information about our module
13+
zend_module_entry my_custom_extension_module_entry = {
14+
STANDARD_MODULE_HEADER,
15+
PHP_MY_CUSTOM_EXTENSION_EXTNAME,
16+
my_custom_extension_functions,
17+
NULL,
18+
NULL,
19+
NULL,
20+
NULL,
21+
NULL,
22+
PHP_MY_CUSTOM_EXTENSION_VERSION,
23+
STANDARD_MODULE_PROPERTIES
24+
};
25+
26+
// use a macro to output additional C code, to make ext dynamically loadable
27+
ZEND_GET_MODULE(my_custom_extension)
28+
29+
// Implement our "Hello World" function, which returns a string
30+
PHP_FUNCTION(helloworld_from_extension) {
31+
zval val;
32+
ZVAL_STRING(&val, "Hello World! (from my_custom_extension.so)\n");
33+
RETURN_STR(Z_STR(val));
34+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
// module constants
2+
#define PHP_MY_CUSTOM_EXTENSION_EXTNAME "my_custom_extension"
3+
#define PHP_MY_CUSTOM_EXTENSION_VERSION "0.0.1"
4+
5+
// the function to be exported
6+
PHP_FUNCTION(helloworld_from_extension);
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
2+
/**
3+
* Copyright 2021 Google LLC.
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
/**
19+
* To use this, set the following environment variables:
20+
* FUNCTION_TARGET=helloHttp
21+
* FUNCTION_EVENT_TYPE=http
22+
*/
23+
24+
use Psr\Http\Message\ServerRequestInterface;
25+
26+
function helloBuildExtension(ServerRequestInterface $request)
27+
{
28+
return helloworld_from_extension();
29+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
;enable our custom extension
2+
extension=/workspace/my_custom_extension.so
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
xml version="1.0" encoding="UTF-8"?>
2+
17+
<phpunit bootstrap="../../testing/bootstrap.php" convertWarningsToExceptions="false">
18+
<testsuites>
19+
<testsuite name="Cloud Functions Build Step Test Suite">
20+
<directory>testdirectory>
21+
testsuite>
22+
testsuites>
23+
<logging>
24+
<log type="coverage-clover" target="build/logs/clover.xml"/>
25+
logging>
26+
<filter>
27+
<whitelist>
28+
<directory suffix=".php">.directory>
29+
<exclude>
30+
<directory>./vendordirectory>
31+
exclude>
32+
whitelist>
33+
filter>
34+
phpunit>
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
2+
/**
3+
* Copyright 2021 Google LLC.
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
declare(strict_types=1);
19+
20+
namespace Google\Cloud\Samples\Functions\ConceptsBuildExtension\Test;
21+
22+
use Google\Cloud\TestUtils\CloudFunctionDeploymentTrait;
23+
use PHPUnit\Framework\TestCase;
24+
25+
/**
26+
* Class DeployTest.
27+
*
28+
* This test is not run by the CI system.
29+
*
30+
* To skip deployment of a new function, run with "GOOGLE_SKIP_DEPLOYMENT=true".
31+
* To skip deletion of the tested function, run with "GOOGLE_KEEP_DEPLOYMENT=true".
32+
* @group deploy
33+
*/
34+
class DeployTest extends TestCase
35+
{
36+
use CloudFunctionDeploymentTrait;
37+
38+
private static $entryPoint = 'helloBuildExtension';
39+
40+
public function testFunction(): void
41+
{
42+
// Send a request to the function.
43+
$resp = $this->client->get('', [
44+
// Uncomment and CURLOPT_VERBOSE debug content will be sent to stdout.
45+
// 'debug' => true
46+
]);
47+
48+
// Assert status code.
49+
$this->assertEquals('200', $resp->getStatusCode());
50+
51+
// Assert function output.
52+
$output = trim((string) $resp->getBody());
53+
// Failures often lead to a large HTML page in the response body.
54+
$this->assertEquals(
55+
'Hello World! (from my_custom_extension.so)',
56+
$output
57+
);
58+
}
59+
}
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
2+
/**
3+
* Copyright 2021 Google LLC.
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
declare(strict_types=1);
18+
19+
namespace Google\Cloud\Samples\Functions\ConceptsBuildExtension\Test;
20+
21+
use PHPUnit\Framework\TestCase;
22+
use Google\Cloud\TestUtils\CloudFunctionLocalTestTrait;
23+
use Google\Cloud\TestUtils\GcloudWrapper\CloudFunction;
24+
use Google\Cloud\Utils\ExponentialBackoff;
25+
use Symfony\Component\Process\Process;
26+
use Symfony\Component\Process\PhpExecutableFinder;
27+
28+
/**
29+
* Class SystemTest.
30+
*/
31+
class SystemTest extends TestCase
32+
{
33+
use CloudFunctionLocalTestTrait;
34+
35+
private static $entryPoint = 'helloBuildExtension';
36+
37+
public function testFunction(): void
38+
{
39+
// Send a request to the function.
40+
$resp = $this->client->get('/');
41+
42+
// Assert status code.
43+
$this->assertEquals('200', $resp->getStatusCode());
44+
45+
// Assert function output.
46+
$output = trim((string) $resp->getBody());
47+
$this->assertEquals(
48+
'Hello World! (from my_custom_extension.so)',
49+
$output
50+
);
51+
}
52+
53+
/**
54+
* Start the development server based on the prepared function.
55+
*
56+
* We override this to run the "gcp-build" step and to enable the built
57+
* extension when running the PHP build-in-web server.
58+
*/
59+
private static function doRun()
60+
{
61+
// Build the extension
62+
$process = new Process(['composer', 'run-script', 'gcp-build']);
63+
$process->start();
64+
65+
$backoff = new ExponentialBackoff($retries = 10);
66+
$backoff->execute(function () use ($process) {
67+
if ($process->isRunning()) {
68+
throw new \Exception('waiting for "gcp-build" step to complete');
69+
}
70+
});
71+
72+
$phpBin = (new PhpExecutableFinder())->find();
73+
$phpBin .= ' -d extension=\'./my_custom_extension.so\'';
74+
return self::$fn->run([], CloudFunction::DEFAULT_PORT, $phpBin);
75+
}
76+
}

0 commit comments

Comments
 (0)