Skip to content

Commit 63ee492

Browse files
committed
handle hooks
Signed-off-by: Robert Landers <landers.robert@gmail.com>
1 parent 623d406 commit 63ee492

File tree

8 files changed

+200
-119
lines changed

8 files changed

+200
-119
lines changed

.idea/php.xml

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

cli/lib/api.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -560,7 +560,7 @@ func Startup(ctx context.Context, js jetstream.JetStream, logger *zap.Logger, po
560560
logger.Debug("Got change!")
561561
status, err := extractStatus(update.Value())
562562
if err != nil {
563-
http.Error(writer, "Internal Server Error", http.StatusInternalServerError)
563+
http.Error(writer, "\"Internal Server Error\"", http.StatusInternalServerError)
564564
return
565565
}
566566
if runtimeStatus, ok := status.(map[string]interface{})["runtimeStatus"].(string); ok {
@@ -620,7 +620,7 @@ func Startup(ctx context.Context, js jetstream.JetStream, logger *zap.Logger, po
620620
})
621621

622622
r.HandleFunc("/", func(writer http.ResponseWriter, request *http.Request) {
623-
logger.Warn("Unkown endpoint")
623+
logger.Warn("Unknown endpoint")
624624
ctx := getCorrelationId(ctx, &request.Header, nil)
625625
logRequest(logger, request, ctx)
626626
})

src/Proxy/ClientProxy.php

Lines changed: 50 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
<?php
2+
23
/*
34
* Copyright ©2023 Robert Landers
45
*
@@ -23,14 +24,24 @@
2324

2425
namespace Bottledcode\DurablePhp\Proxy;
2526

27+
use ReflectionClass;
28+
use ReflectionMethod;
29+
use ReflectionParameter;
30+
2631
class ClientProxy extends Generator
2732
{
28-
protected function pureMethod(\ReflectionMethod $method): string
33+
protected function pureMethod(ReflectionMethod $method, bool $isHook = false): string
2934
{
30-
$name = $method->getName();
35+
if ($isHook) {
36+
$name = explode('::', $method->getName())[0];
37+
$name = str_replace('$', '', $name);
38+
$name = ucfirst($name);
39+
} else {
40+
$name = $method->getName();
41+
}
3142
$params = $method->getParameters();
3243
$params = array_map(
33-
function (\ReflectionParameter $param) {
44+
function (ReflectionParameter $param) {
3445
$type = $param->getType();
3546
if ($type !== null) {
3647
$type = $this->getTypes($type);
@@ -44,29 +55,41 @@ function (\ReflectionParameter $param) {
4455
$return = $method->getReturnType();
4556
$return = $return ? ": {$this->getTypes($return)}" : '';
4657

58+
if ($isHook) {
59+
return <<<EOT
60+
set {
61+
\$this->source->__set{$name}(\$value);
62+
}
63+
EOT;
64+
}
65+
4766
return <<<EOT
48-
public function {$name}({$params}){$return} {
49-
return \$this->source->{$name}(...func_get_args());
50-
}
51-
EOT;
67+
public function {$name}({$params}){$return} {
68+
return \$this->source->{$name}(...func_get_args());
69+
}
70+
EOT;
5271
}
5372

54-
protected function getName(\ReflectionClass $class): string
73+
protected function getName(ReflectionClass $class): string
5574
{
5675
return "__ClientProxy_{$class->getShortName()}";
5776
}
5877

59-
protected function impureSignal(\ReflectionMethod $method): string
78+
protected function impureSignal(ReflectionMethod $method): string
6079
{
6180
return $this->impureCall($method);
6281
}
6382

64-
protected function impureCall(\ReflectionMethod $method): string
83+
protected function impureCall(ReflectionMethod $method, bool $isHook = false): string
6584
{
66-
$name = $method->getName();
85+
if ($isHook) {
86+
$name = 'get';
87+
} else {
88+
$name = $method->getName();
89+
}
6790
$params = $method->getParameters();
6891
$params = array_map(
69-
function (\ReflectionParameter $param) {
92+
function (ReflectionParameter $param) {
7093
$type = $param->getType();
7194
if ($type !== null) {
7295
$type = $this->getTypes($type);
@@ -80,17 +103,25 @@ function (\ReflectionParameter $param) {
80103
$return = $method->getReturnType();
81104
$return = $return ? ": {$this->getTypes($return)}" : '';
82105

106+
if ($isHook) {
107+
return <<<EOT
108+
{$name} {
109+
throw new Bottledcode\DurablePhp\Proxy\ImpureException();
110+
}
111+
EOT;
112+
}
113+
83114
return <<<EOT
84-
public function {$name}({$params}){$return} {
85-
throw new Bottledcode\DurablePhp\Proxy\ImpureException();
86-
}
87-
EOT;
115+
public function {$name}({$params}){$return} {
116+
throw new Bottledcode\DurablePhp\Proxy\ImpureException();
117+
}
118+
EOT;
88119
}
89120

90-
protected function preamble(\ReflectionClass $class): string
121+
protected function preamble(ReflectionClass $class): string
91122
{
92123
return <<<'EOT'
93-
public function __construct(private mixed $source) {}
94-
EOT;
124+
public function __construct(private mixed $source) {}
125+
EOT;
95126
}
96127
}

src/Proxy/Generator.php

Lines changed: 51 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
<?php
2+
23
/*
34
* Copyright ©2024 Robert Landers
45
*
@@ -23,6 +24,12 @@
2324

2425
namespace Bottledcode\DurablePhp\Proxy;
2526

27+
use PropertyHookType;
28+
use ReflectionClass;
29+
use ReflectionIntersectionType;
30+
use ReflectionMethod;
31+
use ReflectionNamedType;
32+
use ReflectionProperty;
2633
use ReflectionUnionType;
2734

2835
abstract class Generator
@@ -31,7 +38,7 @@ public function __construct(protected string|null $cacheDir = null) {}
3138

3239
public function define(string $interface): string
3340
{
34-
$name = $this->getName($class = new \ReflectionClass($interface));
41+
$name = $this->getName($class = new ReflectionClass($interface));
3542
$namespace = $this->getInterfaceNamespace($class);
3643
$cacheFile = null;
3744
if ($this->cacheDir) {
@@ -42,7 +49,7 @@ public function define(string $interface): string
4249
}
4350
}
4451

45-
$reflection = new \ReflectionClass($interface);
52+
$reflection = new ReflectionClass($interface);
4653
$fullname = $this->getInterfaceNamespace($reflection) . '\\' . $this->getName($reflection);
4754

4855
if (!class_exists($fullname)) {
@@ -55,21 +62,21 @@ public function define(string $interface): string
5562
return '\\' . $namespace . '\\' . $name;
5663
}
5764

58-
protected function getInterfaceNamespace(\ReflectionClass $class): string
65+
abstract protected function getName(ReflectionClass $class): string;
66+
67+
protected function getInterfaceNamespace(ReflectionClass $class): string
5968
{
6069
return $class->getNamespaceName();
6170
}
6271

63-
abstract protected function getName(\ReflectionClass $class): string;
64-
6572
public function generate(string $interface): string
6673
{
67-
$reflection = new \ReflectionClass($interface);
74+
$reflection = new ReflectionClass($interface);
6875
$methods = $reflection->getMethods();
6976
$namespace = $reflection->getNamespaceName();
7077
$className = $reflection->getShortName();
7178
$methods = array_map(
72-
function (\ReflectionMethod $method) {
79+
function (ReflectionMethod $method) {
7380
if ($method->getAttributes(Pure::class)) {
7481
return $this->pureMethod($method);
7582
}
@@ -79,7 +86,7 @@ function (\ReflectionMethod $method) {
7986
}
8087

8188
$return = $method->getReturnType();
82-
if (($return instanceof \ReflectionNamedType) && $return->getName() === 'void') {
89+
if (($return instanceof ReflectionNamedType) && $return->getName() === 'void') {
8390
return $this->impureSignal($method);
8491
}
8592

@@ -89,29 +96,46 @@ function (\ReflectionMethod $method) {
8996
);
9097
$methods = implode("\n", $methods);
9198
$namespace = $namespace ? "namespace {$namespace};" : '';
92-
99+
$props = $reflection->getProperties(ReflectionProperty::IS_PUBLIC);
100+
$props = array_map(function (ReflectionProperty $prop) {
101+
$hooks = ['public ' . $this->getTypes($prop->getType()) . ' $' . $prop->getName() . '{'];
102+
if ($hook = $prop->getHook(PropertyHookType::Get)) {
103+
$hooks[] = $this->impureCall($hook, true);
104+
}
105+
if ($hook = $prop->getHook(PropertyHookType::Set)) {
106+
$hooks[] = $this->pureMethod($hook, true);
107+
}
108+
$hooks[] = '}';
109+
return implode(
110+
"\n",
111+
array_filter(
112+
$hooks,
113+
fn($hook) => $hook !== '',
114+
),
115+
);
116+
}, $props);
117+
$props = implode("\n", $props);
93118
return <<<EOT
94-
{$namespace}
95-
96-
class {$this->getName($reflection)} implements {$className} {
97-
{$this->preamble($reflection)}
98-
{$methods}
99-
}
100-
EOT;
119+
{$namespace}
120+
121+
class {$this->getName($reflection)} implements {$className} {
122+
{$this->preamble($reflection)}
123+
{$props}
124+
{$methods}
125+
}
126+
EOT;
101127
}
102128

103-
abstract protected function pureMethod(\ReflectionMethod $method): string;
129+
abstract protected function pureMethod(ReflectionMethod $method, bool $isHook = false): string;
104130

105-
abstract protected function impureSignal(\ReflectionMethod $method): string;
131+
abstract protected function impureSignal(ReflectionMethod $method): string;
106132

107-
abstract protected function impureCall(\ReflectionMethod $method): string;
133+
abstract protected function impureCall(ReflectionMethod $method, bool $isHook = false): string;
108134

109-
abstract protected function preamble(\ReflectionClass $class): string;
110-
111-
protected function getTypes(\ReflectionNamedType|ReflectionUnionType|\ReflectionIntersectionType|null $type): string
135+
protected function getTypes(ReflectionNamedType|ReflectionUnionType|ReflectionIntersectionType|null $type): string
112136
{
113-
if ($type instanceof \ReflectionNamedType) {
114-
if($type->isBuiltin()) {
137+
if ($type instanceof ReflectionNamedType) {
138+
if ($type->isBuiltin()) {
115139
return $type->getName();
116140
}
117141
return '\\' . $type->getName();
@@ -121,10 +145,12 @@ protected function getTypes(\ReflectionNamedType|ReflectionUnionType|\Reflection
121145
return implode('|', array_map($this->getTypes(...), $type->getTypes()));
122146
}
123147

124-
if ($type instanceof \ReflectionIntersectionType) {
148+
if ($type instanceof ReflectionIntersectionType) {
125149
return implode('&', array_map($this->getTypes(...), $type->getTypes()));
126150
}
127151

128152
return '';
129153
}
154+
155+
abstract protected function preamble(ReflectionClass $class): string;
130156
}

src/Proxy/OrchestratorProxy.php

Lines changed: 32 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
<?php
2+
23
/*
34
* Copyright ©2024 Robert Landers
45
*
@@ -29,14 +30,22 @@
2930

3031
class OrchestratorProxy extends Generator
3132
{
32-
protected function pureMethod(ReflectionMethod $method): string
33+
protected function pureMethod(ReflectionMethod $method, bool $isHook = false): string
3334
{
34-
return $this->impureCall($method);
35+
return $this->impureCall($method, $isHook);
3536
}
3637

37-
protected function impureCall(ReflectionMethod $method): string
38+
protected function impureCall(ReflectionMethod $method, bool $isHook = false): string
3839
{
39-
$name = $method->getName();
40+
$getHook = 'return ';
41+
if ($isHook && str_ends_with($method->getName(), 'get')) {
42+
$name = 'get';
43+
} elseif ($isHook && str_ends_with($method->getName(), 'set')) {
44+
$name = 'set';
45+
$getHook = '';
46+
} else {
47+
$name = $method->getName();
48+
}
4049
$params = $method->getParameters();
4150
$params = array_map(
4251
function (ReflectionParameter $param) {
@@ -53,11 +62,20 @@ function (ReflectionParameter $param) {
5362
$return = $method->getReturnType();
5463
$return = $return ? ": {$this->getTypes($return)}" : '';
5564

65+
if ($isHook) {
66+
$hookName = $method->getName();
67+
return <<<EOT
68+
{$name} {
69+
{$getHook}\$this->context->waitOne(\$this->context->callEntity(\$this->id, "{$hookName}", func_get_args()));
70+
}
71+
EOT;
72+
}
73+
5674
return <<<EOT
57-
public function {$name}({$params}){$return} {
58-
return \$this->context->waitOne(\$this->context->callEntity(\$this->id, "{$method->getName()}", func_get_args()));
59-
}
60-
EOT;
75+
public function {$name}({$params}){$return} {
76+
return \$this->context->waitOne(\$this->context->callEntity(\$this->id, "{$method->getName()}", func_get_args()));
77+
}
78+
EOT;
6179
}
6280

6381
protected function getName(ReflectionClass $class): string
@@ -85,16 +103,16 @@ function (ReflectionParameter $param) {
85103
$return = $return ? ": {$this->getTypes($return)}" : '';
86104

87105
return <<<EOT
88-
public function {$name}({$params}){$return} {
89-
\$this->context->signalEntity(\$this->id, "{$method->getName()}", func_get_args());
90-
}
91-
EOT;
106+
public function {$name}({$params}){$return} {
107+
\$this->context->signalEntity(\$this->id, "{$method->getName()}", func_get_args());
108+
}
109+
EOT;
92110
}
93111

94112
protected function preamble(ReflectionClass $class): string
95113
{
96114
return <<<EOT
97-
public function __construct(private \Bottledcode\DurablePhp\OrchestrationContextInterface \$context, private \Bottledcode\DurablePhp\State\EntityId \$id) {}
98-
EOT;
115+
public function __construct(private \Bottledcode\DurablePhp\OrchestrationContextInterface \$context, private \Bottledcode\DurablePhp\State\EntityId \$id) {}
116+
EOT;
99117
}
100118
}

0 commit comments

Comments
 (0)