Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add php #36

Merged
merged 11 commits into from
Jan 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions .github/workflows/package-php-sdk.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
name: Package PHP SDK
on:
push:
tags: ["flipt-php-**"]

permissions:
contents: write

jobs:
build:
uses: ./.github/workflows/package-sdks.yml
with:
sdk: php
secrets: inherit
3 changes: 2 additions & 1 deletion .release-please-manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@
"flipt-java": "0.0.0",
"flipt-node": "0.0.0",
"flipt-python": "0.0.0",
"flipt-rust": "0.0.0"
"flipt-rust": "0.0.0",
"flipt-php": "0.0.0"
}
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@ We are constantly growing our list of clients. Currently, we support the followi
2. [NodeJS](./flipt-node)
3. [Java](./flipt-java)
4. [Rust](./flipt-rust)
5. [PHP](./flipt-php)
6. [Go](https://github.com/flipt-io/flipt/tree/main/sdk/go)

> [!NOTE]
> The Go client is maintained in the main Flipt repository.

Want to see a client in a language we don't support? [Open an issue](https://github.com/flipt-io/flipt-server-sdks/issues/new?assignees=&labels=new-language&projects=&template=new_language.yml) and let us know!

Expand Down
21 changes: 21 additions & 0 deletions build/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ var (
"rust": rustBuild,
"node": nodeBuild,
"java": javaBuild,
"php": phpBuild,
}
)

Expand Down Expand Up @@ -205,3 +206,23 @@ func javaBuild(ctx context.Context, client *dagger.Client, hostDirectory *dagger

return err
}

func phpBuild(ctx context.Context, client *dagger.Client, hostDirectory *dagger.Directory) error {
container := client.Container().From("php:8-cli").
WithDirectory("/src", hostDirectory.Directory("flipt-php")).
WithEnvVariable("COMPOSER_ALLOW_SUPERUSER", "1").
WithExec([]string{"sh", "-c", "curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer"}).
WithWorkdir("/src").
WithExec([]string{"composer", "install"})

var err error

if !push {
_, err = container.Sync(ctx)
return err
}

// TODO

return err
}
7 changes: 6 additions & 1 deletion flipt-java/README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# Flipt Client Java
# Flipt Java

This directory contains the Java source code for the Flipt [server-side](https://www.flipt.io/docs/integration/server/rest) client.

## Documentation

API documentation is available at <https://www.flipt.io/docs/reference/overview>.
This directory contains the Java source code for the Java server side SDK.

## Installation
Expand Down
8 changes: 6 additions & 2 deletions flipt-node/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
# Flipt Client Node
# Flipt Node

This directory contains the TypeScript source code for the TypeScript server side SDK.
This directory contains the TypeScript source code for the Flipt [server-side](https://www.flipt.io/docs/integration/server/rest) client.

## Documentation

API documentation is available at <https://www.flipt.io/docs/reference/overview>.

## Installation

Expand Down
4 changes: 4 additions & 0 deletions flipt-php/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
vendor
.phpunit.result.cache
composer.lock
.vscode
33 changes: 33 additions & 0 deletions flipt-php/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Flipt PHP

[![Packagist Version](https://img.shields.io/packagist/v/flipt-io/flipt)](https://packagist.org/packages/flipt-io/flipt)
![beta](https://img.shields.io/badge/status-beta-yellow)

This directory contains the PHP source code for the Flipt [server-side](https://www.flipt.io/docs/integration/server/rest) client.

## Status

This SDK status is `beta`, and there may be breaking changes between versions without a major version update. Therefore, we recommend pinning your installation of this package wherever necessary.

## Requirements

- PHP 8.0 or higher
- [Composer](https://getcomposer.org/)

## Documentation

API documentation is available at <https://www.flipt.io/docs/reference/overview>.

## Installation

```Bash
composer install flipt-io/flipt
```

## Usage

TODO

## Thanks :tada:

Thanks to [legoheld](https://github.com/legoheld) for the initial implementation of this client.
42 changes: 42 additions & 0 deletions flipt-php/composer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
{
"name": "flipt-io/flipt",
"description": "Flipt Server SDK",
"keywords": [
"flipt",
"flipt php",
"feature flags",
"feature toggles"
],
"license": "MIT",
"homepage": "https://flipt.io",
"require": {
"php": ">=8.0",
"guzzlehttp/guzzle": "^7"
},
"repositories": [
{
"type": "vcs",
"url": "https://github.com/flipt-io/flipt-php"
}
],
"require-dev": {
"phpunit/php-code-coverage": "^10",
"phpunit/phpunit": "^10"
},
"autoload": {
"psr-4": {
"Flipt\\": "src/Flipt/"
}
},
"autoload-dev": {
"psr-4": {
"Flipt\\Tests\\": "tests/"
}
},
"config": {
"sort-packages": true
},
"scripts": {
"test": "./vendor/bin/phpunit tests"
}
}
11 changes: 11 additions & 0 deletions flipt-php/phpunit.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/|version|/phpunit.xsd"

>
<testsuites>
<testsuite name="integration">
<directory>tests</directory>
</testsuite>
</testsuites>
</phpunit>
189 changes: 189 additions & 0 deletions flipt-php/src/Flipt/Client/FliptClient.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
<?php

namespace Flipt\Client;

use GuzzleHttp\Client;
use Flipt\Models\BooleanEvaluationResult;
use Flipt\Models\VariantEvaluationResult;
use Flipt\Models\DefaultBooleanEvaluationResult;
use Flipt\Models\DefaultVariantEvaluationResult;


final class FliptClient
{
protected Client $client;
protected AuthenticationStrategy|null $authentication;
protected string $namespace;
protected string $entityId;
protected array $context;


public function __construct(string|Client $host, string $namespace = "default", array $context = [], string $entityId = '', AuthenticationStrategy $authentication = null)
{
$this->authentication = $authentication;
$this->namespace = $namespace;
$this->context = $context;
$this->entityId = $entityId;
$this->client = (is_string($host)) ? new Client(['base_uri' => $host]) : $host;
}


/**
* Returns the boolean evaluation result
*/
public function boolean(string $name, $context = [], $entityId = NULL): BooleanEvaluationResult
{
$response = $this->apiRequest('/evaluate/v1/boolean', $this->mergeRequestParams($name, $context, $entityId));
return new DefaultBooleanEvaluationResult($response['flagKey'], $response['enabled'], $response['reason'], $response['requestDurationMillis'], $response['requestId'], $response['timestamp']);
}



/**
* Returns the variant evaluation result
*/
public function variant(string $name, $context = [], $entityId = NULL): VariantEvaluationResult
{
$response = $this->apiRequest('/evaluate/v1/variant', $this->mergeRequestParams($name, $context, $entityId));
return new DefaultVariantEvaluationResult($response['flagKey'], $response['match'], $response['reason'], $response['requestDurationMillis'], $response['requestId'], $response['timestamp'], $response['segmentKeys'], $response['variantKey'], $response['variantAttachment']);
}


/**
* Batch return evaluation requests
*/
public function batch(array $names, $context = [], $entityId = NULL): array
{

$response = $this->apiRequest('/evaluate/v1/batch', [
'requests' => array_map(function ($name) use ($context, $entityId) {
return $this->mergeRequestParams($name, $context, $entityId);
}, $names)
]);


// map all responses to corresponding results
return array_map(function ($resp) {

if ($resp['type'] == 'VARIANT_EVALUATION_RESPONSE_TYPE') {
// get the variant response
$vr = $resp['variantResponse'];
return new DefaultVariantEvaluationResult($vr['flagKey'], $vr['match'], $vr['reason'], $vr['requestDurationMillis'], $vr['requestId'], $vr['timestamp'], $vr['segmentKeys'], $vr['variantKey'], $vr['variantAttachment']);
}

if ($resp['type'] == 'BOOLEAN_EVALUATION_RESPONSE_TYPE') {
// get the boolean response
$vr = $resp['booleanResponse'];
return new DefaultBooleanEvaluationResult($vr['flagKey'], $vr['enabled'], $vr['reason'], $vr['requestDurationMillis'], $vr['requestId'], $vr['timestamp']);
}

return null;
}, $response['responses']);
}


protected function mergeRequestParams(string $name, $context = [], $entityId = NULL)
{
return [
'context' => array_merge($this->context, $context),
'entityId' => isset($entityId) ? $entityId : $this->entityId,
'flagKey' => $name,
'namespaceKey' => $this->namespace,
];
}



/**
* Helper function to perform a guzzle request with the correct headers and body
*/
protected function apiRequest(string $path, array $body = [], string $method = 'POST')
{
// merge authentication headers
$headers = [
'Accept' => 'application/json',
];

if ($this->authentication) {
$headers = $this->authentication->authenticate($headers);
}

// execute request
$response = $this->client->request($method, $path, [
'headers' => $headers,
'body' => json_encode($body, JSON_FORCE_OBJECT),
]);

return json_decode($response->getBody(), true);
}


/**
* Create a new client with a different namespace
*/
public function withNamespace(string $namespace)
{
return new FliptClient($this->client, $namespace, $this->context, $this->entityId, $this->authentication);
}

/**
* Create a new client with a different context
*/
public function withContext(array $context)
{
return new FliptClient($this->client, $this->namespace, $context, $this->entityId, $this->authentication);
}

/**
* Create a new client with a different authentication strategy
*/
public function withAuthentication(AuthenticationStrategy $authentication)
{
return new FliptClient($this->client, $this->namespace, $this->context, $this->entityId, $authentication);
}
}

interface AuthenticationStrategy
{
public function authenticate(array $headers);
}

/**
* Authenticate with a client token
* @see https://www.flipt.io/docs/authentication/methods#static-token
*/
class ClientTokenAuthentication implements AuthenticationStrategy
{
protected string $token;

public function __construct(string $token)
{
$this->token = $token;
}

public function authenticate(array $headers)
{
$headers['Authorization'] = 'Bearer ' . $this->token;
return $headers;
}
}

/**
* Authenticate with a JWT token
* @see https://www.flipt.io/docs/authentication/methods#json-web-tokens
*/
class JWTAuthentication implements AuthenticationStrategy
{
protected string $token;

public function __construct(string $token)
{
$this->token = $token;
}

public function authenticate(array $headers)
{
$headers['Authorization'] = 'JWT ' . $this->token;
return $headers;
}
}
15 changes: 15 additions & 0 deletions flipt-php/src/Flipt/Models/BooleanEvaluationResult.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?php

declare(strict_types=1);

namespace Flipt\Models;

interface BooleanEvaluationResult
{
public function getFlagKey(): string;
public function getEnabled(): bool;
public function getReason(): string;
public function getRequestDurationMillis(): float;
public function getRequestId(): string;
public function getTimestamp(): string;
}
Loading
Loading