diff --git a/.fern/metadata.json b/.fern/metadata.json
index dc2de208..19eebcb0 100644
--- a/.fern/metadata.json
+++ b/.fern/metadata.json
@@ -23,5 +23,5 @@
}
}
},
- "sdkVersion": "45.1.0.20260520"
+ "sdkVersion": "45.2.0.20260520"
}
\ No newline at end of file
diff --git a/.fernignore b/.fernignore
index 74dd6751..7a790668 100644
--- a/.fernignore
+++ b/.fernignore
@@ -6,6 +6,7 @@ phpstan.neon
src/Exceptions/SquareApiException.php
src/Legacy
src/Utils/WebhooksHelper.php
+src/Utils/ReportingHelper.php
tests/Integration
.fern/replay.lock
.fern/replay.yml
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 345e2f1d..bc588cbe 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -31,6 +31,7 @@ jobs:
runs-on: ubuntu-latest
env:
TEST_SQUARE_TOKEN: ${{ secrets.TEST_SQUARE_TOKEN }}
+ TEST_SQUARE_REPORTING: ${{ secrets.TEST_SQUARE_REPORTING }}
steps:
- name: Checkout repo
diff --git a/README.md b/README.md
index 4fdb3852..31b35304 100644
--- a/README.md
+++ b/README.md
@@ -210,6 +210,77 @@ $isValid = WebhooksHelper.verifySignature(
)
```
+## Reporting API
+
+The [Square Reporting API](https://developer.squareup.com/docs/reporting-api/overview) lets you
+query aggregated business data. Start by calling `getMetadata` to discover the schema — the cubes,
+views, measures, dimensions, and segments you can reference — then run a query with `load`:
+
+```php
+$metadata = $client->reporting->getMetadata();
+
+$response = $client->reporting->load(new Square\Reporting\Requests\LoadRequest([
+ 'query' => new Square\Types\Query([
+ 'measures' => ['Orders.count'],
+ ]),
+]));
+```
+
+### Polling for long-running queries
+
+The `load` endpoint is asynchronous. While a query is still being computed, the API responds with
+an HTTP `200` whose body is `{ "error": "Continue wait" }` instead of results, and the client is
+expected to re-send the identical request until the results are ready. The SDK provides
+`ReportingHelper::loadAndWait`, which owns that retry loop with exponential backoff:
+
+```php
+use Square\Utils\ReportingHelper;
+use Square\Reporting\Requests\LoadRequest;
+use Square\Types\Query;
+
+$response = ReportingHelper::loadAndWait(
+ $client,
+ new LoadRequest([
+ 'query' => new Query([
+ 'measures' => ['Orders.count'],
+ ]),
+ ]),
+);
+
+foreach ($response->getResults() as $result) {
+ // ...
+}
+```
+
+`loadAndWait` accepts an options array to tune the polling behavior:
+
+| Option | Default | Description |
+| --- | --- | --- |
+| `maxAttempts` | `20` | Maximum poll attempts before giving up. |
+| `initialDelayMs` | `2000` | Delay before the first retry, in milliseconds. |
+| `maxDelayMs` | `20000` | Upper bound on the backoff delay, in milliseconds. |
+| `backoffFactor` | `2` | Multiplier applied to the delay after each attempt. |
+| `shouldCancel` | `null` | A `callable(): bool` polled before each attempt (and during the wait); aborts the loop when it returns `true`. |
+| `requestOptions` | `null` | Per-request options forwarded to each underlying `reporting->load` call. |
+
+```php
+$response = ReportingHelper::loadAndWait(
+ $client,
+ new LoadRequest([/* ... */]),
+ [
+ 'maxAttempts' => 30,
+ 'initialDelayMs' => 1000,
+ 'shouldCancel' => fn (): bool => /* e.g. a deadline check */ false,
+ ],
+);
+```
+
+If the query does not resolve within `maxAttempts`, or if `shouldCancel` aborts it, a
+`Square\Exceptions\SquareException` is thrown.
+
+> **Note:** The Reporting API is available in **production only** and requires a
+> reporting-provisioned access token; it is not available in the sandbox environment.
+
## Legacy SDK
While the new SDK has a lot of improvements, we at Square understand that it takes time to upgrade when there are breaking changes.
diff --git a/composer.json b/composer.json
index 8846c865..31d6e3d3 100644
--- a/composer.json
+++ b/composer.json
@@ -1,6 +1,6 @@
{
"name": "square/square",
- "version": "45.1.0.20260520",
+ "version": "45.2.0.20260520",
"description": "Use Square APIs to manage and run business including payment, customer, product, inventory, and employee management.",
"keywords": [
"square",
diff --git a/reference.md b/reference.md
index cfbcd0aa..35f1d2b1 100644
--- a/reference.md
+++ b/reference.md
@@ -16007,6 +16007,118 @@ $client->vendors->update(
+
+
+
+
+## Reporting
+$client->reporting->getMetadata() -> MetadataResponse
+
+-
+
+#### 📝 Description
+
+
+-
+
+
+-
+
+Describes the data available to query: the cubes, views, measures, dimensions, and segments you can reference in a reporting query. Call this first to discover the schema, then pass the members you need to `load`.
+
+
+
+
+
+#### 🔌 Usage
+
+
+-
+
+
+-
+
+```php
+$client->reporting->getMetadata();
+```
+
+
+
+
+
+
+
+
+
+
+$client->reporting->load($request) -> LoadResponse
+
+-
+
+#### 📝 Description
+
+
+-
+
+
+-
+
+Runs a reporting query against the discovered schema and returns the aggregated results. Long-running queries may return a "Continue wait" response while processing — retry the same request until results are ready.
+
+
+
+
+
+#### 🔌 Usage
+
+
+-
+
+
+-
+
+```php
+$client->reporting->load(
+ new LoadRequest([]),
+);
+```
+
+
+
+
+
+#### ⚙️ Parameters
+
+
+-
+
+
+-
+
+**$queryType:** `?string`
+
+
+
+
+
+-
+
+**$cache:** `?string`
+
+
+
+
+
+-
+
+**$query:** `?Query`
+
+
+
+
+
+
+
diff --git a/src/Reporting/ReportingClient.php b/src/Reporting/ReportingClient.php
new file mode 100644
index 00000000..68e42416
--- /dev/null
+++ b/src/Reporting/ReportingClient.php
@@ -0,0 +1,143 @@
+,
+ * } $options @phpstan-ignore-next-line Property is used in endpoint methods via HttpEndpointGenerator
+ */
+ private array $options;
+
+ /**
+ * @var RawClient $client
+ */
+ private RawClient $client;
+
+ /**
+ * @param RawClient $client
+ * @param ?array{
+ * baseUrl?: string,
+ * client?: ClientInterface,
+ * maxRetries?: int,
+ * timeout?: float,
+ * headers?: array,
+ * } $options
+ */
+ public function __construct(
+ RawClient $client,
+ ?array $options = null,
+ ) {
+ $this->client = $client;
+ $this->options = $options ?? [];
+ }
+
+ /**
+ * Describes the data available to query: the cubes, views, measures, dimensions, and segments you can reference in a reporting query. Call this first to discover the schema, then pass the members you need to `load`.
+ *
+ * @param ?array{
+ * baseUrl?: string,
+ * maxRetries?: int,
+ * timeout?: float,
+ * headers?: array,
+ * queryParameters?: array,
+ * bodyProperties?: array,
+ * } $options
+ * @return MetadataResponse
+ * @throws SquareException
+ * @throws SquareApiException
+ */
+ public function getMetadata(?array $options = null): MetadataResponse
+ {
+ $options = array_merge($this->options, $options ?? []);
+ try {
+ $response = $this->client->sendRequest(
+ new JsonApiRequest(
+ baseUrl: $options['baseUrl'] ?? $this->client->options['baseUrl'] ?? Environments::Production->value,
+ path: "reporting/v1/meta",
+ method: HttpMethod::GET,
+ ),
+ $options,
+ );
+ $statusCode = $response->getStatusCode();
+ if ($statusCode >= 200 && $statusCode < 400) {
+ $json = $response->getBody()->getContents();
+ return MetadataResponse::fromJson($json);
+ }
+ } catch (JsonException $e) {
+ throw new SquareException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e);
+ } catch (ClientExceptionInterface $e) {
+ throw new SquareException(message: $e->getMessage(), previous: $e);
+ }
+ throw new SquareApiException(
+ message: 'API request failed',
+ statusCode: $statusCode,
+ body: $response->getBody()->getContents(),
+ );
+ }
+
+ /**
+ * Runs a reporting query against the discovered schema and returns the aggregated results. Long-running queries may return a "Continue wait" response while processing — retry the same request until results are ready.
+ *
+ * @param LoadRequest $request
+ * @param ?array{
+ * baseUrl?: string,
+ * maxRetries?: int,
+ * timeout?: float,
+ * headers?: array,
+ * queryParameters?: array,
+ * bodyProperties?: array,
+ * } $options
+ * @return LoadResponse
+ * @throws SquareException
+ * @throws SquareApiException
+ */
+ public function load(LoadRequest $request = new LoadRequest(), ?array $options = null): LoadResponse
+ {
+ $options = array_merge($this->options, $options ?? []);
+ try {
+ $response = $this->client->sendRequest(
+ new JsonApiRequest(
+ baseUrl: $options['baseUrl'] ?? $this->client->options['baseUrl'] ?? Environments::Production->value,
+ path: "reporting/v1/load",
+ method: HttpMethod::POST,
+ body: $request,
+ ),
+ $options,
+ );
+ $statusCode = $response->getStatusCode();
+ if ($statusCode >= 200 && $statusCode < 400) {
+ $json = $response->getBody()->getContents();
+ return LoadResponse::fromJson($json);
+ }
+ } catch (JsonException $e) {
+ throw new SquareException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e);
+ } catch (ClientExceptionInterface $e) {
+ throw new SquareException(message: $e->getMessage(), previous: $e);
+ }
+ throw new SquareApiException(
+ message: 'API request failed',
+ statusCode: $statusCode,
+ body: $response->getBody()->getContents(),
+ );
+ }
+}
diff --git a/src/Reporting/Requests/LoadRequest.php b/src/Reporting/Requests/LoadRequest.php
new file mode 100644
index 00000000..e3111b84
--- /dev/null
+++ b/src/Reporting/Requests/LoadRequest.php
@@ -0,0 +1,98 @@
+ $cache
+ */
+ #[JsonProperty('cache')]
+ private ?string $cache;
+
+ /**
+ * @var ?Query $query
+ */
+ #[JsonProperty('query')]
+ private ?Query $query;
+
+ /**
+ * @param array{
+ * queryType?: ?string,
+ * cache?: ?value-of,
+ * query?: ?Query,
+ * } $values
+ */
+ public function __construct(
+ array $values = [],
+ ) {
+ $this->queryType = $values['queryType'] ?? null;
+ $this->cache = $values['cache'] ?? null;
+ $this->query = $values['query'] ?? null;
+ }
+
+ /**
+ * @return ?string
+ */
+ public function getQueryType(): ?string
+ {
+ return $this->queryType;
+ }
+
+ /**
+ * @param ?string $value
+ */
+ public function setQueryType(?string $value = null): self
+ {
+ $this->queryType = $value;
+ $this->_setField('queryType');
+ return $this;
+ }
+
+ /**
+ * @return ?value-of
+ */
+ public function getCache(): ?string
+ {
+ return $this->cache;
+ }
+
+ /**
+ * @param ?value-of $value
+ */
+ public function setCache(?string $value = null): self
+ {
+ $this->cache = $value;
+ $this->_setField('cache');
+ return $this;
+ }
+
+ /**
+ * @return ?Query
+ */
+ public function getQuery(): ?Query
+ {
+ return $this->query;
+ }
+
+ /**
+ * @param ?Query $value
+ */
+ public function setQuery(?Query $value = null): self
+ {
+ $this->query = $value;
+ $this->_setField('query');
+ return $this;
+ }
+}
diff --git a/src/SquareClient.php b/src/SquareClient.php
index d009ffdf..08ee8d59 100644
--- a/src/SquareClient.php
+++ b/src/SquareClient.php
@@ -35,6 +35,7 @@
use Square\Terminal\TerminalClient;
use Square\TransferOrders\TransferOrdersClient;
use Square\Vendors\VendorsClient;
+use Square\Reporting\ReportingClient;
use Square\CashDrawers\CashDrawersClient;
use Square\Webhooks\WebhooksClient;
use Psr\Http\Client\ClientInterface;
@@ -208,6 +209,11 @@ class SquareClient
*/
public VendorsClient $vendors;
+ /**
+ * @var ReportingClient $reporting
+ */
+ public ReportingClient $reporting;
+
/**
* @var CashDrawersClient $cashDrawers
*/
@@ -256,8 +262,8 @@ public function __construct(
'Square-Version' => '2026-05-20',
'X-Fern-Language' => 'PHP',
'X-Fern-SDK-Name' => 'Square',
- 'X-Fern-SDK-Version' => '45.1.0.20260520',
- 'User-Agent' => 'square/square/45.1.0.20260520',
+ 'X-Fern-SDK-Version' => '45.2.0.20260520',
+ 'User-Agent' => 'square/square/45.2.0.20260520',
];
if ($version != null) {
$defaultHeaders['Square-Version'] = $version;
@@ -307,6 +313,7 @@ public function __construct(
$this->terminal = new TerminalClient($this->client, $this->options);
$this->transferOrders = new TransferOrdersClient($this->client, $this->options);
$this->vendors = new VendorsClient($this->client, $this->options);
+ $this->reporting = new ReportingClient($this->client, $this->options);
$this->cashDrawers = new CashDrawersClient($this->client, $this->options);
$this->webhooks = new WebhooksClient($this->client, $this->options);
}
diff --git a/src/Types/CacheMode.php b/src/Types/CacheMode.php
new file mode 100644
index 00000000..13da5c6e
--- /dev/null
+++ b/src/Types/CacheMode.php
@@ -0,0 +1,11 @@
+ $type
+ */
+ #[JsonProperty('type')]
+ private string $type;
+
+ /**
+ * @var ?array $meta
+ */
+ #[JsonProperty('meta'), ArrayType(['string' => 'mixed'])]
+ private ?array $meta;
+
+ /**
+ * @var ?string $description
+ */
+ #[JsonProperty('description')]
+ private ?string $description;
+
+ /**
+ * @var array $measures
+ */
+ #[JsonProperty('measures'), ArrayType([Measure::class])]
+ private array $measures;
+
+ /**
+ * @var array $dimensions
+ */
+ #[JsonProperty('dimensions'), ArrayType([Dimension::class])]
+ private array $dimensions;
+
+ /**
+ * @var array $segments
+ */
+ #[JsonProperty('segments'), ArrayType([Segment::class])]
+ private array $segments;
+
+ /**
+ * @var ?array $joins
+ */
+ #[JsonProperty('joins'), ArrayType([CubeJoin::class])]
+ private ?array $joins;
+
+ /**
+ * @var ?array $folders
+ */
+ #[JsonProperty('folders'), ArrayType([Folder::class])]
+ private ?array $folders;
+
+ /**
+ * @var ?array $nestedFolders
+ */
+ #[JsonProperty('nestedFolders'), ArrayType([NestedFolder::class])]
+ private ?array $nestedFolders;
+
+ /**
+ * @var ?array $hierarchies
+ */
+ #[JsonProperty('hierarchies'), ArrayType([Hierarchy::class])]
+ private ?array $hierarchies;
+
+ /**
+ * @param array{
+ * name: string,
+ * type: value-of,
+ * measures: array,
+ * dimensions: array,
+ * segments: array,
+ * title?: ?string,
+ * meta?: ?array,
+ * description?: ?string,
+ * joins?: ?array,
+ * folders?: ?array,
+ * nestedFolders?: ?array,
+ * hierarchies?: ?array,
+ * } $values
+ */
+ public function __construct(
+ array $values,
+ ) {
+ $this->name = $values['name'];
+ $this->title = $values['title'] ?? null;
+ $this->type = $values['type'];
+ $this->meta = $values['meta'] ?? null;
+ $this->description = $values['description'] ?? null;
+ $this->measures = $values['measures'];
+ $this->dimensions = $values['dimensions'];
+ $this->segments = $values['segments'];
+ $this->joins = $values['joins'] ?? null;
+ $this->folders = $values['folders'] ?? null;
+ $this->nestedFolders = $values['nestedFolders'] ?? null;
+ $this->hierarchies = $values['hierarchies'] ?? null;
+ }
+
+ /**
+ * @return string
+ */
+ public function getName(): string
+ {
+ return $this->name;
+ }
+
+ /**
+ * @param string $value
+ */
+ public function setName(string $value): self
+ {
+ $this->name = $value;
+ $this->_setField('name');
+ return $this;
+ }
+
+ /**
+ * @return ?string
+ */
+ public function getTitle(): ?string
+ {
+ return $this->title;
+ }
+
+ /**
+ * @param ?string $value
+ */
+ public function setTitle(?string $value = null): self
+ {
+ $this->title = $value;
+ $this->_setField('title');
+ return $this;
+ }
+
+ /**
+ * @return value-of
+ */
+ public function getType(): string
+ {
+ return $this->type;
+ }
+
+ /**
+ * @param value-of $value
+ */
+ public function setType(string $value): self
+ {
+ $this->type = $value;
+ $this->_setField('type');
+ return $this;
+ }
+
+ /**
+ * @return ?array
+ */
+ public function getMeta(): ?array
+ {
+ return $this->meta;
+ }
+
+ /**
+ * @param ?array $value
+ */
+ public function setMeta(?array $value = null): self
+ {
+ $this->meta = $value;
+ $this->_setField('meta');
+ return $this;
+ }
+
+ /**
+ * @return ?string
+ */
+ public function getDescription(): ?string
+ {
+ return $this->description;
+ }
+
+ /**
+ * @param ?string $value
+ */
+ public function setDescription(?string $value = null): self
+ {
+ $this->description = $value;
+ $this->_setField('description');
+ return $this;
+ }
+
+ /**
+ * @return array
+ */
+ public function getMeasures(): array
+ {
+ return $this->measures;
+ }
+
+ /**
+ * @param array $value
+ */
+ public function setMeasures(array $value): self
+ {
+ $this->measures = $value;
+ $this->_setField('measures');
+ return $this;
+ }
+
+ /**
+ * @return array
+ */
+ public function getDimensions(): array
+ {
+ return $this->dimensions;
+ }
+
+ /**
+ * @param array $value
+ */
+ public function setDimensions(array $value): self
+ {
+ $this->dimensions = $value;
+ $this->_setField('dimensions');
+ return $this;
+ }
+
+ /**
+ * @return array
+ */
+ public function getSegments(): array
+ {
+ return $this->segments;
+ }
+
+ /**
+ * @param array $value
+ */
+ public function setSegments(array $value): self
+ {
+ $this->segments = $value;
+ $this->_setField('segments');
+ return $this;
+ }
+
+ /**
+ * @return ?array
+ */
+ public function getJoins(): ?array
+ {
+ return $this->joins;
+ }
+
+ /**
+ * @param ?array $value
+ */
+ public function setJoins(?array $value = null): self
+ {
+ $this->joins = $value;
+ $this->_setField('joins');
+ return $this;
+ }
+
+ /**
+ * @return ?array
+ */
+ public function getFolders(): ?array
+ {
+ return $this->folders;
+ }
+
+ /**
+ * @param ?array $value
+ */
+ public function setFolders(?array $value = null): self
+ {
+ $this->folders = $value;
+ $this->_setField('folders');
+ return $this;
+ }
+
+ /**
+ * @return ?array
+ */
+ public function getNestedFolders(): ?array
+ {
+ return $this->nestedFolders;
+ }
+
+ /**
+ * @param ?array $value
+ */
+ public function setNestedFolders(?array $value = null): self
+ {
+ $this->nestedFolders = $value;
+ $this->_setField('nestedFolders');
+ return $this;
+ }
+
+ /**
+ * @return ?array
+ */
+ public function getHierarchies(): ?array
+ {
+ return $this->hierarchies;
+ }
+
+ /**
+ * @param ?array $value
+ */
+ public function setHierarchies(?array $value = null): self
+ {
+ $this->hierarchies = $value;
+ $this->_setField('hierarchies');
+ return $this;
+ }
+
+ /**
+ * @return string
+ */
+ public function __toString(): string
+ {
+ return $this->toJson();
+ }
+}
diff --git a/src/Types/CubeJoin.php b/src/Types/CubeJoin.php
new file mode 100644
index 00000000..b26087e6
--- /dev/null
+++ b/src/Types/CubeJoin.php
@@ -0,0 +1,78 @@
+name = $values['name'];
+ $this->relationship = $values['relationship'];
+ }
+
+ /**
+ * @return string
+ */
+ public function getName(): string
+ {
+ return $this->name;
+ }
+
+ /**
+ * @param string $value
+ */
+ public function setName(string $value): self
+ {
+ $this->name = $value;
+ $this->_setField('name');
+ return $this;
+ }
+
+ /**
+ * @return string
+ */
+ public function getRelationship(): string
+ {
+ return $this->relationship;
+ }
+
+ /**
+ * @param string $value
+ */
+ public function setRelationship(string $value): self
+ {
+ $this->relationship = $value;
+ $this->_setField('relationship');
+ return $this;
+ }
+
+ /**
+ * @return string
+ */
+ public function __toString(): string
+ {
+ return $this->toJson();
+ }
+}
diff --git a/src/Types/CubeType.php b/src/Types/CubeType.php
new file mode 100644
index 00000000..d312102b
--- /dev/null
+++ b/src/Types/CubeType.php
@@ -0,0 +1,9 @@
+type = $values['type'];
+ $this->value = $values['value'];
+ $this->alias = $values['alias'] ?? null;
+ }
+
+ /**
+ * @return 'custom-numeric'
+ */
+ public function getType(): string
+ {
+ return $this->type;
+ }
+
+ /**
+ * @param 'custom-numeric' $value
+ */
+ public function setType(string $value): self
+ {
+ $this->type = $value;
+ $this->_setField('type');
+ return $this;
+ }
+
+ /**
+ * @return string
+ */
+ public function getValue(): string
+ {
+ return $this->value;
+ }
+
+ /**
+ * @param string $value
+ */
+ public function setValue(string $value): self
+ {
+ $this->value = $value;
+ $this->_setField('value');
+ return $this;
+ }
+
+ /**
+ * @return ?string
+ */
+ public function getAlias(): ?string
+ {
+ return $this->alias;
+ }
+
+ /**
+ * @param ?string $value
+ */
+ public function setAlias(?string $value = null): self
+ {
+ $this->alias = $value;
+ $this->_setField('alias');
+ return $this;
+ }
+
+ /**
+ * @return string
+ */
+ public function __toString(): string
+ {
+ return $this->toJson();
+ }
+}
diff --git a/src/Types/CustomTimeFormat.php b/src/Types/CustomTimeFormat.php
new file mode 100644
index 00000000..f42bf91c
--- /dev/null
+++ b/src/Types/CustomTimeFormat.php
@@ -0,0 +1,81 @@
+type = $values['type'];
+ $this->value = $values['value'];
+ }
+
+ /**
+ * @return 'custom-time'
+ */
+ public function getType(): string
+ {
+ return $this->type;
+ }
+
+ /**
+ * @param 'custom-time' $value
+ */
+ public function setType(string $value): self
+ {
+ $this->type = $value;
+ $this->_setField('type');
+ return $this;
+ }
+
+ /**
+ * @return string
+ */
+ public function getValue(): string
+ {
+ return $this->value;
+ }
+
+ /**
+ * @param string $value
+ */
+ public function setValue(string $value): self
+ {
+ $this->value = $value;
+ $this->_setField('value');
+ return $this;
+ }
+
+ /**
+ * @return string
+ */
+ public function __toString(): string
+ {
+ return $this->toJson();
+ }
+}
diff --git a/src/Types/Dimension.php b/src/Types/Dimension.php
new file mode 100644
index 00000000..6c0b51a6
--- /dev/null
+++ b/src/Types/Dimension.php
@@ -0,0 +1,386 @@
+ $granularities
+ */
+ #[JsonProperty('granularities'), ArrayType([DimensionGranularity::class])]
+ private ?array $granularities;
+
+ /**
+ * @var ?array $meta
+ */
+ #[JsonProperty('meta'), ArrayType(['string' => 'mixed'])]
+ private ?array $meta;
+
+ /**
+ * @var (
+ * value-of
+ * |LinkFormat
+ * |CustomTimeFormat
+ * |CustomNumericFormat
+ * )|null $format
+ */
+ #[JsonProperty('format'), Union('string', LinkFormat::class, CustomTimeFormat::class, CustomNumericFormat::class, 'null')]
+ private string|LinkFormat|CustomTimeFormat|CustomNumericFormat|null $format;
+
+ /**
+ * @var ?FormatDescription $formatDescription
+ */
+ #[JsonProperty('formatDescription')]
+ private ?FormatDescription $formatDescription;
+
+ /**
+ * @var ?string $currency ISO 4217 currency code in uppercase (3 characters, e.g. USD, EUR)
+ */
+ #[JsonProperty('currency')]
+ private ?string $currency;
+
+ /**
+ * @var ?value-of $order
+ */
+ #[JsonProperty('order')]
+ private ?string $order;
+
+ /**
+ * @var ?string $key Key reference for the dimension
+ */
+ #[JsonProperty('key')]
+ private ?string $key;
+
+ /**
+ * @param array{
+ * name: string,
+ * type: string,
+ * title?: ?string,
+ * shortTitle?: ?string,
+ * description?: ?string,
+ * aliasMember?: ?string,
+ * granularities?: ?array,
+ * meta?: ?array,
+ * format?: (
+ * value-of
+ * |LinkFormat
+ * |CustomTimeFormat
+ * |CustomNumericFormat
+ * )|null,
+ * formatDescription?: ?FormatDescription,
+ * currency?: ?string,
+ * order?: ?value-of,
+ * key?: ?string,
+ * } $values
+ */
+ public function __construct(
+ array $values,
+ ) {
+ $this->name = $values['name'];
+ $this->title = $values['title'] ?? null;
+ $this->shortTitle = $values['shortTitle'] ?? null;
+ $this->description = $values['description'] ?? null;
+ $this->type = $values['type'];
+ $this->aliasMember = $values['aliasMember'] ?? null;
+ $this->granularities = $values['granularities'] ?? null;
+ $this->meta = $values['meta'] ?? null;
+ $this->format = $values['format'] ?? null;
+ $this->formatDescription = $values['formatDescription'] ?? null;
+ $this->currency = $values['currency'] ?? null;
+ $this->order = $values['order'] ?? null;
+ $this->key = $values['key'] ?? null;
+ }
+
+ /**
+ * @return string
+ */
+ public function getName(): string
+ {
+ return $this->name;
+ }
+
+ /**
+ * @param string $value
+ */
+ public function setName(string $value): self
+ {
+ $this->name = $value;
+ $this->_setField('name');
+ return $this;
+ }
+
+ /**
+ * @return ?string
+ */
+ public function getTitle(): ?string
+ {
+ return $this->title;
+ }
+
+ /**
+ * @param ?string $value
+ */
+ public function setTitle(?string $value = null): self
+ {
+ $this->title = $value;
+ $this->_setField('title');
+ return $this;
+ }
+
+ /**
+ * @return ?string
+ */
+ public function getShortTitle(): ?string
+ {
+ return $this->shortTitle;
+ }
+
+ /**
+ * @param ?string $value
+ */
+ public function setShortTitle(?string $value = null): self
+ {
+ $this->shortTitle = $value;
+ $this->_setField('shortTitle');
+ return $this;
+ }
+
+ /**
+ * @return ?string
+ */
+ public function getDescription(): ?string
+ {
+ return $this->description;
+ }
+
+ /**
+ * @param ?string $value
+ */
+ public function setDescription(?string $value = null): self
+ {
+ $this->description = $value;
+ $this->_setField('description');
+ return $this;
+ }
+
+ /**
+ * @return string
+ */
+ public function getType(): string
+ {
+ return $this->type;
+ }
+
+ /**
+ * @param string $value
+ */
+ public function setType(string $value): self
+ {
+ $this->type = $value;
+ $this->_setField('type');
+ return $this;
+ }
+
+ /**
+ * @return ?string
+ */
+ public function getAliasMember(): ?string
+ {
+ return $this->aliasMember;
+ }
+
+ /**
+ * @param ?string $value
+ */
+ public function setAliasMember(?string $value = null): self
+ {
+ $this->aliasMember = $value;
+ $this->_setField('aliasMember');
+ return $this;
+ }
+
+ /**
+ * @return ?array
+ */
+ public function getGranularities(): ?array
+ {
+ return $this->granularities;
+ }
+
+ /**
+ * @param ?array $value
+ */
+ public function setGranularities(?array $value = null): self
+ {
+ $this->granularities = $value;
+ $this->_setField('granularities');
+ return $this;
+ }
+
+ /**
+ * @return ?array
+ */
+ public function getMeta(): ?array
+ {
+ return $this->meta;
+ }
+
+ /**
+ * @param ?array $value
+ */
+ public function setMeta(?array $value = null): self
+ {
+ $this->meta = $value;
+ $this->_setField('meta');
+ return $this;
+ }
+
+ /**
+ * @return (
+ * value-of
+ * |LinkFormat
+ * |CustomTimeFormat
+ * |CustomNumericFormat
+ * )|null
+ */
+ public function getFormat(): string|LinkFormat|CustomTimeFormat|CustomNumericFormat|null
+ {
+ return $this->format;
+ }
+
+ /**
+ * @param (
+ * value-of
+ * |LinkFormat
+ * |CustomTimeFormat
+ * |CustomNumericFormat
+ * )|null $value
+ */
+ public function setFormat(string|LinkFormat|CustomTimeFormat|CustomNumericFormat|null $value = null): self
+ {
+ $this->format = $value;
+ $this->_setField('format');
+ return $this;
+ }
+
+ /**
+ * @return ?FormatDescription
+ */
+ public function getFormatDescription(): ?FormatDescription
+ {
+ return $this->formatDescription;
+ }
+
+ /**
+ * @param ?FormatDescription $value
+ */
+ public function setFormatDescription(?FormatDescription $value = null): self
+ {
+ $this->formatDescription = $value;
+ $this->_setField('formatDescription');
+ return $this;
+ }
+
+ /**
+ * @return ?string
+ */
+ public function getCurrency(): ?string
+ {
+ return $this->currency;
+ }
+
+ /**
+ * @param ?string $value
+ */
+ public function setCurrency(?string $value = null): self
+ {
+ $this->currency = $value;
+ $this->_setField('currency');
+ return $this;
+ }
+
+ /**
+ * @return ?value-of
+ */
+ public function getOrder(): ?string
+ {
+ return $this->order;
+ }
+
+ /**
+ * @param ?value-of $value
+ */
+ public function setOrder(?string $value = null): self
+ {
+ $this->order = $value;
+ $this->_setField('order');
+ return $this;
+ }
+
+ /**
+ * @return ?string
+ */
+ public function getKey(): ?string
+ {
+ return $this->key;
+ }
+
+ /**
+ * @param ?string $value
+ */
+ public function setKey(?string $value = null): self
+ {
+ $this->key = $value;
+ $this->_setField('key');
+ return $this;
+ }
+
+ /**
+ * @return string
+ */
+ public function __toString(): string
+ {
+ return $this->toJson();
+ }
+}
diff --git a/src/Types/DimensionGranularity.php b/src/Types/DimensionGranularity.php
new file mode 100644
index 00000000..1750e357
--- /dev/null
+++ b/src/Types/DimensionGranularity.php
@@ -0,0 +1,182 @@
+name = $values['name'];
+ $this->title = $values['title'];
+ $this->interval = $values['interval'] ?? null;
+ $this->sql = $values['sql'] ?? null;
+ $this->offset = $values['offset'] ?? null;
+ $this->origin = $values['origin'] ?? null;
+ }
+
+ /**
+ * @return string
+ */
+ public function getName(): string
+ {
+ return $this->name;
+ }
+
+ /**
+ * @param string $value
+ */
+ public function setName(string $value): self
+ {
+ $this->name = $value;
+ $this->_setField('name');
+ return $this;
+ }
+
+ /**
+ * @return string
+ */
+ public function getTitle(): string
+ {
+ return $this->title;
+ }
+
+ /**
+ * @param string $value
+ */
+ public function setTitle(string $value): self
+ {
+ $this->title = $value;
+ $this->_setField('title');
+ return $this;
+ }
+
+ /**
+ * @return ?string
+ */
+ public function getInterval(): ?string
+ {
+ return $this->interval;
+ }
+
+ /**
+ * @param ?string $value
+ */
+ public function setInterval(?string $value = null): self
+ {
+ $this->interval = $value;
+ $this->_setField('interval');
+ return $this;
+ }
+
+ /**
+ * @return ?string
+ */
+ public function getSql(): ?string
+ {
+ return $this->sql;
+ }
+
+ /**
+ * @param ?string $value
+ */
+ public function setSql(?string $value = null): self
+ {
+ $this->sql = $value;
+ $this->_setField('sql');
+ return $this;
+ }
+
+ /**
+ * @return ?string
+ */
+ public function getOffset(): ?string
+ {
+ return $this->offset;
+ }
+
+ /**
+ * @param ?string $value
+ */
+ public function setOffset(?string $value = null): self
+ {
+ $this->offset = $value;
+ $this->_setField('offset');
+ return $this;
+ }
+
+ /**
+ * @return ?string
+ */
+ public function getOrigin(): ?string
+ {
+ return $this->origin;
+ }
+
+ /**
+ * @param ?string $value
+ */
+ public function setOrigin(?string $value = null): self
+ {
+ $this->origin = $value;
+ $this->_setField('origin');
+ return $this;
+ }
+
+ /**
+ * @return string
+ */
+ public function __toString(): string
+ {
+ return $this->toJson();
+ }
+}
diff --git a/src/Types/DimensionOrder.php b/src/Types/DimensionOrder.php
new file mode 100644
index 00000000..9d71e527
--- /dev/null
+++ b/src/Types/DimensionOrder.php
@@ -0,0 +1,9 @@
+ $members
+ */
+ #[JsonProperty('members'), ArrayType(['string'])]
+ private array $members;
+
+ /**
+ * @param array{
+ * name: string,
+ * members: array,
+ * } $values
+ */
+ public function __construct(
+ array $values,
+ ) {
+ $this->name = $values['name'];
+ $this->members = $values['members'];
+ }
+
+ /**
+ * @return string
+ */
+ public function getName(): string
+ {
+ return $this->name;
+ }
+
+ /**
+ * @param string $value
+ */
+ public function setName(string $value): self
+ {
+ $this->name = $value;
+ $this->_setField('name');
+ return $this;
+ }
+
+ /**
+ * @return array
+ */
+ public function getMembers(): array
+ {
+ return $this->members;
+ }
+
+ /**
+ * @param array $value
+ */
+ public function setMembers(array $value): self
+ {
+ $this->members = $value;
+ $this->_setField('members');
+ return $this;
+ }
+
+ /**
+ * @return string
+ */
+ public function __toString(): string
+ {
+ return $this->toJson();
+ }
+}
diff --git a/src/Types/FormatDescription.php b/src/Types/FormatDescription.php
new file mode 100644
index 00000000..30a8b386
--- /dev/null
+++ b/src/Types/FormatDescription.php
@@ -0,0 +1,107 @@
+name = $values['name'];
+ $this->specifier = $values['specifier'];
+ $this->currency = $values['currency'] ?? null;
+ }
+
+ /**
+ * @return string
+ */
+ public function getName(): string
+ {
+ return $this->name;
+ }
+
+ /**
+ * @param string $value
+ */
+ public function setName(string $value): self
+ {
+ $this->name = $value;
+ $this->_setField('name');
+ return $this;
+ }
+
+ /**
+ * @return string
+ */
+ public function getSpecifier(): string
+ {
+ return $this->specifier;
+ }
+
+ /**
+ * @param string $value
+ */
+ public function setSpecifier(string $value): self
+ {
+ $this->specifier = $value;
+ $this->_setField('specifier');
+ return $this;
+ }
+
+ /**
+ * @return ?string
+ */
+ public function getCurrency(): ?string
+ {
+ return $this->currency;
+ }
+
+ /**
+ * @param ?string $value
+ */
+ public function setCurrency(?string $value = null): self
+ {
+ $this->currency = $value;
+ $this->_setField('currency');
+ return $this;
+ }
+
+ /**
+ * @return string
+ */
+ public function __toString(): string
+ {
+ return $this->toJson();
+ }
+}
diff --git a/src/Types/Hierarchy.php b/src/Types/Hierarchy.php
new file mode 100644
index 00000000..39431630
--- /dev/null
+++ b/src/Types/Hierarchy.php
@@ -0,0 +1,131 @@
+ $levels
+ */
+ #[JsonProperty('levels'), ArrayType(['string'])]
+ private array $levels;
+
+ /**
+ * @param array{
+ * name: string,
+ * levels: array,
+ * aliasMember?: ?string,
+ * title?: ?string,
+ * } $values
+ */
+ public function __construct(
+ array $values,
+ ) {
+ $this->name = $values['name'];
+ $this->aliasMember = $values['aliasMember'] ?? null;
+ $this->title = $values['title'] ?? null;
+ $this->levels = $values['levels'];
+ }
+
+ /**
+ * @return string
+ */
+ public function getName(): string
+ {
+ return $this->name;
+ }
+
+ /**
+ * @param string $value
+ */
+ public function setName(string $value): self
+ {
+ $this->name = $value;
+ $this->_setField('name');
+ return $this;
+ }
+
+ /**
+ * @return ?string
+ */
+ public function getAliasMember(): ?string
+ {
+ return $this->aliasMember;
+ }
+
+ /**
+ * @param ?string $value
+ */
+ public function setAliasMember(?string $value = null): self
+ {
+ $this->aliasMember = $value;
+ $this->_setField('aliasMember');
+ return $this;
+ }
+
+ /**
+ * @return ?string
+ */
+ public function getTitle(): ?string
+ {
+ return $this->title;
+ }
+
+ /**
+ * @param ?string $value
+ */
+ public function setTitle(?string $value = null): self
+ {
+ $this->title = $value;
+ $this->_setField('title');
+ return $this;
+ }
+
+ /**
+ * @return array
+ */
+ public function getLevels(): array
+ {
+ return $this->levels;
+ }
+
+ /**
+ * @param array $value
+ */
+ public function setLevels(array $value): self
+ {
+ $this->levels = $value;
+ $this->_setField('levels');
+ return $this;
+ }
+
+ /**
+ * @return string
+ */
+ public function __toString(): string
+ {
+ return $this->toJson();
+ }
+}
diff --git a/src/Types/JoinSubquery.php b/src/Types/JoinSubquery.php
new file mode 100644
index 00000000..2673a3d5
--- /dev/null
+++ b/src/Types/JoinSubquery.php
@@ -0,0 +1,130 @@
+sql = $values['sql'];
+ $this->on = $values['on'];
+ $this->joinType = $values['joinType'];
+ $this->alias = $values['alias'];
+ }
+
+ /**
+ * @return string
+ */
+ public function getSql(): string
+ {
+ return $this->sql;
+ }
+
+ /**
+ * @param string $value
+ */
+ public function setSql(string $value): self
+ {
+ $this->sql = $value;
+ $this->_setField('sql');
+ return $this;
+ }
+
+ /**
+ * @return string
+ */
+ public function getOn(): string
+ {
+ return $this->on;
+ }
+
+ /**
+ * @param string $value
+ */
+ public function setOn(string $value): self
+ {
+ $this->on = $value;
+ $this->_setField('on');
+ return $this;
+ }
+
+ /**
+ * @return string
+ */
+ public function getJoinType(): string
+ {
+ return $this->joinType;
+ }
+
+ /**
+ * @param string $value
+ */
+ public function setJoinType(string $value): self
+ {
+ $this->joinType = $value;
+ $this->_setField('joinType');
+ return $this;
+ }
+
+ /**
+ * @return string
+ */
+ public function getAlias(): string
+ {
+ return $this->alias;
+ }
+
+ /**
+ * @param string $value
+ */
+ public function setAlias(string $value): self
+ {
+ $this->alias = $value;
+ $this->_setField('alias');
+ return $this;
+ }
+
+ /**
+ * @return string
+ */
+ public function __toString(): string
+ {
+ return $this->toJson();
+ }
+}
diff --git a/src/Types/LinkFormat.php b/src/Types/LinkFormat.php
new file mode 100644
index 00000000..474e8772
--- /dev/null
+++ b/src/Types/LinkFormat.php
@@ -0,0 +1,81 @@
+label = $values['label'];
+ $this->type = $values['type'];
+ }
+
+ /**
+ * @return string
+ */
+ public function getLabel(): string
+ {
+ return $this->label;
+ }
+
+ /**
+ * @param string $value
+ */
+ public function setLabel(string $value): self
+ {
+ $this->label = $value;
+ $this->_setField('label');
+ return $this;
+ }
+
+ /**
+ * @return 'link'
+ */
+ public function getType(): string
+ {
+ return $this->type;
+ }
+
+ /**
+ * @param 'link' $value
+ */
+ public function setType(string $value): self
+ {
+ $this->type = $value;
+ $this->_setField('type');
+ return $this;
+ }
+
+ /**
+ * @return string
+ */
+ public function __toString(): string
+ {
+ return $this->toJson();
+ }
+}
diff --git a/src/Types/LoadResponse.php b/src/Types/LoadResponse.php
new file mode 100644
index 00000000..e2dba995
--- /dev/null
+++ b/src/Types/LoadResponse.php
@@ -0,0 +1,131 @@
+ $pivotQuery
+ */
+ #[JsonProperty('pivotQuery'), ArrayType(['string' => 'mixed'])]
+ private ?array $pivotQuery;
+
+ /**
+ * @var ?bool $slowQuery
+ */
+ #[JsonProperty('slowQuery')]
+ private ?bool $slowQuery;
+
+ /**
+ * @var ?string $queryType
+ */
+ #[JsonProperty('queryType')]
+ private ?string $queryType;
+
+ /**
+ * @var array $results
+ */
+ #[JsonProperty('results'), ArrayType([LoadResult::class])]
+ private array $results;
+
+ /**
+ * @param array{
+ * results: array,
+ * pivotQuery?: ?array,
+ * slowQuery?: ?bool,
+ * queryType?: ?string,
+ * } $values
+ */
+ public function __construct(
+ array $values,
+ ) {
+ $this->pivotQuery = $values['pivotQuery'] ?? null;
+ $this->slowQuery = $values['slowQuery'] ?? null;
+ $this->queryType = $values['queryType'] ?? null;
+ $this->results = $values['results'];
+ }
+
+ /**
+ * @return ?array
+ */
+ public function getPivotQuery(): ?array
+ {
+ return $this->pivotQuery;
+ }
+
+ /**
+ * @param ?array $value
+ */
+ public function setPivotQuery(?array $value = null): self
+ {
+ $this->pivotQuery = $value;
+ $this->_setField('pivotQuery');
+ return $this;
+ }
+
+ /**
+ * @return ?bool
+ */
+ public function getSlowQuery(): ?bool
+ {
+ return $this->slowQuery;
+ }
+
+ /**
+ * @param ?bool $value
+ */
+ public function setSlowQuery(?bool $value = null): self
+ {
+ $this->slowQuery = $value;
+ $this->_setField('slowQuery');
+ return $this;
+ }
+
+ /**
+ * @return ?string
+ */
+ public function getQueryType(): ?string
+ {
+ return $this->queryType;
+ }
+
+ /**
+ * @param ?string $value
+ */
+ public function setQueryType(?string $value = null): self
+ {
+ $this->queryType = $value;
+ $this->_setField('queryType');
+ return $this;
+ }
+
+ /**
+ * @return array
+ */
+ public function getResults(): array
+ {
+ return $this->results;
+ }
+
+ /**
+ * @param array $value
+ */
+ public function setResults(array $value): self
+ {
+ $this->results = $value;
+ $this->_setField('results');
+ return $this;
+ }
+
+ /**
+ * @return string
+ */
+ public function __toString(): string
+ {
+ return $this->toJson();
+ }
+}
diff --git a/src/Types/LoadResult.php b/src/Types/LoadResult.php
new file mode 100644
index 00000000..d0b01bce
--- /dev/null
+++ b/src/Types/LoadResult.php
@@ -0,0 +1,174 @@
+>
+ * |LoadResultDataCompact
+ * |LoadResultDataColumnar
+ * ) $data
+ */
+ #[JsonProperty('data'), Union([['string' => 'mixed']], LoadResultDataCompact::class, LoadResultDataColumnar::class)]
+ private array|LoadResultDataCompact|LoadResultDataColumnar $data;
+
+ /**
+ * @var ?array> $refreshKeyValues
+ */
+ #[JsonProperty('refreshKeyValues'), ArrayType([['string' => 'mixed']])]
+ private ?array $refreshKeyValues;
+
+ /**
+ * @var ?string $lastRefreshTime
+ */
+ #[JsonProperty('lastRefreshTime')]
+ private ?string $lastRefreshTime;
+
+ /**
+ * @param array{
+ * annotation: LoadResultAnnotation,
+ * data: (
+ * array>
+ * |LoadResultDataCompact
+ * |LoadResultDataColumnar
+ * ),
+ * dataSource?: ?string,
+ * refreshKeyValues?: ?array>,
+ * lastRefreshTime?: ?string,
+ * } $values
+ */
+ public function __construct(
+ array $values,
+ ) {
+ $this->dataSource = $values['dataSource'] ?? null;
+ $this->annotation = $values['annotation'];
+ $this->data = $values['data'];
+ $this->refreshKeyValues = $values['refreshKeyValues'] ?? null;
+ $this->lastRefreshTime = $values['lastRefreshTime'] ?? null;
+ }
+
+ /**
+ * @return ?string
+ */
+ public function getDataSource(): ?string
+ {
+ return $this->dataSource;
+ }
+
+ /**
+ * @param ?string $value
+ */
+ public function setDataSource(?string $value = null): self
+ {
+ $this->dataSource = $value;
+ $this->_setField('dataSource');
+ return $this;
+ }
+
+ /**
+ * @return LoadResultAnnotation
+ */
+ public function getAnnotation(): LoadResultAnnotation
+ {
+ return $this->annotation;
+ }
+
+ /**
+ * @param LoadResultAnnotation $value
+ */
+ public function setAnnotation(LoadResultAnnotation $value): self
+ {
+ $this->annotation = $value;
+ $this->_setField('annotation');
+ return $this;
+ }
+
+ /**
+ * @return (
+ * array>
+ * |LoadResultDataCompact
+ * |LoadResultDataColumnar
+ * )
+ */
+ public function getData(): array|LoadResultDataCompact|LoadResultDataColumnar
+ {
+ return $this->data;
+ }
+
+ /**
+ * @param (
+ * array>
+ * |LoadResultDataCompact
+ * |LoadResultDataColumnar
+ * ) $value
+ */
+ public function setData(array|LoadResultDataCompact|LoadResultDataColumnar $value): self
+ {
+ $this->data = $value;
+ $this->_setField('data');
+ return $this;
+ }
+
+ /**
+ * @return ?array>
+ */
+ public function getRefreshKeyValues(): ?array
+ {
+ return $this->refreshKeyValues;
+ }
+
+ /**
+ * @param ?array> $value
+ */
+ public function setRefreshKeyValues(?array $value = null): self
+ {
+ $this->refreshKeyValues = $value;
+ $this->_setField('refreshKeyValues');
+ return $this;
+ }
+
+ /**
+ * @return ?string
+ */
+ public function getLastRefreshTime(): ?string
+ {
+ return $this->lastRefreshTime;
+ }
+
+ /**
+ * @param ?string $value
+ */
+ public function setLastRefreshTime(?string $value = null): self
+ {
+ $this->lastRefreshTime = $value;
+ $this->_setField('lastRefreshTime');
+ return $this;
+ }
+
+ /**
+ * @return string
+ */
+ public function __toString(): string
+ {
+ return $this->toJson();
+ }
+}
diff --git a/src/Types/LoadResultAnnotation.php b/src/Types/LoadResultAnnotation.php
new file mode 100644
index 00000000..4ab5b7af
--- /dev/null
+++ b/src/Types/LoadResultAnnotation.php
@@ -0,0 +1,131 @@
+ $measures
+ */
+ #[JsonProperty('measures'), ArrayType(['string' => 'mixed'])]
+ private array $measures;
+
+ /**
+ * @var array $dimensions
+ */
+ #[JsonProperty('dimensions'), ArrayType(['string' => 'mixed'])]
+ private array $dimensions;
+
+ /**
+ * @var array $segments
+ */
+ #[JsonProperty('segments'), ArrayType(['string' => 'mixed'])]
+ private array $segments;
+
+ /**
+ * @var array $timeDimensions
+ */
+ #[JsonProperty('timeDimensions'), ArrayType(['string' => 'mixed'])]
+ private array $timeDimensions;
+
+ /**
+ * @param array{
+ * measures: array,
+ * dimensions: array,
+ * segments: array,
+ * timeDimensions: array,
+ * } $values
+ */
+ public function __construct(
+ array $values,
+ ) {
+ $this->measures = $values['measures'];
+ $this->dimensions = $values['dimensions'];
+ $this->segments = $values['segments'];
+ $this->timeDimensions = $values['timeDimensions'];
+ }
+
+ /**
+ * @return array
+ */
+ public function getMeasures(): array
+ {
+ return $this->measures;
+ }
+
+ /**
+ * @param array $value
+ */
+ public function setMeasures(array $value): self
+ {
+ $this->measures = $value;
+ $this->_setField('measures');
+ return $this;
+ }
+
+ /**
+ * @return array
+ */
+ public function getDimensions(): array
+ {
+ return $this->dimensions;
+ }
+
+ /**
+ * @param array $value
+ */
+ public function setDimensions(array $value): self
+ {
+ $this->dimensions = $value;
+ $this->_setField('dimensions');
+ return $this;
+ }
+
+ /**
+ * @return array
+ */
+ public function getSegments(): array
+ {
+ return $this->segments;
+ }
+
+ /**
+ * @param array $value
+ */
+ public function setSegments(array $value): self
+ {
+ $this->segments = $value;
+ $this->_setField('segments');
+ return $this;
+ }
+
+ /**
+ * @return array
+ */
+ public function getTimeDimensions(): array
+ {
+ return $this->timeDimensions;
+ }
+
+ /**
+ * @param array $value
+ */
+ public function setTimeDimensions(array $value): self
+ {
+ $this->timeDimensions = $value;
+ $this->_setField('timeDimensions');
+ return $this;
+ }
+
+ /**
+ * @return string
+ */
+ public function __toString(): string
+ {
+ return $this->toJson();
+ }
+}
diff --git a/src/Types/LoadResultDataColumnar.php b/src/Types/LoadResultDataColumnar.php
new file mode 100644
index 00000000..0ff87adb
--- /dev/null
+++ b/src/Types/LoadResultDataColumnar.php
@@ -0,0 +1,82 @@
+ $members Ordered list of member names. Element `i` of `columns` holds the values for `members[i]` across all rows.
+ */
+ #[JsonProperty('members'), ArrayType(['string'])]
+ private array $members;
+
+ /**
+ * @var array> $columns One array per member, in the same order as `members`. Each inner array contains the primitive value of that member for every row (null, boolean, number, string).
+ */
+ #[JsonProperty('columns'), ArrayType([['mixed']])]
+ private array $columns;
+
+ /**
+ * @param array{
+ * members: array,
+ * columns: array>,
+ * } $values
+ */
+ public function __construct(
+ array $values,
+ ) {
+ $this->members = $values['members'];
+ $this->columns = $values['columns'];
+ }
+
+ /**
+ * @return array
+ */
+ public function getMembers(): array
+ {
+ return $this->members;
+ }
+
+ /**
+ * @param array $value
+ */
+ public function setMembers(array $value): self
+ {
+ $this->members = $value;
+ $this->_setField('members');
+ return $this;
+ }
+
+ /**
+ * @return array>
+ */
+ public function getColumns(): array
+ {
+ return $this->columns;
+ }
+
+ /**
+ * @param array> $value
+ */
+ public function setColumns(array $value): self
+ {
+ $this->columns = $value;
+ $this->_setField('columns');
+ return $this;
+ }
+
+ /**
+ * @return string
+ */
+ public function __toString(): string
+ {
+ return $this->toJson();
+ }
+}
diff --git a/src/Types/LoadResultDataCompact.php b/src/Types/LoadResultDataCompact.php
new file mode 100644
index 00000000..ac921508
--- /dev/null
+++ b/src/Types/LoadResultDataCompact.php
@@ -0,0 +1,82 @@
+ $members Ordered list of member names that correspond to each cell position in `dataset` rows.
+ */
+ #[JsonProperty('members'), ArrayType(['string'])]
+ private array $members;
+
+ /**
+ * @var array> $dataset Array of rows, where each row is an array of primitive values (null, boolean, number, string) aligned with `members`.
+ */
+ #[JsonProperty('dataset'), ArrayType([['mixed']])]
+ private array $dataset;
+
+ /**
+ * @param array{
+ * members: array,
+ * dataset: array>,
+ * } $values
+ */
+ public function __construct(
+ array $values,
+ ) {
+ $this->members = $values['members'];
+ $this->dataset = $values['dataset'];
+ }
+
+ /**
+ * @return array
+ */
+ public function getMembers(): array
+ {
+ return $this->members;
+ }
+
+ /**
+ * @param array $value
+ */
+ public function setMembers(array $value): self
+ {
+ $this->members = $value;
+ $this->_setField('members');
+ return $this;
+ }
+
+ /**
+ * @return array>
+ */
+ public function getDataset(): array
+ {
+ return $this->dataset;
+ }
+
+ /**
+ * @param array> $value
+ */
+ public function setDataset(array $value): self
+ {
+ $this->dataset = $value;
+ $this->_setField('dataset');
+ return $this;
+ }
+
+ /**
+ * @return string
+ */
+ public function __toString(): string
+ {
+ return $this->toJson();
+ }
+}
diff --git a/src/Types/Measure.php b/src/Types/Measure.php
new file mode 100644
index 00000000..5a7b0a73
--- /dev/null
+++ b/src/Types/Measure.php
@@ -0,0 +1,334 @@
+ $meta
+ */
+ #[JsonProperty('meta'), ArrayType(['string' => 'mixed'])]
+ private ?array $meta;
+
+ /**
+ * @var (
+ * value-of
+ * |LinkFormat
+ * |CustomTimeFormat
+ * |CustomNumericFormat
+ * )|null $format
+ */
+ #[JsonProperty('format'), Union('string', LinkFormat::class, CustomTimeFormat::class, CustomNumericFormat::class, 'null')]
+ private string|LinkFormat|CustomTimeFormat|CustomNumericFormat|null $format;
+
+ /**
+ * @var ?FormatDescription $formatDescription
+ */
+ #[JsonProperty('formatDescription')]
+ private ?FormatDescription $formatDescription;
+
+ /**
+ * @var ?string $currency ISO 4217 currency code in uppercase (3 characters, e.g. USD, EUR)
+ */
+ #[JsonProperty('currency')]
+ private ?string $currency;
+
+ /**
+ * @var ?string $aliasMember When measure is defined in View, it keeps the original path: Cube.measure
+ */
+ #[JsonProperty('aliasMember')]
+ private ?string $aliasMember;
+
+ /**
+ * @param array{
+ * name: string,
+ * type: string,
+ * title?: ?string,
+ * shortTitle?: ?string,
+ * description?: ?string,
+ * aggType?: ?string,
+ * meta?: ?array,
+ * format?: (
+ * value-of
+ * |LinkFormat
+ * |CustomTimeFormat
+ * |CustomNumericFormat
+ * )|null,
+ * formatDescription?: ?FormatDescription,
+ * currency?: ?string,
+ * aliasMember?: ?string,
+ * } $values
+ */
+ public function __construct(
+ array $values,
+ ) {
+ $this->name = $values['name'];
+ $this->title = $values['title'] ?? null;
+ $this->shortTitle = $values['shortTitle'] ?? null;
+ $this->description = $values['description'] ?? null;
+ $this->type = $values['type'];
+ $this->aggType = $values['aggType'] ?? null;
+ $this->meta = $values['meta'] ?? null;
+ $this->format = $values['format'] ?? null;
+ $this->formatDescription = $values['formatDescription'] ?? null;
+ $this->currency = $values['currency'] ?? null;
+ $this->aliasMember = $values['aliasMember'] ?? null;
+ }
+
+ /**
+ * @return string
+ */
+ public function getName(): string
+ {
+ return $this->name;
+ }
+
+ /**
+ * @param string $value
+ */
+ public function setName(string $value): self
+ {
+ $this->name = $value;
+ $this->_setField('name');
+ return $this;
+ }
+
+ /**
+ * @return ?string
+ */
+ public function getTitle(): ?string
+ {
+ return $this->title;
+ }
+
+ /**
+ * @param ?string $value
+ */
+ public function setTitle(?string $value = null): self
+ {
+ $this->title = $value;
+ $this->_setField('title');
+ return $this;
+ }
+
+ /**
+ * @return ?string
+ */
+ public function getShortTitle(): ?string
+ {
+ return $this->shortTitle;
+ }
+
+ /**
+ * @param ?string $value
+ */
+ public function setShortTitle(?string $value = null): self
+ {
+ $this->shortTitle = $value;
+ $this->_setField('shortTitle');
+ return $this;
+ }
+
+ /**
+ * @return ?string
+ */
+ public function getDescription(): ?string
+ {
+ return $this->description;
+ }
+
+ /**
+ * @param ?string $value
+ */
+ public function setDescription(?string $value = null): self
+ {
+ $this->description = $value;
+ $this->_setField('description');
+ return $this;
+ }
+
+ /**
+ * @return string
+ */
+ public function getType(): string
+ {
+ return $this->type;
+ }
+
+ /**
+ * @param string $value
+ */
+ public function setType(string $value): self
+ {
+ $this->type = $value;
+ $this->_setField('type');
+ return $this;
+ }
+
+ /**
+ * @return ?string
+ */
+ public function getAggType(): ?string
+ {
+ return $this->aggType;
+ }
+
+ /**
+ * @param ?string $value
+ */
+ public function setAggType(?string $value = null): self
+ {
+ $this->aggType = $value;
+ $this->_setField('aggType');
+ return $this;
+ }
+
+ /**
+ * @return ?array
+ */
+ public function getMeta(): ?array
+ {
+ return $this->meta;
+ }
+
+ /**
+ * @param ?array $value
+ */
+ public function setMeta(?array $value = null): self
+ {
+ $this->meta = $value;
+ $this->_setField('meta');
+ return $this;
+ }
+
+ /**
+ * @return (
+ * value-of
+ * |LinkFormat
+ * |CustomTimeFormat
+ * |CustomNumericFormat
+ * )|null
+ */
+ public function getFormat(): string|LinkFormat|CustomTimeFormat|CustomNumericFormat|null
+ {
+ return $this->format;
+ }
+
+ /**
+ * @param (
+ * value-of
+ * |LinkFormat
+ * |CustomTimeFormat
+ * |CustomNumericFormat
+ * )|null $value
+ */
+ public function setFormat(string|LinkFormat|CustomTimeFormat|CustomNumericFormat|null $value = null): self
+ {
+ $this->format = $value;
+ $this->_setField('format');
+ return $this;
+ }
+
+ /**
+ * @return ?FormatDescription
+ */
+ public function getFormatDescription(): ?FormatDescription
+ {
+ return $this->formatDescription;
+ }
+
+ /**
+ * @param ?FormatDescription $value
+ */
+ public function setFormatDescription(?FormatDescription $value = null): self
+ {
+ $this->formatDescription = $value;
+ $this->_setField('formatDescription');
+ return $this;
+ }
+
+ /**
+ * @return ?string
+ */
+ public function getCurrency(): ?string
+ {
+ return $this->currency;
+ }
+
+ /**
+ * @param ?string $value
+ */
+ public function setCurrency(?string $value = null): self
+ {
+ $this->currency = $value;
+ $this->_setField('currency');
+ return $this;
+ }
+
+ /**
+ * @return ?string
+ */
+ public function getAliasMember(): ?string
+ {
+ return $this->aliasMember;
+ }
+
+ /**
+ * @param ?string $value
+ */
+ public function setAliasMember(?string $value = null): self
+ {
+ $this->aliasMember = $value;
+ $this->_setField('aliasMember');
+ return $this;
+ }
+
+ /**
+ * @return string
+ */
+ public function __toString(): string
+ {
+ return $this->toJson();
+ }
+}
diff --git a/src/Types/MetadataResponse.php b/src/Types/MetadataResponse.php
new file mode 100644
index 00000000..bd7149cd
--- /dev/null
+++ b/src/Types/MetadataResponse.php
@@ -0,0 +1,79 @@
+ $cubes
+ */
+ #[JsonProperty('cubes'), ArrayType([Cube::class])]
+ private ?array $cubes;
+
+ /**
+ * @var ?string $compilerId
+ */
+ #[JsonProperty('compilerId')]
+ private ?string $compilerId;
+
+ /**
+ * @param array{
+ * cubes?: ?array,
+ * compilerId?: ?string,
+ * } $values
+ */
+ public function __construct(
+ array $values = [],
+ ) {
+ $this->cubes = $values['cubes'] ?? null;
+ $this->compilerId = $values['compilerId'] ?? null;
+ }
+
+ /**
+ * @return ?array
+ */
+ public function getCubes(): ?array
+ {
+ return $this->cubes;
+ }
+
+ /**
+ * @param ?array $value
+ */
+ public function setCubes(?array $value = null): self
+ {
+ $this->cubes = $value;
+ $this->_setField('cubes');
+ return $this;
+ }
+
+ /**
+ * @return ?string
+ */
+ public function getCompilerId(): ?string
+ {
+ return $this->compilerId;
+ }
+
+ /**
+ * @param ?string $value
+ */
+ public function setCompilerId(?string $value = null): self
+ {
+ $this->compilerId = $value;
+ $this->_setField('compilerId');
+ return $this;
+ }
+
+ /**
+ * @return string
+ */
+ public function __toString(): string
+ {
+ return $this->toJson();
+ }
+}
diff --git a/src/Types/NestedFolder.php b/src/Types/NestedFolder.php
new file mode 100644
index 00000000..12f9aafb
--- /dev/null
+++ b/src/Types/NestedFolder.php
@@ -0,0 +1,79 @@
+ $members
+ */
+ #[JsonProperty('members'), ArrayType(['string'])]
+ private array $members;
+
+ /**
+ * @param array{
+ * name: string,
+ * members: array,
+ * } $values
+ */
+ public function __construct(
+ array $values,
+ ) {
+ $this->name = $values['name'];
+ $this->members = $values['members'];
+ }
+
+ /**
+ * @return string
+ */
+ public function getName(): string
+ {
+ return $this->name;
+ }
+
+ /**
+ * @param string $value
+ */
+ public function setName(string $value): self
+ {
+ $this->name = $value;
+ $this->_setField('name');
+ return $this;
+ }
+
+ /**
+ * @return array
+ */
+ public function getMembers(): array
+ {
+ return $this->members;
+ }
+
+ /**
+ * @param array $value
+ */
+ public function setMembers(array $value): self
+ {
+ $this->members = $value;
+ $this->_setField('members');
+ return $this;
+ }
+
+ /**
+ * @return string
+ */
+ public function __toString(): string
+ {
+ return $this->toJson();
+ }
+}
diff --git a/src/Types/Query.php b/src/Types/Query.php
new file mode 100644
index 00000000..c99e37f7
--- /dev/null
+++ b/src/Types/Query.php
@@ -0,0 +1,382 @@
+ $measures
+ */
+ #[JsonProperty('measures'), ArrayType(['string'])]
+ private ?array $measures;
+
+ /**
+ * @var ?array $dimensions
+ */
+ #[JsonProperty('dimensions'), ArrayType(['string'])]
+ private ?array $dimensions;
+
+ /**
+ * @var ?array $segments
+ */
+ #[JsonProperty('segments'), ArrayType(['string'])]
+ private ?array $segments;
+
+ /**
+ * @var ?array $timeDimensions
+ */
+ #[JsonProperty('timeDimensions'), ArrayType([TimeDimension::class])]
+ private ?array $timeDimensions;
+
+ /**
+ * @var ?array> $order
+ */
+ #[JsonProperty('order'), ArrayType([['string']])]
+ private ?array $order;
+
+ /**
+ * @var ?int $limit
+ */
+ #[JsonProperty('limit')]
+ private ?int $limit;
+
+ /**
+ * @var ?int $offset
+ */
+ #[JsonProperty('offset')]
+ private ?int $offset;
+
+ /**
+ * @var ?array<(
+ * QueryFilterCondition
+ * |QueryFilterOr
+ * |QueryFilterAnd
+ * )> $filters
+ */
+ #[JsonProperty('filters'), ArrayType([new Union(QueryFilterCondition::class, QueryFilterOr::class, QueryFilterAnd::class)])]
+ private ?array $filters;
+
+ /**
+ * @var ?bool $ungrouped
+ */
+ #[JsonProperty('ungrouped')]
+ private ?bool $ungrouped;
+
+ /**
+ * @var ?array $subqueryJoins
+ */
+ #[JsonProperty('subqueryJoins'), ArrayType([JoinSubquery::class])]
+ private ?array $subqueryJoins;
+
+ /**
+ * @var ?array> $joinHints
+ */
+ #[JsonProperty('joinHints'), ArrayType([['string']])]
+ private ?array $joinHints;
+
+ /**
+ * @var ?string $timezone
+ */
+ #[JsonProperty('timezone')]
+ private ?string $timezone;
+
+ /**
+ * @var ?value-of $responseFormat
+ */
+ #[JsonProperty('responseFormat')]
+ private ?string $responseFormat;
+
+ /**
+ * @param array{
+ * measures?: ?array,
+ * dimensions?: ?array,
+ * segments?: ?array,
+ * timeDimensions?: ?array,
+ * order?: ?array>,
+ * limit?: ?int,
+ * offset?: ?int,
+ * filters?: ?array<(
+ * QueryFilterCondition
+ * |QueryFilterOr
+ * |QueryFilterAnd
+ * )>,
+ * ungrouped?: ?bool,
+ * subqueryJoins?: ?array,
+ * joinHints?: ?array>,
+ * timezone?: ?string,
+ * responseFormat?: ?value-of,
+ * } $values
+ */
+ public function __construct(
+ array $values = [],
+ ) {
+ $this->measures = $values['measures'] ?? null;
+ $this->dimensions = $values['dimensions'] ?? null;
+ $this->segments = $values['segments'] ?? null;
+ $this->timeDimensions = $values['timeDimensions'] ?? null;
+ $this->order = $values['order'] ?? null;
+ $this->limit = $values['limit'] ?? null;
+ $this->offset = $values['offset'] ?? null;
+ $this->filters = $values['filters'] ?? null;
+ $this->ungrouped = $values['ungrouped'] ?? null;
+ $this->subqueryJoins = $values['subqueryJoins'] ?? null;
+ $this->joinHints = $values['joinHints'] ?? null;
+ $this->timezone = $values['timezone'] ?? null;
+ $this->responseFormat = $values['responseFormat'] ?? null;
+ }
+
+ /**
+ * @return ?array
+ */
+ public function getMeasures(): ?array
+ {
+ return $this->measures;
+ }
+
+ /**
+ * @param ?array $value
+ */
+ public function setMeasures(?array $value = null): self
+ {
+ $this->measures = $value;
+ $this->_setField('measures');
+ return $this;
+ }
+
+ /**
+ * @return ?array
+ */
+ public function getDimensions(): ?array
+ {
+ return $this->dimensions;
+ }
+
+ /**
+ * @param ?array $value
+ */
+ public function setDimensions(?array $value = null): self
+ {
+ $this->dimensions = $value;
+ $this->_setField('dimensions');
+ return $this;
+ }
+
+ /**
+ * @return ?array
+ */
+ public function getSegments(): ?array
+ {
+ return $this->segments;
+ }
+
+ /**
+ * @param ?array $value
+ */
+ public function setSegments(?array $value = null): self
+ {
+ $this->segments = $value;
+ $this->_setField('segments');
+ return $this;
+ }
+
+ /**
+ * @return ?array
+ */
+ public function getTimeDimensions(): ?array
+ {
+ return $this->timeDimensions;
+ }
+
+ /**
+ * @param ?array $value
+ */
+ public function setTimeDimensions(?array $value = null): self
+ {
+ $this->timeDimensions = $value;
+ $this->_setField('timeDimensions');
+ return $this;
+ }
+
+ /**
+ * @return ?array>
+ */
+ public function getOrder(): ?array
+ {
+ return $this->order;
+ }
+
+ /**
+ * @param ?array> $value
+ */
+ public function setOrder(?array $value = null): self
+ {
+ $this->order = $value;
+ $this->_setField('order');
+ return $this;
+ }
+
+ /**
+ * @return ?int
+ */
+ public function getLimit(): ?int
+ {
+ return $this->limit;
+ }
+
+ /**
+ * @param ?int $value
+ */
+ public function setLimit(?int $value = null): self
+ {
+ $this->limit = $value;
+ $this->_setField('limit');
+ return $this;
+ }
+
+ /**
+ * @return ?int
+ */
+ public function getOffset(): ?int
+ {
+ return $this->offset;
+ }
+
+ /**
+ * @param ?int $value
+ */
+ public function setOffset(?int $value = null): self
+ {
+ $this->offset = $value;
+ $this->_setField('offset');
+ return $this;
+ }
+
+ /**
+ * @return ?array<(
+ * QueryFilterCondition
+ * |QueryFilterOr
+ * |QueryFilterAnd
+ * )>
+ */
+ public function getFilters(): ?array
+ {
+ return $this->filters;
+ }
+
+ /**
+ * @param ?array<(
+ * QueryFilterCondition
+ * |QueryFilterOr
+ * |QueryFilterAnd
+ * )> $value
+ */
+ public function setFilters(?array $value = null): self
+ {
+ $this->filters = $value;
+ $this->_setField('filters');
+ return $this;
+ }
+
+ /**
+ * @return ?bool
+ */
+ public function getUngrouped(): ?bool
+ {
+ return $this->ungrouped;
+ }
+
+ /**
+ * @param ?bool $value
+ */
+ public function setUngrouped(?bool $value = null): self
+ {
+ $this->ungrouped = $value;
+ $this->_setField('ungrouped');
+ return $this;
+ }
+
+ /**
+ * @return ?array
+ */
+ public function getSubqueryJoins(): ?array
+ {
+ return $this->subqueryJoins;
+ }
+
+ /**
+ * @param ?array $value
+ */
+ public function setSubqueryJoins(?array $value = null): self
+ {
+ $this->subqueryJoins = $value;
+ $this->_setField('subqueryJoins');
+ return $this;
+ }
+
+ /**
+ * @return ?array>
+ */
+ public function getJoinHints(): ?array
+ {
+ return $this->joinHints;
+ }
+
+ /**
+ * @param ?array> $value
+ */
+ public function setJoinHints(?array $value = null): self
+ {
+ $this->joinHints = $value;
+ $this->_setField('joinHints');
+ return $this;
+ }
+
+ /**
+ * @return ?string
+ */
+ public function getTimezone(): ?string
+ {
+ return $this->timezone;
+ }
+
+ /**
+ * @param ?string $value
+ */
+ public function setTimezone(?string $value = null): self
+ {
+ $this->timezone = $value;
+ $this->_setField('timezone');
+ return $this;
+ }
+
+ /**
+ * @return ?value-of
+ */
+ public function getResponseFormat(): ?string
+ {
+ return $this->responseFormat;
+ }
+
+ /**
+ * @param ?value-of $value
+ */
+ public function setResponseFormat(?string $value = null): self
+ {
+ $this->responseFormat = $value;
+ $this->_setField('responseFormat');
+ return $this;
+ }
+
+ /**
+ * @return string
+ */
+ public function __toString(): string
+ {
+ return $this->toJson();
+ }
+}
diff --git a/src/Types/QueryFilterAnd.php b/src/Types/QueryFilterAnd.php
new file mode 100644
index 00000000..e00d55ca
--- /dev/null
+++ b/src/Types/QueryFilterAnd.php
@@ -0,0 +1,53 @@
+> $and
+ */
+ #[JsonProperty('and'), ArrayType([['string' => 'mixed']])]
+ private ?array $and;
+
+ /**
+ * @param array{
+ * and?: ?array>,
+ * } $values
+ */
+ public function __construct(
+ array $values = [],
+ ) {
+ $this->and = $values['and'] ?? null;
+ }
+
+ /**
+ * @return ?array>
+ */
+ public function getAnd(): ?array
+ {
+ return $this->and;
+ }
+
+ /**
+ * @param ?array> $value
+ */
+ public function setAnd(?array $value = null): self
+ {
+ $this->and = $value;
+ $this->_setField('and');
+ return $this;
+ }
+
+ /**
+ * @return string
+ */
+ public function __toString(): string
+ {
+ return $this->toJson();
+ }
+}
diff --git a/src/Types/QueryFilterCondition.php b/src/Types/QueryFilterCondition.php
new file mode 100644
index 00000000..156ee98c
--- /dev/null
+++ b/src/Types/QueryFilterCondition.php
@@ -0,0 +1,105 @@
+ $values
+ */
+ #[JsonProperty('values'), ArrayType(['string'])]
+ private ?array $values;
+
+ /**
+ * @param array{
+ * member?: ?string,
+ * operator?: ?string,
+ * values?: ?array,
+ * } $values
+ */
+ public function __construct(
+ array $values = [],
+ ) {
+ $this->member = $values['member'] ?? null;
+ $this->operator = $values['operator'] ?? null;
+ $this->values = $values['values'] ?? null;
+ }
+
+ /**
+ * @return ?string
+ */
+ public function getMember(): ?string
+ {
+ return $this->member;
+ }
+
+ /**
+ * @param ?string $value
+ */
+ public function setMember(?string $value = null): self
+ {
+ $this->member = $value;
+ $this->_setField('member');
+ return $this;
+ }
+
+ /**
+ * @return ?string
+ */
+ public function getOperator(): ?string
+ {
+ return $this->operator;
+ }
+
+ /**
+ * @param ?string $value
+ */
+ public function setOperator(?string $value = null): self
+ {
+ $this->operator = $value;
+ $this->_setField('operator');
+ return $this;
+ }
+
+ /**
+ * @return ?array
+ */
+ public function getValues(): ?array
+ {
+ return $this->values;
+ }
+
+ /**
+ * @param ?array $value
+ */
+ public function setValues(?array $value = null): self
+ {
+ $this->values = $value;
+ $this->_setField('values');
+ return $this;
+ }
+
+ /**
+ * @return string
+ */
+ public function __toString(): string
+ {
+ return $this->toJson();
+ }
+}
diff --git a/src/Types/QueryFilterOr.php b/src/Types/QueryFilterOr.php
new file mode 100644
index 00000000..0b4dd018
--- /dev/null
+++ b/src/Types/QueryFilterOr.php
@@ -0,0 +1,53 @@
+> $or
+ */
+ #[JsonProperty('or'), ArrayType([['string' => 'mixed']])]
+ private ?array $or;
+
+ /**
+ * @param array{
+ * or?: ?array>,
+ * } $values
+ */
+ public function __construct(
+ array $values = [],
+ ) {
+ $this->or = $values['or'] ?? null;
+ }
+
+ /**
+ * @return ?array>
+ */
+ public function getOr(): ?array
+ {
+ return $this->or;
+ }
+
+ /**
+ * @param ?array> $value
+ */
+ public function setOr(?array $value = null): self
+ {
+ $this->or = $value;
+ $this->_setField('or');
+ return $this;
+ }
+
+ /**
+ * @return string
+ */
+ public function __toString(): string
+ {
+ return $this->toJson();
+ }
+}
diff --git a/src/Types/ReportingError.php b/src/Types/ReportingError.php
new file mode 100644
index 00000000..711e71b4
--- /dev/null
+++ b/src/Types/ReportingError.php
@@ -0,0 +1,55 @@
+error = $values['error'];
+ }
+
+ /**
+ * @return string
+ */
+ public function getError(): string
+ {
+ return $this->error;
+ }
+
+ /**
+ * @param string $value
+ */
+ public function setError(string $value): self
+ {
+ $this->error = $value;
+ $this->_setField('error');
+ return $this;
+ }
+
+ /**
+ * @return string
+ */
+ public function __toString(): string
+ {
+ return $this->toJson();
+ }
+}
diff --git a/src/Types/ResponseFormat.php b/src/Types/ResponseFormat.php
new file mode 100644
index 00000000..d3ea513b
--- /dev/null
+++ b/src/Types/ResponseFormat.php
@@ -0,0 +1,10 @@
+ $meta
+ */
+ #[JsonProperty('meta'), ArrayType(['string' => 'mixed'])]
+ private ?array $meta;
+
+ /**
+ * @param array{
+ * name: string,
+ * title: string,
+ * shortTitle: string,
+ * description?: ?string,
+ * meta?: ?array,
+ * } $values
+ */
+ public function __construct(
+ array $values,
+ ) {
+ $this->name = $values['name'];
+ $this->title = $values['title'];
+ $this->description = $values['description'] ?? null;
+ $this->shortTitle = $values['shortTitle'];
+ $this->meta = $values['meta'] ?? null;
+ }
+
+ /**
+ * @return string
+ */
+ public function getName(): string
+ {
+ return $this->name;
+ }
+
+ /**
+ * @param string $value
+ */
+ public function setName(string $value): self
+ {
+ $this->name = $value;
+ $this->_setField('name');
+ return $this;
+ }
+
+ /**
+ * @return string
+ */
+ public function getTitle(): string
+ {
+ return $this->title;
+ }
+
+ /**
+ * @param string $value
+ */
+ public function setTitle(string $value): self
+ {
+ $this->title = $value;
+ $this->_setField('title');
+ return $this;
+ }
+
+ /**
+ * @return ?string
+ */
+ public function getDescription(): ?string
+ {
+ return $this->description;
+ }
+
+ /**
+ * @param ?string $value
+ */
+ public function setDescription(?string $value = null): self
+ {
+ $this->description = $value;
+ $this->_setField('description');
+ return $this;
+ }
+
+ /**
+ * @return string
+ */
+ public function getShortTitle(): string
+ {
+ return $this->shortTitle;
+ }
+
+ /**
+ * @param string $value
+ */
+ public function setShortTitle(string $value): self
+ {
+ $this->shortTitle = $value;
+ $this->_setField('shortTitle');
+ return $this;
+ }
+
+ /**
+ * @return ?array
+ */
+ public function getMeta(): ?array
+ {
+ return $this->meta;
+ }
+
+ /**
+ * @param ?array $value
+ */
+ public function setMeta(?array $value = null): self
+ {
+ $this->meta = $value;
+ $this->_setField('meta');
+ return $this;
+ }
+
+ /**
+ * @return string
+ */
+ public function __toString(): string
+ {
+ return $this->toJson();
+ }
+}
diff --git a/src/Types/SimpleFormat.php b/src/Types/SimpleFormat.php
new file mode 100644
index 00000000..3d534723
--- /dev/null
+++ b/src/Types/SimpleFormat.php
@@ -0,0 +1,13 @@
+ $dateRange
+ */
+ #[JsonProperty('dateRange'), ArrayType(['string' => 'mixed'])]
+ private ?array $dateRange;
+
+ /**
+ * @param array{
+ * dimension: string,
+ * granularity?: ?string,
+ * dateRange?: ?array,
+ * } $values
+ */
+ public function __construct(
+ array $values,
+ ) {
+ $this->dimension = $values['dimension'];
+ $this->granularity = $values['granularity'] ?? null;
+ $this->dateRange = $values['dateRange'] ?? null;
+ }
+
+ /**
+ * @return string
+ */
+ public function getDimension(): string
+ {
+ return $this->dimension;
+ }
+
+ /**
+ * @param string $value
+ */
+ public function setDimension(string $value): self
+ {
+ $this->dimension = $value;
+ $this->_setField('dimension');
+ return $this;
+ }
+
+ /**
+ * @return ?string
+ */
+ public function getGranularity(): ?string
+ {
+ return $this->granularity;
+ }
+
+ /**
+ * @param ?string $value
+ */
+ public function setGranularity(?string $value = null): self
+ {
+ $this->granularity = $value;
+ $this->_setField('granularity');
+ return $this;
+ }
+
+ /**
+ * @return ?array
+ */
+ public function getDateRange(): ?array
+ {
+ return $this->dateRange;
+ }
+
+ /**
+ * @param ?array $value
+ */
+ public function setDateRange(?array $value = null): self
+ {
+ $this->dateRange = $value;
+ $this->_setField('dateRange');
+ return $this;
+ }
+
+ /**
+ * @return string
+ */
+ public function __toString(): string
+ {
+ return $this->toJson();
+ }
+}
diff --git a/src/Utils/ReportingHelper.php b/src/Utils/ReportingHelper.php
new file mode 100644
index 00000000..7b19b7a9
--- /dev/null
+++ b/src/Utils/ReportingHelper.php
@@ -0,0 +1,149 @@
+load()` raises a `TypeError` while deserializing it rather than
+ * returning a populated object. That `TypeError` — distinct from the
+ * `SquareApiException` / `SquareException` raised for real API and transport
+ * failures, which are allowed to propagate — is the retry signal. (Should the
+ * `LoadResponse` schema ever make `results` optional, the sentinel would instead
+ * survive as an unmapped `error` property; the helper checks for that case too.)
+ */
+class ReportingHelper
+{
+ /**
+ * Sentinel value returned by the Reporting API on an HTTP 200 while a
+ * `/reporting/v1/load` query is still processing. It is NOT an error — the
+ * request should be retried.
+ */
+ private const CONTINUE_WAIT = 'Continue wait';
+
+ /**
+ * Runs a reporting query and transparently polls until it resolves, returning
+ * the final {@see LoadResponse}. Re-sends the identical request with exponential
+ * backoff while the API answers "Continue wait".
+ *
+ * @param SquareClient $client The configured Square client.
+ * @param LoadRequest $request The reporting query (same shape as `reporting->load`).
+ * @param ?array{
+ * maxAttempts?: int,
+ * initialDelayMs?: int,
+ * maxDelayMs?: int,
+ * backoffFactor?: float,
+ * shouldCancel?: callable(): bool,
+ * requestOptions?: array{
+ * baseUrl?: string,
+ * maxRetries?: int,
+ * timeout?: float,
+ * headers?: array,
+ * queryParameters?: array,
+ * bodyProperties?: array,
+ * },
+ * } $options Polling/backoff configuration:
+ * - `maxAttempts` Maximum poll attempts before giving up. Default 20.
+ * - `initialDelayMs` Delay before the first retry, in ms. Default 2000.
+ * - `maxDelayMs` Upper bound on the backoff delay, in ms. Default 20000.
+ * - `backoffFactor` Multiplier applied to the delay after each attempt. Default 2.
+ * - `shouldCancel` Predicate polled before each attempt and during the
+ * backoff wait; aborts the loop when it returns `true`.
+ * - `requestOptions` Forwarded to each underlying `reporting->load` call.
+ * @return LoadResponse The resolved response (never the "Continue wait" sentinel).
+ * @throws SquareException If the query does not resolve within `maxAttempts`, or if cancelled.
+ */
+ public static function loadAndWait(
+ SquareClient $client,
+ LoadRequest $request = new LoadRequest(),
+ ?array $options = null,
+ ): LoadResponse {
+ $options ??= [];
+ $maxAttempts = $options['maxAttempts'] ?? 20;
+ $initialDelayMs = $options['initialDelayMs'] ?? 2000;
+ $maxDelayMs = $options['maxDelayMs'] ?? 20000;
+ $backoffFactor = $options['backoffFactor'] ?? 2;
+ $shouldCancel = $options['shouldCancel'] ?? null;
+ $requestOptions = $options['requestOptions'] ?? null;
+
+ $delayMs = $initialDelayMs;
+ for ($attempt = 1; $attempt <= $maxAttempts; $attempt++) {
+ if ($shouldCancel !== null && $shouldCancel()) {
+ throw new SquareException(message: 'Reporting query polling was cancelled.');
+ }
+
+ try {
+ $response = $client->reporting->load($request, $requestOptions);
+ if (!self::isContinueWait($response)) {
+ return $response;
+ }
+ } catch (TypeError) {
+ // The still-processing "Continue wait" body omits the required
+ // `results` field, so the generated deserializer raises a TypeError.
+ // Real API/transport failures raise SquareApiException/SquareException
+ // and are intentionally left to propagate.
+ }
+
+ if ($attempt === $maxAttempts) {
+ break;
+ }
+ self::sleep($delayMs, $shouldCancel);
+ $delayMs = (int) min($delayMs * $backoffFactor, $maxDelayMs);
+ }
+
+ throw new SquareException(
+ message: sprintf(
+ 'Reporting query did not complete after %d attempts ("%s").',
+ $maxAttempts,
+ self::CONTINUE_WAIT,
+ ),
+ );
+ }
+
+ /**
+ * Forward-compatible sentinel check: if the generated `LoadResponse` ever makes
+ * `results` optional, a "Continue wait" body would deserialize successfully with
+ * the unmapped `error` field preserved as an additional property. Treat that as
+ * a retry signal rather than a result.
+ */
+ private static function isContinueWait(LoadResponse $response): bool
+ {
+ return ($response->getAdditionalProperties()['error'] ?? null) === self::CONTINUE_WAIT;
+ }
+
+ /**
+ * Sleeps for the given number of milliseconds, polling `$shouldCancel` in small
+ * slices so cancellation stays responsive during a long backoff wait.
+ *
+ * @param ?callable(): bool $shouldCancel
+ * @throws SquareException If cancelled mid-wait.
+ */
+ private static function sleep(int $ms, ?callable $shouldCancel): void
+ {
+ $sliceMs = 100;
+ $remaining = $ms;
+ while ($remaining > 0) {
+ if ($shouldCancel !== null && $shouldCancel()) {
+ throw new SquareException(message: 'Reporting query polling was cancelled.');
+ }
+ $chunk = min($sliceMs, $remaining);
+ usleep($chunk * 1000);
+ $remaining -= $chunk;
+ }
+ }
+}
diff --git a/tests/Integration/ReportingHelperTest.php b/tests/Integration/ReportingHelperTest.php
new file mode 100644
index 00000000..82b933fd
--- /dev/null
+++ b/tests/Integration/ReportingHelperTest.php
@@ -0,0 +1,163 @@
+ [
+ new LoadResult([
+ 'annotation' => new LoadResultAnnotation([
+ 'measures' => [],
+ 'dimensions' => [],
+ 'segments' => [],
+ 'timeDimensions' => [],
+ ]),
+ 'data' => [['Orders.count' => '128']],
+ ]),
+ ],
+ ]);
+ }
+
+ /**
+ * Builds a SquareClient whose `reporting->load` returns the next scripted entry
+ * (the last entry repeats once exhausted), recording the call count. A string
+ * entry is deserialized via the real `LoadResponse::fromJson`; a `LoadResponse`
+ * entry is returned as-is.
+ *
+ * @param array $sequence One entry per expected `load` call.
+ * @param int &$callCount Receives the number of `load` invocations.
+ */
+ private function clientReturning(array $sequence, int &$callCount): SquareClient
+ {
+ $callCount = 0;
+ $reporting = $this->getMockBuilder(ReportingClient::class)
+ ->disableOriginalConstructor()
+ ->onlyMethods(['load'])
+ ->getMock();
+ $reporting->method('load')->willReturnCallback(
+ function () use ($sequence, &$callCount): LoadResponse {
+ $entry = $sequence[min($callCount, count($sequence) - 1)];
+ $callCount++;
+ // Real deserialization for the sentinel: a "Continue wait" body raises
+ // a TypeError here, exactly as the generated client does in production.
+ return is_string($entry) ? LoadResponse::fromJson($entry) : $entry;
+ }
+ );
+
+ $client = new SquareClient('test-token');
+ $client->reporting = $reporting;
+ return $client;
+ }
+
+ public function testPollsPastContinueWaitAndReturnsResolvedResult(): void
+ {
+ $callCount = 0;
+ $client = $this->clientReturning(
+ [self::CONTINUE_WAIT_BODY, self::CONTINUE_WAIT_BODY, self::resolvedResponse()],
+ $callCount,
+ );
+
+ $response = ReportingHelper::loadAndWait(
+ $client,
+ new LoadRequest(),
+ ['initialDelayMs' => 1, 'maxDelayMs' => 1, 'maxAttempts' => 5],
+ );
+
+ $this->assertCount(1, $response->getResults());
+ $this->assertArrayNotHasKey('error', $response->getAdditionalProperties());
+ $this->assertSame(3, $callCount);
+ }
+
+ public function testReturnsImmediatelyWhenFirstResponseHasResults(): void
+ {
+ $callCount = 0;
+ $client = $this->clientReturning([self::resolvedResponse()], $callCount);
+
+ $response = ReportingHelper::loadAndWait($client, new LoadRequest(), ['initialDelayMs' => 1]);
+
+ $this->assertCount(1, $response->getResults());
+ $this->assertSame(1, $callCount);
+ }
+
+ public function testThrowsOnceMaxAttemptsExhausted(): void
+ {
+ $callCount = 0;
+ $client = $this->clientReturning([self::CONTINUE_WAIT_BODY], $callCount); // never resolves
+
+ try {
+ ReportingHelper::loadAndWait(
+ $client,
+ new LoadRequest(),
+ ['initialDelayMs' => 1, 'maxDelayMs' => 1, 'maxAttempts' => 3],
+ );
+ $this->fail('Expected SquareException was not thrown.');
+ } catch (SquareException $e) {
+ $this->assertStringContainsString('did not complete after 3 attempts', $e->getMessage());
+ }
+ $this->assertSame(3, $callCount);
+ }
+
+ public function testCancellationAbortsPolling(): void
+ {
+ $callCount = 0;
+ $client = $this->clientReturning([self::CONTINUE_WAIT_BODY], $callCount); // would otherwise poll
+
+ $this->expectException(SquareException::class);
+ $this->expectExceptionMessage('cancelled');
+
+ ReportingHelper::loadAndWait(
+ $client,
+ new LoadRequest(),
+ ['maxAttempts' => 10, 'shouldCancel' => fn (): bool => true],
+ );
+ }
+
+ /**
+ * The crux of the design: the generated `reporting->load` deserializes the
+ * "Continue wait" body into a TypeError (the non-nullable `results` is absent).
+ * That raised TypeError is how `loadAndWait` recognizes the sentinel; if it ever
+ * stops throwing, the helper would mistake "Continue wait" for a result.
+ */
+ public function testContinueWaitBodyRaisesTypeErrorOnDeserialization(): void
+ {
+ $this->expectException(TypeError::class);
+ LoadResponse::fromJson(self::CONTINUE_WAIT_BODY);
+ }
+
+ /** A real (here, empty) result body deserializes cleanly, with no sentinel. */
+ public function testResolvedBodyDeserializesCleanly(): void
+ {
+ $resolved = LoadResponse::fromJson('{"results":[]}');
+ $this->assertSame([], $resolved->getResults());
+ $this->assertArrayNotHasKey('error', $resolved->getAdditionalProperties());
+ }
+}
diff --git a/tests/Integration/ReportingTest.php b/tests/Integration/ReportingTest.php
new file mode 100644
index 00000000..1fddcfa9
--- /dev/null
+++ b/tests/Integration/ReportingTest.php
@@ -0,0 +1,63 @@
+`).
+ */
+class ReportingTest extends TestCase
+{
+ private static SquareClient $client;
+
+ public static function setUpBeforeClass(): void
+ {
+ $token = getenv('TEST_SQUARE_REPORTING');
+ if (!$token) {
+ self::markTestSkipped('Set TEST_SQUARE_REPORTING= to run the Reporting API live tests against production.');
+ }
+
+ self::$client = new SquareClient(
+ $token,
+ null,
+ ['baseUrl' => Environments::Production->value],
+ );
+ }
+
+ public function testGetMetadataReturnsSchema(): void
+ {
+ $metadata = self::$client->reporting->getMetadata();
+ $this->assertInstanceOf(MetadataResponse::class, $metadata);
+ }
+
+ public function testLoadAndWaitResolvesQuery(): void
+ {
+ $response = ReportingHelper::loadAndWait(
+ self::$client,
+ new LoadRequest([
+ 'query' => new Query([
+ 'measures' => ['Orders.count'],
+ ]),
+ ]),
+ );
+
+ $this->assertInstanceOf(LoadResponse::class, $response);
+ $this->assertIsArray($response->getResults());
+ }
+}