Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: GoogleCloudPlatform/functions-framework-php
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: v0.7.2
Choose a base ref
...
head repository: GoogleCloudPlatform/functions-framework-php
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: v0.8.0
Choose a head ref

Commits on Apr 26, 2021

  1. chore(docs): remove disclaimer about production use (#80)

    The functions framework is ready for production use.
    mtraver authored Apr 26, 2021
    Copy the full SHA
    255bcce View commit details
  2. Copy the full SHA
    99dddc5 View commit details
  3. Copy the full SHA
    9f35e19 View commit details
  4. Copy the full SHA
    4d062a4 View commit details
  5. Copy the full SHA
    796d86c View commit details

Commits on Apr 27, 2021

  1. chore(tests): better use of assertions (#85)

    Lucas Michot authored Apr 27, 2021
    Copy the full SHA
    3c506d2 View commit details
  2. Copy the full SHA
    a174657 View commit details
  3. Copy the full SHA
    bec3373 View commit details

Commits on Apr 29, 2021

  1. Copy the full SHA
    c319280 View commit details

Commits on May 19, 2021

  1. Copy the full SHA
    8092108 View commit details

Commits on Jun 7, 2021

  1. Copy the full SHA
    a195d00 View commit details
  2. Copy the full SHA
    27e24be View commit details

Commits on Jun 8, 2021

  1. Copy the full SHA
    d0d478b View commit details
  2. Copy the full SHA
    c4163bd View commit details
  3. Copy the full SHA
    d31f2a9 View commit details

Commits on Jul 12, 2021

  1. Copy the full SHA
    56a7fe2 View commit details
  2. Copy the full SHA
    0bffea3 View commit details
  3. Copy the full SHA
    889bc48 View commit details
  4. Copy the full SHA
    9bdf156 View commit details

Commits on Aug 23, 2021

  1. Copy the full SHA
    e5ed9bd View commit details

Commits on Aug 25, 2021

  1. fix: Firebase RTDB event types have ".ref" not ".document" (#107)

    * fix: Firebase RTDB event types have ".ref" not ".document"
    
    See GoogleCloudPlatform/functions-framework-conformance#79
    
    * Update unit tests
    MontyCarter authored Aug 25, 2021
    Copy the full SHA
    7f52b55 View commit details

Commits on Aug 27, 2021

  1. Copy the full SHA
    c8930e7 View commit details
6 changes: 3 additions & 3 deletions .github/workflows/conformance.yml
Original file line number Diff line number Diff line change
@@ -12,7 +12,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
php-version: [7.2, 7.3, 7.4]
php-version: [ '7.2', '7.3', '7.4','8.0' ]
name: PHP ${{ matrix.php-version }} Conformance Test
steps:
- name: Checkout code
@@ -36,7 +36,7 @@ jobs:
command: composer install

- name: Run HTTP conformance tests
uses: GoogleCloudPlatform/functions-framework-conformance/action@v0.3.7
uses: GoogleCloudPlatform/functions-framework-conformance/action@v0.3.12
env:
FUNCTION_TARGET: 'httpFunc'
FUNCTION_SIGNATURE_TYPE: 'http'
@@ -47,7 +47,7 @@ jobs:
cmd: "'php -S localhost:8080 router.php'"

- name: Run CloudEvent conformance tests
uses: GoogleCloudPlatform/functions-framework-conformance/action@v0.3.7
uses: GoogleCloudPlatform/functions-framework-conformance/action@v0.3.12
env:
FUNCTION_TARGET: 'cloudEventFunc'
FUNCTION_SIGNATURE_TYPE: 'cloudevent'
2 changes: 1 addition & 1 deletion .github/workflows/lint.yaml
Original file line number Diff line number Diff line change
@@ -12,4 +12,4 @@ jobs:
- name: PHP-CS-Fixer
uses: docker://oskarstark/php-cs-fixer-ga
with:
args: --diff --dry-run
args: . --diff --dry-run
2 changes: 1 addition & 1 deletion .github/workflows/unit.yaml
Original file line number Diff line number Diff line change
@@ -10,7 +10,7 @@ jobs:
strategy:
matrix:
operating-system: [ubuntu-latest]
php-versions: ['7.2', '7.3', '7.4']
php-versions: [ '7.2', '7.3', '7.4','8.0' ]
name: PHP ${{ matrix.php-versions }} Unit Test
steps:
- name: Checkout
3 changes: 2 additions & 1 deletion .php_cs.dist → .php_cs.dist.php
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
<?php

return PhpCsFixer\Config::create()
return (new PhpCsFixer\Config())
->setRules([
'@PSR2' => true,
'@PHP71Migration' => true,
'concat_space' => ['spacing' => 'one'],
'no_unused_imports' => true,
'method_argument_space' => false,
42 changes: 32 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
**DISCLAIMER: This repository is in development and not meant for production use**

# Functions Framework for PHP [![Build Status](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Factions-badge.atrox.dev%2FGoogleCloudPlatform%2Ffunctions-framework-php%2Fbadge&style=flat)](https://actions-badge.atrox.dev/GoogleCloudPlatform/functions-framework-php/goto) [![Packagist](https://poser.pugx.org/google/cloud-functions-framework/v/stable)](https://packagist.org/packages/google/cloud-functions-framework)

An open source FaaS (Function as a service) framework for writing portable
@@ -112,17 +110,15 @@ curl localhost:8080

## Run your function on Google Cloud Functions

**NOTE**: For an extensive list of samples, see the [PHP functions samples][functions-samples]
and the [official how-to guides][functions-how-to].

Follow the steps below to deploy to Google Cloud Functions. More information
on function deployment is available in the
[GCF documentation](https://cloud.google.com/functions/docs/deploying).

To run your function on Cloud Functions, first you must have the [gcloud SDK][gcloud] installed and [authenticated][gcloud-auth].

> **Note:** PHP support on Cloud Functions is currently in limited alpha.
> It is not yet suitable for production workloads, and support is best-effort
> only. Access is currently limited to selected early-access users.
> To request access please fill out [this form][gcf-early-access-form].
Make sure your source file (which defines your function) is called
`index.php`. The Functions Framework lets you choose a function source file,
but Cloud Functions currently uses the default of `index.php`.
@@ -155,8 +151,6 @@ for `gcloud functions deploy`.
To update your deployment, just redeploy using the same function **name**.
Configuration flags are not required.

[gcf-early-access-form]: https://docs.google.com/forms/d/e/1FAIpQLSc3-nfJEPpFk1XHy5FsQJ6c709bto9uhdgnnTX5VLbOvpq9yw/viewform?usp=sf_link

## Run your function in Cloud Run

To run your function in Cloud Run, first you must have the [gcloud SDK][gcloud] installed and [authenticated][gcloud-auth].
@@ -202,6 +196,8 @@ gcloud run deploy my-cloud-function \
After your instance deploys, you can access it at the URL provided, or view it
in the [Cloud Console][cloud-run-console].

[functions-samples]: https://github.com/GoogleCloudPlatform/php-docs-samples/tree/master/functions
[functions-how-to]: https://cloud.google.com/functions/docs/how-to
[gcloud]: https://cloud.google.com/sdk/gcloud/
[gcloud-auth]: https://cloud.google.com/sdk/docs/authorizing
[gcp-project]: https://cloud.google.com/resource-manager/docs/creating-managing-projects
@@ -290,14 +286,15 @@ and headers.
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Message\ResponseInterface;
use GuzzleHttp\Psr7\Response;
use GuzzleHttp\Psr7\Utils;

function helloHttp(ServerRequestInterface $request): ResponseInterface
{
$body = sprintf("Hello %s from PHP HTTP function!" . PHP_EOL,
$request->getQueryParams()['name'] ?? 'World');

return (new Response())
->withBody(GuzzleHttp\Psr7\stream_for($body))
->withBody(Utils::streamFor($body))
->withStatus(418) // I'm a teapot
->withHeader('Foo', 'Bar');
}
@@ -319,6 +316,31 @@ with the request and response objects.

[psr7]: https://www.php-fig.org/psr/psr-7/

## Use Google Cloud Storage

When you require the `google/cloud-storage` package with composer, the functions
framework will register the `gs://` stream wrapper. This enables your function
to read and write to Google Cloud Storage as you would any filesystem:

```php
// Get the contents of an object in GCS
$object = file_get_contents('gs://{YOUR_BUCKET_NAME}/object.txt');
// Make modifications
$object .= "\nadd a line";
// Write the new contents back to GCS
file_put_contents('gs://{YOUR_BUCKET_NAME}/object.txt', $object);
```

You can unregister this at any time by using
[`stream_wrapper_unregister`][stream_wrapper_unregister]:

```php
// unregister the automatically registered one
stream_wrapper_unregister('gs');
```

[stream_wrapper_unregister]: https://www.php.net/manual/en/function.stream-wrapper-unregister.php

## Run your function on Knative

Cloud Run and Cloud Run on GKE both implement the
7 changes: 5 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
@@ -4,12 +4,15 @@
"license": "Apache-2.0",
"require": {
"php": ">=7.2",
"guzzlehttp/psr7": "^1.6",
"guzzlehttp/psr7": "^1.7|^2.0",
"psr/http-message": "^1.0"
},
"suggest": {
"google/cloud-storage": "Google Cloud Storage client library for storing and persisting objects. When included, the functions framework will register the gs:// stream wrapper."
},
"autoload": {
"psr-4": {
"Google\\CloudFunctions\\": "src"
"Google\\CloudFunctions\\": "src/"
}
},
"require-dev": {
6 changes: 4 additions & 2 deletions examples/hello/index.php
Original file line number Diff line number Diff line change
@@ -25,8 +25,10 @@

function helloHttp(ServerRequestInterface $request)
{
return sprintf("Hello %s from PHP HTTP function!" . PHP_EOL,
$request->getQueryParams()['name'] ?? 'World');
return sprintf(
"Hello %s from PHP HTTP function!" . PHP_EOL,
$request->getQueryParams()['name'] ?? 'World'
);
}

/**
51 changes: 19 additions & 32 deletions router.php
Original file line number Diff line number Diff line change
@@ -17,45 +17,30 @@

use Google\CloudFunctions\Emitter;
use Google\CloudFunctions\Invoker;
use Google\CloudFunctions\ProjectContext;

/**
* Determine the autoload file to load.
*/
if (file_exists(__DIR__ . '/../../autoload.php')) {
// when running from vendor/google/cloud-functions-framework
require_once __DIR__ . '/../../autoload.php';
} elseif (file_exists(__DIR__ . '/vendor/autoload.php')) {
// when running from git clone.
require_once __DIR__ . '/vendor/autoload.php';
// ProjectContext finds the autoload file, so we must manually include it first
require_once __DIR__ . '/src/ProjectContext.php';

$projectContext = new ProjectContext();

if ($autoloadFile = $projectContext->locateAutoloadFile()) {
require_once $autoloadFile;
}

/**
* Determine the function source file to load
*/
// Ensure function source is loaded relative to the application root directory
$documentRoot = __DIR__ . '/../../../';
if ($functionSource = getenv('FUNCTION_SOURCE', true)) {
if (0 !== strpos($functionSource, '/')) {
// Make the path relative
$relativeSource = $documentRoot . $functionSource;
if (!file_exists($relativeSource)) {
throw new RuntimeException(sprintf(
'Unable to load function from "%s"',
getenv('FUNCTION_SOURCE', true)
));
}
require_once $relativeSource;
} else {
require_once $functionSource;
}
} elseif (file_exists($defaultSource = $documentRoot . 'index.php')) {
// When running from vendor/google/cloud-functions-framework, default to
// "index.php" in the root of the application.
require_once $defaultSource;
} else {
// Do nothing - assume the function source is being autoloaded.
$functionSourceEnv = getenv('FUNCTION_SOURCE', true);
if ($source = $projectContext->locateFunctionSource($functionSourceEnv)) {
require_once $source;
}

// Register the "gs://" stream wrapper for Cloud Storage if the package
// "google/cloud-storage" is installed and the "gs" protocol has not been
// registered
$projectContext->registerCloudStorageStreamWrapperIfPossible();

/**
* Invoke the function based on the function type.
*/
@@ -66,7 +51,9 @@
}
if (!is_callable($target)) {
throw new InvalidArgumentException(sprintf(
'Function target is not callable: "%s"', $target));
'Function target is not callable: "%s"',
$target
));
}

$signatureType = getenv('FUNCTION_SIGNATURE_TYPE', true) ?: 'http';
12 changes: 10 additions & 2 deletions src/Context.php
Original file line number Diff line number Diff line change
@@ -23,17 +23,20 @@ class Context
private $timestamp;
private $eventType;
private $resource;
private $domain;

public function __construct(
?string $eventId,
?string $timestamp,
?string $eventType,
?array $resource
?array $resource,
?string $domain
) {
$this->eventId = $eventId;
$this->timestamp = $timestamp;
$this->eventType = $eventType;
$this->resource = $resource;
$this->domain = $domain;
}

public function getEventId(): ?string
@@ -66,6 +69,11 @@ public function getResourceName(): ?string
return $this->resource['name'] ?? null;
}

public function getDomain(): ?string
{
return $this->domain;
}

public static function fromArray(array $arr)
{
// When "resource" is defined in the root (instead of in "context") it
@@ -75,7 +83,7 @@ public static function fromArray(array $arr)
}

$args = [];
$argKeys = ['eventId', 'timestamp', 'eventType', 'resource'];
$argKeys = ['eventId', 'timestamp', 'eventType', 'resource', 'domain'];
foreach ($argKeys as $key) {
$args[] = $arr[$key] ?? null;
}
10 changes: 6 additions & 4 deletions src/FunctionWrapper.php
Original file line number Diff line number Diff line change
@@ -27,7 +27,7 @@

abstract class FunctionWrapper
{
const FUNCTION_STATUS_HEADER = 'X-Google-Status';
public const FUNCTION_STATUS_HEADER = 'X-Google-Status';

protected $function;

@@ -69,8 +69,10 @@ private function validateFunctionSignature(
ReflectionFunctionAbstract $reflection
) {
$parameters = $reflection->getParameters();
$parametersCount = count($parameters);

// Check there is at least one parameter
if (count($parameters) < 1) {
if ($parametersCount === 0) {
$this->throwInvalidFunctionException();
}
// Check the first parameter has the proper typehint
@@ -80,8 +82,8 @@ private function validateFunctionSignature(
$this->throwInvalidFunctionException();
}

if (count($parameters) > 1) {
for ($i = 1; $i < count($parameters); $i++) {
if ($parametersCount > 1) {
for ($i = 1; $i < $parametersCount; $i++) {
if (!$parameters[$i]->isOptional()) {
throw new LogicException(
'If your function accepts more than one parameter the '
3 changes: 2 additions & 1 deletion src/HttpFunctionWrapper.php
Original file line number Diff line number Diff line change
@@ -17,6 +17,7 @@

namespace Google\CloudFunctions;

use LogicException;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use GuzzleHttp\Psr7\Response;
@@ -43,7 +44,7 @@ public function execute(ServerRequestInterface $request): ResponseInterface
return $response;
}

throw new \LogicException(
throw new LogicException(
'Function response must be string or ' . ResponseInterface::class
);
}
15 changes: 9 additions & 6 deletions src/Invoker.php
Original file line number Diff line number Diff line change
@@ -17,6 +17,7 @@

namespace Google\CloudFunctions;

use Exception;
use InvalidArgumentException;
use GuzzleHttp\Psr7\Response;
use GuzzleHttp\Psr7\ServerRequest;
@@ -29,9 +30,9 @@ class Invoker
private $errorLogFunc;

/**
* @param $target callable The callable to be invoked
* @param $signatureType The signature type of the target callable, either
* "event" or "http".
* @param callable $target The callable to be invoked
* @param string $signatureType The signature type of the target callable,
* either "event" or "http".
*/
public function __construct(callable $target, string $signatureType)
{
@@ -44,7 +45,9 @@ public function __construct(callable $target, string $signatureType)
$this->function = new CloudEventFunctionWrapper($target);
} else {
throw new InvalidArgumentException(sprintf(
'Invalid signature type: "%s"', $signatureType));
'Invalid signature type: "%s"',
$signatureType
));
}
$this->errorLogFunc = function (string $error) {
fwrite(fopen('php://stderr', 'wb'), json_encode([
@@ -56,14 +59,14 @@ public function __construct(callable $target, string $signatureType)

public function handle(
ServerRequestInterface $request = null
) : ResponseInterface {
): ResponseInterface {
if ($request === null) {
$request = ServerRequest::fromGlobals();
}

try {
return $this->function->execute($request);
} catch (\Exception $e) {
} catch (Exception $e) {
// Log the full error and stack trace
($this->errorLogFunc)((string) $e);
// Set "X-Google-Status" to "crash" for Http functions and "error"
Loading