1: <?php declare( strict_types = 1 );
2:
3: namespace Waves\Transactions\Invocation;
4:
5: use Exception;
6: use Waves\Common\ExceptionCode;
7: use Waves\Common\Json;
8: use Waves\Common\Value;
9:
10: class FunctionCall
11: {
12: const DEFAULT_NAME = 'default';
13:
14: private string $name;
15: /**
16: * @var array<int, Arg>
17: */
18: private array $args;
19:
20: /**
21: * @param string|null $name
22: * @param array<int, Arg>|null $args
23: * @return FunctionCall
24: */
25: static function as( string $name = null, array $args = null ): FunctionCall
26: {
27: $func = new FunctionCall;
28: $func->name = $name ?? FunctionCall::DEFAULT_NAME;
29: $func->args = $args ?? [];
30: return $func;
31: }
32:
33: static function fromJson( Json $json ): FunctionCall
34: {
35: $name = $json->getOr( 'function', FunctionCall::DEFAULT_NAME )->asString();
36: $args = [];
37: foreach( $json->get( 'args' )->asArray() as $arg )
38: $args[] = Arg::fromJson( Value::as( $arg )->asJson() );
39: return FunctionCall::as( $name, $args );
40: }
41:
42: function name(): string
43: {
44: return $this->name;
45: }
46:
47: function isDefault(): bool
48: {
49: return $this->name == FunctionCall::DEFAULT_NAME;
50: }
51:
52: /**
53: * @return array<int, Arg>
54: */
55: function args(): array
56: {
57: return $this->args;
58: }
59:
60: /**
61: * @return array<string, mixed>
62: */
63: function toJsonValue(): array
64: {
65: $args = [];
66: foreach( $this->args() as $arg )
67: $args[] = $arg->toJsonValue();
68: return
69: [
70: 'function' => $this->name(),
71: 'args' => $args,
72: ];
73: }
74:
75: /**
76: * @param array<mixed, mixed> $args
77: * @return string
78: */
79: static function argsBodyBytes( array $args ): string
80: {
81: $bytes = pack( 'N', count( $args ) );
82: foreach( $args as $arg )
83: {
84: if( !( $arg instanceof Arg ) )
85: throw new Exception( __FUNCTION__ . ' failed to detect Arg class', ExceptionCode::UNEXPECTED );
86: $value = $arg->value();
87: switch( $arg->type() )
88: {
89: case Arg::INTEGER:
90: $bytes .= chr( 0 ) . pack( 'J', $value->asInt() );
91: break;
92:
93: case Arg::BINARY:
94: $value = $value->asString();
95: $bytes .= chr( 1 ) . pack( 'N', strlen( $value ) ) . $value;
96: break;
97:
98: case Arg::STRING:
99: $value = $value->asString();
100: $bytes .= chr( 2 ) . pack( 'N', strlen( $value ) ) . $value;
101: break;
102:
103: case Arg::BOOLEAN:
104: $bytes .= chr( $value->asBoolean() ? 6 : 7 );
105: break;
106:
107: case Arg::LIST:
108: $bytes .= chr( 11 ) . FunctionCall::argsBodyBytes( $value->asArray() );
109: break;
110:
111: default:
112: throw new Exception( __FUNCTION__ . ' failed to detect type `' . serialize( $arg->type() ) . '`', ExceptionCode::UNKNOWN_TYPE );
113: }
114: }
115: return $bytes;
116: }
117:
118: function toBodyBytes(): string
119: {
120: if( $this->isDefault() )
121: return chr( 0 );
122:
123: $bytes = chr( 1 ) . chr( 9 ). chr( 1 );
124: $bytes .= pack( 'N', strlen( $this->name() ) ) . $this->name();
125: $bytes .= FunctionCall::argsBodyBytes( $this->args() );
126: return $bytes;
127: }
128: }
129: