Skip to content
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
6 changes: 5 additions & 1 deletion .dagger/lint.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,14 @@ func (m *Lint) Go() *dagger.Container {
}

func (m *Lint) Phpstan() *dagger.Container {
// Generate example files before running phpstan
source := m.Main.source().
WithDirectory("example/generated", m.Main.Generate().Example())

return dag.Phpstan(dagger.PhpstanOpts{
Version: phpstanVersion,
PhpVersion: defaultPhpVersion,
}).Analyse(m.Main.source())
}).Analyse(source)
}

func (m *Lint) PhpCsFixer() *dagger.Container {
Expand Down
7 changes: 7 additions & 0 deletions phpstan.neon.dist
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,12 @@ parameters:
# checkMissingIterableValueType: false
paths:
- lib/src
- example/
excludePaths:
analyse:
# These are generated by the protobuf php tooling, not us.
- example/generated/Twitch/Twirp/Example/Hat.php
- example/generated/Twitch/Twirp/Example/Size.php
- example/generated/GPBMetadata/Service.php
bootstrapFiles:
- lib/vendor/autoload.php
17 changes: 3 additions & 14 deletions protoc-gen-twirp_php/templates/global/TwirpError.php.tmpl
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
<?php
# Generated by the protocol buffer compiler. DO NOT EDIT! (protoc-gen-twirp_php {{ .Version }})

// Generated by the protocol buffer compiler. DO NOT EDIT! (protoc-gen-twirp_php {{ .Version }})

declare(strict_types=1);

namespace {{ .Namespace }};

use Twirp\ErrorCode;
use Twirp\Error;
use Twirp\ErrorCode;

/**
* Error class implementation for Twirp errors.
Expand All @@ -30,25 +31,16 @@ final class TwirpError extends \Exception implements Error
parent::__construct($message, $exCode, $previous);
}

/**
* {@inheritdoc}
*/
public function getErrorCode(): string
{
return $this->errorCode;
}

/**
* {@inheritdoc}
*/
public function setMeta(string $key, string $value): void
{
$this->meta[$key] = $value;
}

/**
* {@inheritdoc}
*/
public function getMeta(string $key): string
{
if (isset($this->meta[$key])) {
Expand All @@ -58,9 +50,6 @@ final class TwirpError extends \Exception implements Error
return '';
}

/**
* {@inheritdoc}
*/
public function getMetaMap(): array
{
return $this->meta;
Expand Down
52 changes: 44 additions & 8 deletions protoc-gen-twirp_php/templates/service/AbstractClient.php.tmpl
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<?php
# Generated by the protocol buffer compiler. DO NOT EDIT! (protoc-gen-twirp_php {{ .Version }})
# source: {{ .File.Proto.Name }}

// Generated by the protocol buffer compiler. DO NOT EDIT! (protoc-gen-twirp_php {{ .Version }})
// source: {{ .File.Proto.Name }}

declare(strict_types=1);

Expand Down Expand Up @@ -88,7 +89,7 @@ abstract class {{ .Service | phpServiceName .File }}AbstractClient

$url = $this->addr;
if (empty($this->prefix)) {
$url = $url.'/{{ $method | protoMethodFullName }}';
$url .= '/{{ $method | protoMethodFullName }}';
} else {
$url = $url.'/'.$this->prefix.'/{{ $method | protoMethodFullName }}';
}
Expand Down Expand Up @@ -165,7 +166,7 @@ abstract class {{ .Service | phpServiceName .File }}AbstractClient
return $this->twirpErrorFromIntermediary($statusCode, $msg, $location);
}

$body = (string)$resp->getBody();
$body = (string) $resp->getBody();

$rawError = json_decode($body, true);
if ($rawError === null) {
Expand All @@ -174,18 +175,53 @@ abstract class {{ .Service | phpServiceName .File }}AbstractClient
return $this->twirpErrorFromIntermediary($statusCode, $msg, $body);
}

$rawError = $rawError + ['code' => '', 'msg' => '', 'meta' => []];
if (!is_array($rawError)) {
$msg = 'invalid type returned from server error response body';

return TwirpError::newError(ErrorCode::Internal, $msg);
}

$rawError += ['code' => '', 'msg' => '', 'meta' => []];

if (!is_string($rawError['code'])) {
$msg = 'invalid type returned for error code.';

return TwirpError::newError(ErrorCode::Internal, $msg);
}

if (ErrorCode::isValid($rawError['code']) === false) {
$msg = 'invalid type returned from server error response: '.$rawError['code'];

return TwirpError::newError(ErrorCode::Internal, $msg);
}

if (!is_string($rawError['msg'])) {
$msg = 'invalid type returned for error message.';

return TwirpError::newError(ErrorCode::Internal, $msg);
}

$error = TwirpError::newError($rawError['code'], $rawError['msg']);

if (!is_array($rawError['meta'])) {
$msg = 'invalid type returned for error meta.';

return TwirpError::newError(ErrorCode::Internal, $msg);
}

foreach ($rawError['meta'] as $key => $value) {
$error->setMeta($key, $value);
if (!is_string($key)) {
$msg = 'invalid type returned for error meta key.';

return TwirpError::newError(ErrorCode::Internal, $msg);
}

if (!is_string($value)) {
$msg = 'invalid type returned for error meta value.';

return TwirpError::newError(ErrorCode::Internal, $msg);
}
$error->setMeta($key, $value);
}

return $error;
Expand Down Expand Up @@ -230,7 +266,7 @@ abstract class {{ .Service | phpServiceName .File }}AbstractClient

$error = TwirpError::newError($code, $msg);
$error->setMeta('http_error_from_intermediary', 'true');
$error->setMeta('status_code', (string)$status);
$error->setMeta('status_code', (string) $status);

if ($this->isHttpRedirect($status)) {
$error->setMeta('location', $bodyOrLocation);
Expand All @@ -248,7 +284,7 @@ abstract class {{ .Service | phpServiceName .File }}AbstractClient

protected function urlBase(string $addr): string
{
$scheme = parse_url($addr, PHP_URL_SCHEME);
$scheme = parse_url($addr, \PHP_URL_SCHEME);

// If parse_url fails, return the addr unchanged.
if ($scheme === false) {
Expand Down
10 changes: 4 additions & 6 deletions protoc-gen-twirp_php/templates/service/Client.php.tmpl
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<?php
# Generated by the protocol buffer compiler. DO NOT EDIT! (protoc-gen-twirp_php {{ .Version }})
# source: {{ .File.Proto.Name }}

// Generated by the protocol buffer compiler. DO NOT EDIT! (protoc-gen-twirp_php {{ .Version }})
// source: {{ .File.Proto.Name }}

declare(strict_types=1);

Expand All @@ -17,9 +18,6 @@ use Google\Protobuf\Internal\Message;
*/
final class {{ .Service | phpServiceName .File }}Client extends {{ .Service | phpServiceName .File }}AbstractClient implements {{ .Service | phpServiceName .File }}
{
/**
* @inheritDoc
*/
protected function doRequest(array $ctx, string $url, Message $in, Message $out): void
{
$body = $in->serializeToString();
Expand All @@ -37,7 +35,7 @@ final class {{ .Service | phpServiceName .File }}Client extends {{ .Service | ph
}

try {
$out->mergeFromString((string)$resp->getBody());
$out->mergeFromString((string) $resp->getBody());
} catch (GPBDecodeException $e) {
throw $this->clientError('failed to unmarshal proto response', $e);
}
Expand Down
10 changes: 4 additions & 6 deletions protoc-gen-twirp_php/templates/service/JsonClient.php.tmpl
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<?php
# Generated by the protocol buffer compiler. DO NOT EDIT! (protoc-gen-twirp_php {{ .Version }})
# source: {{ .File.Proto.Name }}

// Generated by the protocol buffer compiler. DO NOT EDIT! (protoc-gen-twirp_php {{ .Version }})
// source: {{ .File.Proto.Name }}

declare(strict_types=1);

Expand All @@ -17,9 +18,6 @@ use Google\Protobuf\Internal\Message;
*/
final class {{ .Service | phpServiceName .File }}JsonClient extends {{ .Service | phpServiceName .File }}AbstractClient implements {{ .Service | phpServiceName .File }}
{
/**
* @inheritDoc
*/
protected function doRequest(array $ctx, string $url, Message $in, Message $out): void
{
$body = $in->serializeToJsonString();
Expand All @@ -37,7 +35,7 @@ final class {{ .Service | phpServiceName .File }}JsonClient extends {{ .Service
}

try {
$out->mergeFromJsonString((string)$resp->getBody());
$out->mergeFromJsonString((string) $resp->getBody());
} catch (GPBDecodeException $e) {
throw $this->clientError('failed to unmarshal json response', $e);
}
Expand Down
47 changes: 16 additions & 31 deletions protoc-gen-twirp_php/templates/service/Server.php.tmpl
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<?php
# Generated by the protocol buffer compiler. DO NOT EDIT! (protoc-gen-twirp_php {{ .Version }})
# source: {{ .File.Proto.Name }}

// Generated by the protocol buffer compiler. DO NOT EDIT! (protoc-gen-twirp_php {{ .Version }})
// source: {{ .File.Proto.Name }}

declare(strict_types=1);

Expand All @@ -24,9 +25,7 @@ use Twirp\ServerWithPathPrefix;
*
* Generated from protobuf service <code>{{ .Service.Desc.FullName }}</code>
*/
final class {{ .Service | phpServiceName .File }}Server implements
RequestHandlerInterface,
ServerWithPathPrefix
final class {{ .Service | phpServiceName .File }}Server implements RequestHandlerInterface, ServerWithPathPrefix
{
/**
* A convenience constant that may identify URL paths.
Expand All @@ -38,20 +37,11 @@ final class {{ .Service | phpServiceName .File }}Server implements
*/
public const PATH_PREFIX = '/twirp/{{ .Service.Desc.FullName }}/';

/**
* @var ResponseFactoryInterface
*/
private $responseFactory;
private ResponseFactoryInterface $responseFactory;

/**
* @var StreamFactoryInterface
*/
private $streamFactory;
private StreamFactoryInterface $streamFactory;

/**
* @var {{ .Service | phpServiceName .File }}
*/
private $svc;
private {{ .Service | phpServiceName .File }} $svc;

/**
* @var ServerHooks
Expand Down Expand Up @@ -104,6 +94,7 @@ final class {{ .Service | phpServiceName .File }}Server implements
*/
public function handle(ServerRequestInterface $req): ResponseInterface
{
/** @var array<string, mixed> $ctx */
$ctx = $req->getAttributes();
$ctx = Context::withPackageName($ctx, '{{ .File.Proto.GetPackage }}');
$ctx = Context::withServiceName($ctx, '{{ .Service.Desc.Name }}');
Expand All @@ -120,7 +111,7 @@ final class {{ .Service | phpServiceName .File }}Server implements
return $this->writeError($ctx, $this->badRouteError($msg, $req->getMethod(), $req->getUri()->getPath()));
}

list($prefix, $service, $method) = $this->parsePath($req->getUri()->getPath());
[$prefix, $service, $method] = $this->parsePath($req->getUri()->getPath());

if ($service != '{{ .Service.Desc.FullName }}') {
return $this->writeError($ctx, $this->noRouteError($req));
Expand Down Expand Up @@ -174,6 +165,8 @@ final class {{ .Service | phpServiceName .File }}Server implements
return $this->writeError($ctx, $this->badRouteError($msg, $req->getMethod(), $req->getUri()->getPath()));
}

// This is set by reference, so phpstan can't infer the contents
/** @phpstan-ignore foreach.emptyArray */
foreach ($respHeaders as $key => $value) {
$resp = $resp->withHeader($key, $value);
}
Expand All @@ -192,14 +185,10 @@ final class {{ .Service | phpServiceName .File }}Server implements
$ctx = $this->hook->requestRouted($ctx);

$in = new {{ $inputType }}();
$in->mergeFromJsonString((string)$req->getBody(), true);
$in->mergeFromJsonString((string) $req->getBody(), true);

$out = $this->svc->{{ $method.Desc.Name }}($ctx, $in);

if ($out === null) {
return $this->writeError($ctx, TwirpError::newError(ErrorCode::Internal, 'received a null response while calling {{ $method.Desc.Name }}. null responses are not supported'));
}

$ctx = $this->hook->responsePrepared($ctx);
} catch (GPBDecodeException $e) {
return $this->writeError($ctx, TwirpError::newError(ErrorCode::Internal, 'failed to parse request json'));
Expand Down Expand Up @@ -232,14 +221,10 @@ final class {{ .Service | phpServiceName .File }}Server implements
$ctx = $this->hook->requestRouted($ctx);

$in = new {{ $inputType }}();
$in->mergeFromString((string)$req->getBody());
$in->mergeFromString((string) $req->getBody());

$out = $this->svc->{{ $method.Desc.Name }}($ctx, $in);

if ($out === null) {
return $this->writeError($ctx, TwirpError::newError(ErrorCode::Internal, 'received a null response while calling {{ $method.Desc.Name }}. null responses are not supported'));
}

$ctx = $this->hook->responsePrepared($ctx);
} catch (GPBDecodeException $e) {
return $this->writeError($ctx, TwirpError::newError(ErrorCode::Internal, 'failed to parse request proto'));
Expand Down Expand Up @@ -274,7 +259,7 @@ final class {{ .Service | phpServiceName .File }}Server implements
$parts = explode('/', $path);

if (count($parts) < 2) {
return ["", "", ""];
return ['', '', ''];
}

$method = $parts[count($parts) - 1];
Expand All @@ -300,7 +285,7 @@ final class {{ .Service | phpServiceName .File }}Server implements
private function badRouteError(string $msg, string $method, string $url): TwirpError
{
$e = TwirpError::newError(ErrorCode::BadRoute, $msg);
$e->setMeta('twirp_invalid_route', $method . ' ' . $url);
$e->setMeta('twirp_invalid_route', $method.' '.$url);

return $e;
}
Expand Down Expand Up @@ -356,7 +341,7 @@ final class {{ .Service | phpServiceName .File }}Server implements
$rawBody['meta'] = $e->getMetaMap();
}

$body = $this->streamFactory->createStream(json_encode($rawBody, \JSON_FORCE_OBJECT));
$body = $this->streamFactory->createStream(json_encode($rawBody, \JSON_FORCE_OBJECT | \JSON_THROW_ON_ERROR));

return $this->responseFactory
->createResponse($statusCode)
Expand Down
5 changes: 3 additions & 2 deletions protoc-gen-twirp_php/templates/service/_Service_.php.tmpl
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<?php
# Generated by the protocol buffer compiler. DO NOT EDIT! (protoc-gen-twirp_php {{ .Version }})
# source: {{ .File.Proto.Name }}

// Generated by the protocol buffer compiler. DO NOT EDIT! (protoc-gen-twirp_php {{ .Version }})
// source: {{ .File.Proto.Name }}

declare(strict_types=1);

Expand Down
Loading