diff --git a/composer.json b/composer.json
index 2d60363..a94d1ce 100644
--- a/composer.json
+++ b/composer.json
@@ -33,7 +33,14 @@
"require-dev": {
"phpunit/phpunit": "^9.6.22",
"rector/rector": "^2.0",
- "phpstan/phpstan": "^2.1"
+ "phpstan/phpstan": "^2.1",
+ "symfony/yaml": "^7|^8",
+ "symfony/messenger": "^7.4",
+ "symfony/security-bundle": "^7.4",
+ "symfony/uid": "^7.4",
+ "symfony/string": "^7.4",
+ "doctrine/orm": "^3.6",
+ "doctrine/doctrine-bundle": "^2.18"
},
"scripts": {
"test": "vendor/bin/phpunit",
diff --git a/phpstan.neon b/phpstan.neon
index 9e92b3d..d33da4a 100644
--- a/phpstan.neon
+++ b/phpstan.neon
@@ -1,6 +1,3 @@
-includes:
- - phpstan-baseline.neon
-
parameters:
level: max
paths:
@@ -14,6 +11,3 @@ parameters:
ignoreErrors:
- identifier: missingType.iterableValue
- - identifier: return.unusedType
- - identifier: staticMethod.void
- - identifier: staticMethod.alreadyNarrowedType
diff --git a/phpunit.xml.dist b/phpunit.xml.dist
new file mode 100644
index 0000000..20a66cf
--- /dev/null
+++ b/phpunit.xml.dist
@@ -0,0 +1,30 @@
+
+
+
+
+ tests
+
+
+
+
+
+ src
+
+
+ src/DependencyInjection
+ src/Resources
+ src/Enums
+ src/Actions/Dto
+ src/Provisioning/Dto
+ src/Provisioning/ValueObject
+ src/Message
+
+
+
diff --git a/src/ActionService.php b/src/ActionService.php
index 8669886..ed14c4c 100644
--- a/src/ActionService.php
+++ b/src/ActionService.php
@@ -16,6 +16,7 @@ public function __construct(
/**
* @param class-string $actionClass
+ * @param array $input
*
* @throws ZabbixApiException
*/
@@ -32,6 +33,13 @@ public function call(string $actionClass, array $input): mixed
$method = $input['method'] ?? 'get';
$params = $input['params'] ?? $input;
+ if (!is_string($method)) {
+ throw new ZabbixApiException(
+ 'Method must be a string',
+ -1,
+ );
+ }
+
$actionString = sprintf('%s.%s', $base, $method);
try {
@@ -45,6 +53,20 @@ public function call(string $actionClass, array $input): mixed
);
}
- return $this->zabbixClient->call($action, $params);
+ if (!is_array($params)) {
+ throw new ZabbixApiException(
+ 'Params must be an array',
+ -1,
+ );
+ }
+
+ $typedParams = [];
+ foreach ($params as $key => $value) {
+ if (is_string($key)) {
+ $typedParams[$key] = $value;
+ }
+ }
+
+ return $this->zabbixClient->call($action, $typedParams);
}
}
diff --git a/src/ActionServiceInterface.php b/src/ActionServiceInterface.php
index ddc8af2..ced11fc 100644
--- a/src/ActionServiceInterface.php
+++ b/src/ActionServiceInterface.php
@@ -7,6 +7,8 @@
interface ActionServiceInterface
{
/**
+ * @param array $input
+ *
* @throws ZabbixApiException
*/
public function call(string $actionClass, array $input): mixed;
diff --git a/src/Actions/Action.php b/src/Actions/Action.php
index fe9c0c9..981a67b 100644
--- a/src/Actions/Action.php
+++ b/src/Actions/Action.php
@@ -16,6 +16,7 @@
use BytesCommerce\ZabbixApi\Actions\Dto\GetActionDto;
use BytesCommerce\ZabbixApi\Enums\OutputEnum;
use BytesCommerce\ZabbixApi\Enums\ZabbixAction;
+use Webmozart\Assert\Assert;
final class Action extends AbstractAction
{
@@ -32,6 +33,7 @@ public function get(GetActionDto $dto): GetActionResponseDto
}
$result = $this->client->call(ZabbixAction::ACTION_GET, $params);
+ Assert::isArray($result);
return GetActionResponseDto::fromArray($result);
}
@@ -40,7 +42,10 @@ public function create(CreateActionDto $dto): CreateActionResponseDto
{
$actions = array_map($this->mapCreateAction(...), $dto->actions);
- $result = $this->client->call(ZabbixAction::ACTION_CREATE, $actions);
+ $result = $this->client->call(ZabbixAction::ACTION_CREATE, ['actions' => $actions]);
+ Assert::isArray($result);
+ Assert::keyExists($result, 'actionids');
+ Assert::isArray($result['actionids']);
return new CreateActionResponseDto($result['actionids']);
}
@@ -49,7 +54,10 @@ public function update(UpdateActionDto $dto): UpdateActionResponseDto
{
$actions = array_map($this->mapUpdateAction(...), $dto->actions);
- $result = $this->client->call(ZabbixAction::ACTION_UPDATE, $actions);
+ $result = $this->client->call(ZabbixAction::ACTION_UPDATE, ['actions' => $actions]);
+ Assert::isArray($result);
+ Assert::keyExists($result, 'actionids');
+ Assert::isArray($result['actionids']);
return new UpdateActionResponseDto($result['actionids']);
}
@@ -61,6 +69,9 @@ public function delete(DeleteActionDto $dto): DeleteActionResponseDto
return new DeleteActionResponseDto();
}
+ /**
+ * @return array
+ */
private function mapCreateAction(CreateSingleActionDto $action): array
{
$data = [
@@ -96,6 +107,9 @@ private function mapCreateAction(CreateSingleActionDto $action): array
return $data;
}
+ /**
+ * @return array
+ */
private function mapUpdateAction(UpdateSingleActionDto $action): array
{
$data = [
diff --git a/src/Actions/Alert.php b/src/Actions/Alert.php
index 229493f..fa0e224 100644
--- a/src/Actions/Alert.php
+++ b/src/Actions/Alert.php
@@ -8,6 +8,7 @@
use BytesCommerce\ZabbixApi\Enums\OutputEnum;
use BytesCommerce\ZabbixApi\Enums\ZabbixAction;
use DateTimeInterface;
+use Webmozart\Assert\Assert;
final class Alert extends AbstractAction
{
@@ -16,15 +17,23 @@ public static function getActionPrefix(): string
return 'alert';
}
+ /**
+ * @param array $params
+ */
public function get(array $params): GetAlertResponseDto
{
$processedParams = $this->processParams($params);
$result = $this->client->call(ZabbixAction::ALERT_GET, $processedParams);
+ Assert::isArray($result);
return GetAlertResponseDto::fromArray($result);
}
+ /**
+ * @param array $params
+ * @return array
+ */
private function processParams(array $params): array
{
if (!isset($params['output'])) {
diff --git a/src/Actions/AuditLog.php b/src/Actions/AuditLog.php
index bd06fd3..1167b78 100644
--- a/src/Actions/AuditLog.php
+++ b/src/Actions/AuditLog.php
@@ -8,6 +8,7 @@
use BytesCommerce\ZabbixApi\Enums\OutputEnum;
use BytesCommerce\ZabbixApi\Enums\ZabbixAction;
use DateTimeInterface;
+use Webmozart\Assert\Assert;
final class AuditLog extends AbstractAction
{
@@ -16,15 +17,23 @@ public static function getActionPrefix(): string
return 'auditlog';
}
+ /**
+ * @param array $params
+ */
public function get(array $params): GetAuditLogResponseDto
{
$processedParams = $this->processParams($params);
$result = $this->client->call(ZabbixAction::AUDITLOG_GET, $processedParams);
+ Assert::isArray($result);
return GetAuditLogResponseDto::fromArray($result);
}
+ /**
+ * @param array $params
+ * @return array
+ */
private function processParams(array $params): array
{
if (!isset($params['output'])) {
diff --git a/src/Actions/Dashboard.php b/src/Actions/Dashboard.php
index d8780cc..7c73894 100644
--- a/src/Actions/Dashboard.php
+++ b/src/Actions/Dashboard.php
@@ -14,6 +14,7 @@
use BytesCommerce\ZabbixApi\Actions\Dto\UpdateDashboardResponseDto;
use BytesCommerce\ZabbixApi\Enums\OutputEnum;
use BytesCommerce\ZabbixApi\Enums\ZabbixAction;
+use Webmozart\Assert\Assert;
final class Dashboard extends AbstractAction
{
@@ -36,20 +37,38 @@ public function get(GetDashboardDto $dto): GetDashboardResponseDto
public function create(CreateDashboardDto $dto): CreateDashboardResponseDto
{
- $params = array_map($this->mapCreateDashboard(...), $dto->dashboards);
+ $params = [];
+ foreach ($dto->dashboards as $dashboard) {
+ $params[] = $this->mapCreateDashboard($dashboard);
+ }
$result = $this->client->call(ZabbixAction::DASHBOARD_CREATE, $params);
+ Assert::isArray($result);
+ Assert::keyExists($result, 'dashboardids');
+ Assert::isList($result['dashboardids']);
+
+ /** @var list $dashboardids */
+ $dashboardids = $result['dashboardids'];
- return new CreateDashboardResponseDto($result['dashboardids']);
+ return new CreateDashboardResponseDto($dashboardids);
}
public function update(UpdateDashboardDto $dto): UpdateDashboardResponseDto
{
- $params = array_map($this->mapUpdateDashboard(...), $dto->dashboards);
+ $params = [];
+ foreach ($dto->dashboards as $dashboard) {
+ $params[] = $this->mapUpdateDashboard($dashboard);
+ }
$result = $this->client->call(ZabbixAction::DASHBOARD_UPDATE, $params);
+ Assert::isArray($result);
+ Assert::keyExists($result, 'dashboardids');
+ Assert::isList($result['dashboardids']);
+
+ /** @var list $dashboardids */
+ $dashboardids = $result['dashboardids'];
- return new UpdateDashboardResponseDto($result['dashboardids']);
+ return new UpdateDashboardResponseDto($dashboardids);
}
public function delete(DeleteDashboardDto $dto): DeleteDashboardResponseDto
@@ -59,6 +78,10 @@ public function delete(DeleteDashboardDto $dto): DeleteDashboardResponseDto
return new DeleteDashboardResponseDto();
}
+ /**
+ * @param array $dashboard
+ * @return array
+ */
private function mapCreateDashboard(array $dashboard): array
{
$data = [
@@ -88,6 +111,10 @@ private function mapCreateDashboard(array $dashboard): array
return $data;
}
+ /**
+ * @param array $dashboard
+ * @return array
+ */
private function mapUpdateDashboard(array $dashboard): array
{
$data = [
diff --git a/src/Actions/Dto/ActionDto.php b/src/Actions/Dto/ActionDto.php
index 67835aa..a999a88 100644
--- a/src/Actions/Dto/ActionDto.php
+++ b/src/Actions/Dto/ActionDto.php
@@ -6,6 +6,7 @@
use BytesCommerce\ZabbixApi\Enums\EventSourceEnum;
use BytesCommerce\ZabbixApi\Enums\StatusEnum;
+use Webmozart\Assert\Assert;
final readonly class ActionDto
{
@@ -24,16 +25,27 @@ public function __construct(
public static function fromArray(array $data): self
{
+ Assert::string($data['actionid'] ?? null);
+ Assert::string($data['name'] ?? null);
+ Assert::integerish($data['eventsource'] ?? null);
+ Assert::string($data['esc_period'] ?? null);
+
+ $status = null;
+ if (isset($data['status'])) {
+ Assert::integerish($data['status']);
+ $status = StatusEnum::from((int) $data['status']);
+ }
+
return new self(
actionid: $data['actionid'],
name: $data['name'],
- eventsource: EventSourceEnum::from($data['eventsource']),
+ eventsource: EventSourceEnum::from((int) $data['eventsource']),
esc_period: $data['esc_period'],
- status: isset($data['status']) ? StatusEnum::from($data['status']) : null,
- filter: $data['filter'] ?? null,
- operations: $data['operations'] ?? null,
- recovery_operations: $data['recovery_operations'] ?? null,
- update_operations: $data['update_operations'] ?? null,
+ status: $status,
+ filter: isset($data['filter']) && is_array($data['filter']) ? $data['filter'] : null,
+ operations: isset($data['operations']) && is_array($data['operations']) ? $data['operations'] : null,
+ recovery_operations: isset($data['recovery_operations']) && is_array($data['recovery_operations']) ? $data['recovery_operations'] : null,
+ update_operations: isset($data['update_operations']) && is_array($data['update_operations']) ? $data['update_operations'] : null,
);
}
diff --git a/src/Actions/Dto/AlertDto.php b/src/Actions/Dto/AlertDto.php
index f181802..a2232e0 100644
--- a/src/Actions/Dto/AlertDto.php
+++ b/src/Actions/Dto/AlertDto.php
@@ -4,6 +4,8 @@
namespace BytesCommerce\ZabbixApi\Actions\Dto;
+use Webmozart\Assert\Assert;
+
final readonly class AlertDto
{
public function __construct(
@@ -27,22 +29,36 @@ public function __construct(
public static function fromArray(array $data): self
{
+ Assert::string($data['alertid'] ?? null);
+ Assert::string($data['actionid'] ?? null);
+ Assert::string($data['eventid'] ?? null);
+ Assert::string($data['userid'] ?? null);
+ Assert::integerish($data['clock'] ?? null);
+ Assert::integerish($data['mediatypeid'] ?? null);
+ Assert::string($data['sendto'] ?? null);
+ Assert::string($data['subject'] ?? null);
+ Assert::string($data['message'] ?? null);
+ Assert::integerish($data['status'] ?? null);
+ Assert::integerish($data['retries'] ?? null);
+ Assert::string($data['error'] ?? null);
+ Assert::integerish($data['esc_step'] ?? null);
+
return new self(
alertid: $data['alertid'],
actionid: $data['actionid'],
eventid: $data['eventid'],
userid: $data['userid'],
- clock: $data['clock'],
- mediatypeid: $data['mediatypeid'],
+ clock: (int) $data['clock'],
+ mediatypeid: (int) $data['mediatypeid'],
sendto: $data['sendto'],
subject: $data['subject'],
message: $data['message'],
- status: $data['status'],
- retries: $data['retries'],
+ status: (int) $data['status'],
+ retries: (int) $data['retries'],
error: $data['error'],
- esc_step: $data['esc_step'],
- alerttype: $data['alerttype'] ?? null,
- p_eventid: $data['p_eventid'] ?? null,
+ esc_step: (int) $data['esc_step'],
+ alerttype: isset($data['alerttype']) && is_string($data['alerttype']) ? $data['alerttype'] : null,
+ p_eventid: isset($data['p_eventid']) && is_string($data['p_eventid']) ? $data['p_eventid'] : null,
);
}
diff --git a/src/Actions/Dto/AuditLogDto.php b/src/Actions/Dto/AuditLogDto.php
index 1174cca..62ee229 100644
--- a/src/Actions/Dto/AuditLogDto.php
+++ b/src/Actions/Dto/AuditLogDto.php
@@ -4,6 +4,8 @@
namespace BytesCommerce\ZabbixApi\Actions\Dto;
+use Webmozart\Assert\Assert;
+
final readonly class AuditLogDto
{
public function __construct(
@@ -22,17 +24,25 @@ public function __construct(
public static function fromArray(array $data): self
{
+ Assert::string($data['auditid'] ?? null);
+ Assert::string($data['userid'] ?? null);
+ Assert::integerish($data['clock'] ?? null);
+ Assert::string($data['action'] ?? null);
+ Assert::string($data['resourcetype'] ?? null);
+ Assert::string($data['resourceid'] ?? null);
+ Assert::string($data['resourcename'] ?? null);
+
return new self(
auditid: $data['auditid'],
userid: $data['userid'],
- clock: $data['clock'],
+ clock: (int) $data['clock'],
action: $data['action'],
resourcetype: $data['resourcetype'],
resourceid: $data['resourceid'],
resourcename: $data['resourcename'],
- details: $data['details'] ?? null,
- ip: $data['ip'] ?? null,
- resource_cuid: $data['resource_cuid'] ?? null,
+ details: isset($data['details']) && is_string($data['details']) ? $data['details'] : null,
+ ip: isset($data['ip']) && is_string($data['ip']) ? $data['ip'] : null,
+ resource_cuid: isset($data['resource_cuid']) && is_string($data['resource_cuid']) ? $data['resource_cuid'] : null,
);
}
diff --git a/src/Actions/Dto/CreateDashboardDto.php b/src/Actions/Dto/CreateDashboardDto.php
index 60185bc..2fa10ba 100644
--- a/src/Actions/Dto/CreateDashboardDto.php
+++ b/src/Actions/Dto/CreateDashboardDto.php
@@ -7,7 +7,7 @@
final readonly class CreateDashboardDto
{
/**
- * @param list, view_mode?: string}>}> $dashboards
+ * @param list> $dashboards
*/
public function __construct(
public array $dashboards,
@@ -15,7 +15,7 @@ public function __construct(
}
/**
- * @return list, view_mode?: string}>}>
+ * @return list>
*/
public function getDashboards(): array
{
diff --git a/src/Actions/Dto/DashboardDto.php b/src/Actions/Dto/DashboardDto.php
index f3320cd..d64a94f 100644
--- a/src/Actions/Dto/DashboardDto.php
+++ b/src/Actions/Dto/DashboardDto.php
@@ -4,6 +4,8 @@
namespace BytesCommerce\ZabbixApi\Actions\Dto;
+use Webmozart\Assert\Assert;
+
final readonly class DashboardDto
{
/**
@@ -22,17 +24,43 @@ public function __construct(
public static function fromArray(array $data): self
{
- $pages = isset($data['pages']) && is_array($data['pages'])
- ? array_map(DashboardPageDto::fromArray(...), $data['pages'])
- : [];
+ Assert::string($data['dashboardid'] ?? null);
+ Assert::string($data['name'] ?? null);
+
+ $pages = [];
+ if (isset($data['pages']) && is_array($data['pages'])) {
+ foreach ($data['pages'] as $pageData) {
+ if (is_array($pageData)) {
+ $pages[] = DashboardPageDto::fromArray($pageData);
+ }
+ }
+ }
+
+ $private = null;
+ if (isset($data['private'])) {
+ Assert::integerish($data['private']);
+ $private = (int) $data['private'];
+ }
+
+ $displayPeriod = null;
+ if (isset($data['display_period'])) {
+ Assert::integerish($data['display_period']);
+ $displayPeriod = (int) $data['display_period'];
+ }
+
+ $autoStart = null;
+ if (isset($data['auto_start'])) {
+ Assert::integerish($data['auto_start']);
+ $autoStart = (int) $data['auto_start'];
+ }
return new self(
dashboardid: $data['dashboardid'],
name: $data['name'],
- private: isset($data['private']) ? (int) $data['private'] : null,
- userid: $data['userid'] ?? null,
- display_period: isset($data['display_period']) ? (int) $data['display_period'] : null,
- auto_start: isset($data['auto_start']) ? (int) $data['auto_start'] : null,
+ private: $private,
+ userid: isset($data['userid']) && is_string($data['userid']) ? $data['userid'] : null,
+ display_period: $displayPeriod,
+ auto_start: $autoStart,
pages: $pages,
);
}
diff --git a/src/Actions/Dto/DashboardPageDto.php b/src/Actions/Dto/DashboardPageDto.php
index 4b7ecae..c87c8a2 100644
--- a/src/Actions/Dto/DashboardPageDto.php
+++ b/src/Actions/Dto/DashboardPageDto.php
@@ -19,14 +19,29 @@ public function __construct(
public static function fromArray(array $data): self
{
- $widgets = isset($data['widgets']) && is_array($data['widgets'])
- ? array_map(DashboardWidgetDto::fromArray(...), $data['widgets'])
- : [];
+ $widgets = [];
+ if (isset($data['widgets']) && is_array($data['widgets'])) {
+ foreach ($data['widgets'] as $widgetData) {
+ if (is_array($widgetData)) {
+ $widgets[] = DashboardWidgetDto::fromArray($widgetData);
+ }
+ }
+ }
+
+ $displayPeriod = null;
+ if (isset($data['display_period'])) {
+ $displayPeriod = is_int($data['display_period']) ? $data['display_period'] : null;
+ }
+
+ $sortorder = null;
+ if (isset($data['sortorder'])) {
+ $sortorder = is_int($data['sortorder']) ? $data['sortorder'] : null;
+ }
return new self(
- name: $data['name'] ?? null,
- display_period: isset($data['display_period']) ? (int) $data['display_period'] : null,
- sortorder: isset($data['sortorder']) ? (int) $data['sortorder'] : null,
+ name: isset($data['name']) && is_string($data['name']) ? $data['name'] : null,
+ display_period: $displayPeriod,
+ sortorder: $sortorder,
widgets: $widgets,
);
}
diff --git a/src/Actions/Dto/DashboardWidgetDto.php b/src/Actions/Dto/DashboardWidgetDto.php
index 98a1120..a802b5b 100644
--- a/src/Actions/Dto/DashboardWidgetDto.php
+++ b/src/Actions/Dto/DashboardWidgetDto.php
@@ -4,6 +4,8 @@
namespace BytesCommerce\ZabbixApi\Actions\Dto;
+use Webmozart\Assert\Assert;
+
final readonly class DashboardWidgetDto
{
/**
@@ -23,18 +25,59 @@ public function __construct(
public static function fromArray(array $data): self
{
+ Assert::string($data['type'] ?? null);
+
+ $x = null;
+ if (isset($data['x'])) {
+ Assert::integerish($data['x']);
+ $x = (int) $data['x'];
+ }
+
+ $y = null;
+ if (isset($data['y'])) {
+ Assert::integerish($data['y']);
+ $y = (int) $data['y'];
+ }
+
+ $width = null;
+ if (isset($data['width'])) {
+ Assert::integerish($data['width']);
+ $width = (int) $data['width'];
+ }
+
+ $height = null;
+ if (isset($data['height'])) {
+ Assert::integerish($data['height']);
+ $height = (int) $data['height'];
+ }
+
return new self(
type: $data['type'],
- name: $data['name'] ?? null,
- x: isset($data['x']) ? (int) $data['x'] : null,
- y: isset($data['y']) ? (int) $data['y'] : null,
- width: isset($data['width']) ? (int) $data['width'] : null,
- height: isset($data['height']) ? (int) $data['height'] : null,
- fields: $data['fields'] ?? null,
- view_mode: $data['view_mode'] ?? null,
+ name: isset($data['name']) && is_string($data['name']) ? $data['name'] : null,
+ x: $x,
+ y: $y,
+ width: $width,
+ height: $height,
+ fields: isset($data['fields']) && is_array($data['fields']) ? self::normalizeFields($data['fields']) : null,
+ view_mode: isset($data['view_mode']) && is_string($data['view_mode']) ? $data['view_mode'] : null,
);
}
+ /**
+ * @param array $fields
+ * @return array
+ */
+ private static function normalizeFields(array $fields): array
+ {
+ $normalized = [];
+ foreach ($fields as $key => $value) {
+ if (is_string($key)) {
+ $normalized[$key] = $value;
+ }
+ }
+ return $normalized;
+ }
+
public function getType(): string
{
return $this->type;
diff --git a/src/Actions/Dto/EventDto.php b/src/Actions/Dto/EventDto.php
index e05dbf2..6b3b9d3 100644
--- a/src/Actions/Dto/EventDto.php
+++ b/src/Actions/Dto/EventDto.php
@@ -4,6 +4,8 @@
namespace BytesCommerce\ZabbixApi\Actions\Dto;
+use Webmozart\Assert\Assert;
+
final readonly class EventDto
{
public function __construct(
@@ -25,20 +27,35 @@ public function __construct(
public static function fromArray(array $data): self
{
+ Assert::string($data['eventid'] ?? null);
+ Assert::integerish($data['source'] ?? null);
+ Assert::integerish($data['object'] ?? null);
+ Assert::integerish($data['objectid'] ?? null);
+ Assert::integerish($data['clock'] ?? null);
+ Assert::integerish($data['value'] ?? null);
+ Assert::integerish($data['acknowledged'] ?? null);
+ Assert::integerish($data['ns'] ?? null);
+
+ $severity = null;
+ if (isset($data['severity'])) {
+ Assert::integerish($data['severity']);
+ $severity = (int) $data['severity'];
+ }
+
return new self(
eventid: $data['eventid'],
- source: $data['source'],
- object: $data['object'],
- objectid: $data['objectid'],
- clock: $data['clock'],
- value: $data['value'],
- acknowledged: $data['acknowledged'],
- ns: $data['ns'],
- name: $data['name'] ?? null,
- severity: $data['severity'] ?? null,
- acknowledges: $data['acknowledges'] ?? null,
- hosts: $data['hosts'] ?? null,
- tags: $data['tags'] ?? null,
+ source: (int) $data['source'],
+ object: (int) $data['object'],
+ objectid: (int) $data['objectid'],
+ clock: (int) $data['clock'],
+ value: (int) $data['value'],
+ acknowledged: (int) $data['acknowledged'],
+ ns: (int) $data['ns'],
+ name: isset($data['name']) && is_string($data['name']) ? $data['name'] : null,
+ severity: $severity,
+ acknowledges: isset($data['acknowledges']) && is_array($data['acknowledges']) ? $data['acknowledges'] : null,
+ hosts: isset($data['hosts']) && is_array($data['hosts']) ? $data['hosts'] : null,
+ tags: isset($data['tags']) && is_array($data['tags']) ? $data['tags'] : null,
);
}
diff --git a/src/Actions/Dto/GetActionResponseDto.php b/src/Actions/Dto/GetActionResponseDto.php
index 04b435f..61208d6 100644
--- a/src/Actions/Dto/GetActionResponseDto.php
+++ b/src/Actions/Dto/GetActionResponseDto.php
@@ -7,7 +7,7 @@
final readonly class GetActionResponseDto
{
/**
- * @param ActionDto[] $actions
+ * @param list $actions
*/
public function __construct(
public array $actions,
@@ -16,8 +16,13 @@ public function __construct(
public static function fromArray(array $data): self
{
- return new self(
- actions: array_map(ActionDto::fromArray(...), $data),
- );
+ $actions = [];
+ foreach ($data as $item) {
+ if (is_array($item)) {
+ $actions[] = ActionDto::fromArray($item);
+ }
+ }
+
+ return new self(actions: $actions);
}
}
diff --git a/src/Actions/Dto/GetAlertResponseDto.php b/src/Actions/Dto/GetAlertResponseDto.php
index d7ea685..d2a47b2 100644
--- a/src/Actions/Dto/GetAlertResponseDto.php
+++ b/src/Actions/Dto/GetAlertResponseDto.php
@@ -7,7 +7,7 @@
final readonly class GetAlertResponseDto
{
/**
- * @param AlertDto[] $alerts
+ * @param list $alerts
*/
public function __construct(
public array $alerts,
@@ -16,8 +16,13 @@ public function __construct(
public static function fromArray(array $data): self
{
- return new self(
- alerts: array_map(AlertDto::fromArray(...), $data),
- );
+ $alerts = [];
+ foreach ($data as $item) {
+ if (is_array($item)) {
+ $alerts[] = AlertDto::fromArray($item);
+ }
+ }
+
+ return new self(alerts: $alerts);
}
}
diff --git a/src/Actions/Dto/GetAuditLogResponseDto.php b/src/Actions/Dto/GetAuditLogResponseDto.php
index a7ba656..086789f 100644
--- a/src/Actions/Dto/GetAuditLogResponseDto.php
+++ b/src/Actions/Dto/GetAuditLogResponseDto.php
@@ -7,7 +7,7 @@
final readonly class GetAuditLogResponseDto
{
/**
- * @param AuditLogDto[] $auditlogs
+ * @param list $auditlogs
*/
public function __construct(
public array $auditlogs,
@@ -16,8 +16,13 @@ public function __construct(
public static function fromArray(array $data): self
{
- return new self(
- auditlogs: array_map(AuditLogDto::fromArray(...), $data),
- );
+ $auditlogs = [];
+ foreach ($data as $item) {
+ if (is_array($item)) {
+ $auditlogs[] = AuditLogDto::fromArray($item);
+ }
+ }
+
+ return new self(auditlogs: $auditlogs);
}
}
diff --git a/src/Actions/Dto/GetDashboardResponseDto.php b/src/Actions/Dto/GetDashboardResponseDto.php
index e2b481d..3aaea33 100644
--- a/src/Actions/Dto/GetDashboardResponseDto.php
+++ b/src/Actions/Dto/GetDashboardResponseDto.php
@@ -16,9 +16,14 @@ public function __construct(
public static function fromArray(array $data): self
{
- return new self(
- dashboards: array_map(DashboardDto::fromArray(...), $data),
- );
+ $dashboards = [];
+ foreach ($data as $item) {
+ if (is_array($item)) {
+ $dashboards[] = DashboardDto::fromArray($item);
+ }
+ }
+
+ return new self(dashboards: $dashboards);
}
/**
diff --git a/src/Actions/Dto/GetEventResponseDto.php b/src/Actions/Dto/GetEventResponseDto.php
index 658e855..b6d150d 100644
--- a/src/Actions/Dto/GetEventResponseDto.php
+++ b/src/Actions/Dto/GetEventResponseDto.php
@@ -7,7 +7,7 @@
final readonly class GetEventResponseDto
{
/**
- * @param EventDto[] $events
+ * @param list $events
*/
public function __construct(
public array $events,
@@ -16,8 +16,13 @@ public function __construct(
public static function fromArray(array $data): self
{
- return new self(
- events: array_map(EventDto::fromArray(...), $data),
- );
+ $events = [];
+ foreach ($data as $item) {
+ if (is_array($item)) {
+ $events[] = EventDto::fromArray($item);
+ }
+ }
+
+ return new self(events: $events);
}
}
diff --git a/src/Actions/Dto/GetGraphResponseDto.php b/src/Actions/Dto/GetGraphResponseDto.php
index a289454..6a2734b 100644
--- a/src/Actions/Dto/GetGraphResponseDto.php
+++ b/src/Actions/Dto/GetGraphResponseDto.php
@@ -16,9 +16,14 @@ public function __construct(
public static function fromArray(array $data): self
{
- return new self(
- graphs: array_map(GraphDto::fromArray(...), $data),
- );
+ $graphs = [];
+ foreach ($data as $item) {
+ if (is_array($item)) {
+ $graphs[] = GraphDto::fromArray($item);
+ }
+ }
+
+ return new self(graphs: $graphs);
}
/**
diff --git a/src/Actions/Dto/GetHistoryResponseDto.php b/src/Actions/Dto/GetHistoryResponseDto.php
index 60255a7..6ba9c87 100644
--- a/src/Actions/Dto/GetHistoryResponseDto.php
+++ b/src/Actions/Dto/GetHistoryResponseDto.php
@@ -7,7 +7,7 @@
final readonly class GetHistoryResponseDto
{
/**
- * @param HistoryDto[] $history
+ * @param list $history
*/
public function __construct(
public array $history,
@@ -16,13 +16,18 @@ public function __construct(
public static function fromArray(array $data): self
{
- return new self(
- history: array_map(HistoryDto::fromArray(...), $data),
- );
+ $history = [];
+ foreach ($data as $item) {
+ if (is_array($item)) {
+ $history[] = HistoryDto::fromArray($item);
+ }
+ }
+
+ return new self(history: $history);
}
/**
- * @return HistoryDto[]
+ * @return list
*/
public function getHistory(): array
{
diff --git a/src/Actions/Dto/GetHostGroupResponseDto.php b/src/Actions/Dto/GetHostGroupResponseDto.php
index 6ee723f..641d41f 100644
--- a/src/Actions/Dto/GetHostGroupResponseDto.php
+++ b/src/Actions/Dto/GetHostGroupResponseDto.php
@@ -16,9 +16,14 @@ public function __construct(
public static function fromArray(array $data): self
{
- return new self(
- hostGroups: array_map(HostGroupDto::fromArray(...), $data),
- );
+ $hostGroups = [];
+ foreach ($data as $item) {
+ if (is_array($item)) {
+ $hostGroups[] = HostGroupDto::fromArray($item);
+ }
+ }
+
+ return new self(hostGroups: $hostGroups);
}
/**
diff --git a/src/Actions/Dto/GetHostResponseDto.php b/src/Actions/Dto/GetHostResponseDto.php
index badffad..2be0c6d 100644
--- a/src/Actions/Dto/GetHostResponseDto.php
+++ b/src/Actions/Dto/GetHostResponseDto.php
@@ -7,7 +7,7 @@
final readonly class GetHostResponseDto
{
/**
- * @param HostDto[] $hosts
+ * @param list $hosts
*/
public function __construct(
public array $hosts,
@@ -16,8 +16,13 @@ public function __construct(
public static function fromArray(array $data): self
{
- return new self(
- hosts: array_map(HostDto::fromArray(...), $data),
- );
+ $hosts = [];
+ foreach ($data as $item) {
+ if (is_array($item)) {
+ $hosts[] = HostDto::fromArray($item);
+ }
+ }
+
+ return new self(hosts: $hosts);
}
}
diff --git a/src/Actions/Dto/GetItemResponseDto.php b/src/Actions/Dto/GetItemResponseDto.php
index 9b24b9d..4b86626 100644
--- a/src/Actions/Dto/GetItemResponseDto.php
+++ b/src/Actions/Dto/GetItemResponseDto.php
@@ -7,7 +7,7 @@
final readonly class GetItemResponseDto
{
/**
- * @param ItemDto[] $items
+ * @param list $items
*/
public function __construct(
public array $items,
@@ -16,8 +16,13 @@ public function __construct(
public static function fromArray(array $data): self
{
- return new self(
- items: array_map(ItemDto::fromArray(...), $data),
- );
+ $items = [];
+ foreach ($data as $item) {
+ if (is_array($item)) {
+ $items[] = ItemDto::fromArray($item);
+ }
+ }
+
+ return new self(items: $items);
}
}
diff --git a/src/Actions/Dto/GetObjectsHostGroupResponseDto.php b/src/Actions/Dto/GetObjectsHostGroupResponseDto.php
index f871154..bb574ca 100644
--- a/src/Actions/Dto/GetObjectsHostGroupResponseDto.php
+++ b/src/Actions/Dto/GetObjectsHostGroupResponseDto.php
@@ -16,9 +16,14 @@ public function __construct(
public static function fromArray(array $data): self
{
- return new self(
- hostGroups: array_map(HostGroupDto::fromArray(...), $data),
- );
+ $hostGroups = [];
+ foreach ($data as $item) {
+ if (is_array($item)) {
+ $hostGroups[] = HostGroupDto::fromArray($item);
+ }
+ }
+
+ return new self(hostGroups: $hostGroups);
}
/**
diff --git a/src/Actions/Dto/GetTriggerResponseDto.php b/src/Actions/Dto/GetTriggerResponseDto.php
index a18b0e7..3e2e8fe 100644
--- a/src/Actions/Dto/GetTriggerResponseDto.php
+++ b/src/Actions/Dto/GetTriggerResponseDto.php
@@ -7,7 +7,7 @@
final readonly class GetTriggerResponseDto
{
/**
- * @param TriggerDto[] $triggers
+ * @param list $triggers
*/
public function __construct(
public array $triggers,
@@ -16,8 +16,13 @@ public function __construct(
public static function fromArray(array $data): self
{
- return new self(
- triggers: array_map(TriggerDto::fromArray(...), $data),
- );
+ $triggers = [];
+ foreach ($data as $item) {
+ if (is_array($item)) {
+ $triggers[] = TriggerDto::fromArray($item);
+ }
+ }
+
+ return new self(triggers: $triggers);
}
}
diff --git a/src/Actions/Dto/GraphDto.php b/src/Actions/Dto/GraphDto.php
index 05e25fd..a0090a9 100644
--- a/src/Actions/Dto/GraphDto.php
+++ b/src/Actions/Dto/GraphDto.php
@@ -4,6 +4,8 @@
namespace BytesCommerce\ZabbixApi\Actions\Dto;
+use Webmozart\Assert\Assert;
+
final readonly class GraphDto
{
/**
@@ -33,28 +35,114 @@ public function __construct(
public static function fromArray(array $data): self
{
- $gitems = isset($data['gitems']) && is_array($data['gitems'])
- ? array_map(GraphItemDto::fromArray(...), $data['gitems'])
- : [];
+ Assert::string($data['graphid'] ?? null);
+ Assert::string($data['name'] ?? null);
+
+ $gitems = [];
+ if (isset($data['gitems']) && is_array($data['gitems'])) {
+ foreach ($data['gitems'] as $itemData) {
+ if (is_array($itemData)) {
+ $gitems[] = GraphItemDto::fromArray($itemData);
+ }
+ }
+ }
+
+ $width = null;
+ if (isset($data['width'])) {
+ Assert::integerish($data['width']);
+ $width = (int) $data['width'];
+ }
+
+ $height = null;
+ if (isset($data['height'])) {
+ Assert::integerish($data['height']);
+ $height = (int) $data['height'];
+ }
+
+ $yaxismin = null;
+ if (isset($data['yaxismin'])) {
+ Assert::numeric($data['yaxismin']);
+ $yaxismin = (float) $data['yaxismin'];
+ }
+
+ $yaxismax = null;
+ if (isset($data['yaxismax'])) {
+ Assert::numeric($data['yaxismax']);
+ $yaxismax = (float) $data['yaxismax'];
+ }
+
+ $showWorkPeriod = null;
+ if (isset($data['show_work_period'])) {
+ Assert::integerish($data['show_work_period']);
+ $showWorkPeriod = (int) $data['show_work_period'];
+ }
+
+ $showTriggers = null;
+ if (isset($data['show_triggers'])) {
+ Assert::integerish($data['show_triggers']);
+ $showTriggers = (int) $data['show_triggers'];
+ }
+
+ $graphtype = null;
+ if (isset($data['graphtype'])) {
+ Assert::integerish($data['graphtype']);
+ $graphtype = (int) $data['graphtype'];
+ }
+
+ $showLegend = null;
+ if (isset($data['show_legend'])) {
+ Assert::integerish($data['show_legend']);
+ $showLegend = (int) $data['show_legend'];
+ }
+
+ $show3d = null;
+ if (isset($data['show_3d'])) {
+ Assert::integerish($data['show_3d']);
+ $show3d = (int) $data['show_3d'];
+ }
+
+ $percentLeft = null;
+ if (isset($data['percent_left'])) {
+ Assert::numeric($data['percent_left']);
+ $percentLeft = (float) $data['percent_left'];
+ }
+
+ $percentRight = null;
+ if (isset($data['percent_right'])) {
+ Assert::numeric($data['percent_right']);
+ $percentRight = (float) $data['percent_right'];
+ }
+
+ $yminType = null;
+ if (isset($data['ymin_type'])) {
+ Assert::integerish($data['ymin_type']);
+ $yminType = (int) $data['ymin_type'];
+ }
+
+ $ymaxType = null;
+ if (isset($data['ymax_type'])) {
+ Assert::integerish($data['ymax_type']);
+ $ymaxType = (int) $data['ymax_type'];
+ }
return new self(
graphid: $data['graphid'],
name: $data['name'],
- width: isset($data['width']) ? (int) $data['width'] : null,
- height: isset($data['height']) ? (int) $data['height'] : null,
- yaxismin: isset($data['yaxismin']) ? (float) $data['yaxismin'] : null,
- yaxismax: isset($data['yaxismax']) ? (float) $data['yaxismax'] : null,
- show_work_period: isset($data['show_work_period']) ? (int) $data['show_work_period'] : null,
- show_triggers: isset($data['show_triggers']) ? (int) $data['show_triggers'] : null,
- graphtype: isset($data['graphtype']) ? (int) $data['graphtype'] : null,
- show_legend: isset($data['show_legend']) ? (int) $data['show_legend'] : null,
- show_3d: isset($data['show_3d']) ? (int) $data['show_3d'] : null,
- percent_left: isset($data['percent_left']) ? (float) $data['percent_left'] : null,
- percent_right: isset($data['percent_right']) ? (float) $data['percent_right'] : null,
- ymin_type: isset($data['ymin_type']) ? (int) $data['ymin_type'] : null,
- ymax_type: isset($data['ymax_type']) ? (int) $data['ymax_type'] : null,
- ymin_itemid: $data['ymin_itemid'] ?? null,
- ymax_itemid: $data['ymax_itemid'] ?? null,
+ width: $width,
+ height: $height,
+ yaxismin: $yaxismin,
+ yaxismax: $yaxismax,
+ show_work_period: $showWorkPeriod,
+ show_triggers: $showTriggers,
+ graphtype: $graphtype,
+ show_legend: $showLegend,
+ show_3d: $show3d,
+ percent_left: $percentLeft,
+ percent_right: $percentRight,
+ ymin_type: $yminType,
+ ymax_type: $ymaxType,
+ ymin_itemid: isset($data['ymin_itemid']) && is_string($data['ymin_itemid']) ? $data['ymin_itemid'] : null,
+ ymax_itemid: isset($data['ymax_itemid']) && is_string($data['ymax_itemid']) ? $data['ymax_itemid'] : null,
gitems: $gitems,
);
}
diff --git a/src/Actions/Dto/GraphItemDto.php b/src/Actions/Dto/GraphItemDto.php
index a533f73..251c367 100644
--- a/src/Actions/Dto/GraphItemDto.php
+++ b/src/Actions/Dto/GraphItemDto.php
@@ -4,6 +4,8 @@
namespace BytesCommerce\ZabbixApi\Actions\Dto;
+use Webmozart\Assert\Assert;
+
final readonly class GraphItemDto
{
public function __construct(
@@ -20,15 +22,48 @@ public function __construct(
public static function fromArray(array $data): self
{
+ Assert::string($data['gitemid'] ?? null);
+ Assert::string($data['itemid'] ?? null);
+
+ $drawtype = null;
+ if (isset($data['drawtype'])) {
+ Assert::integerish($data['drawtype']);
+ $drawtype = (int) $data['drawtype'];
+ }
+
+ $sortorder = null;
+ if (isset($data['sortorder'])) {
+ Assert::integerish($data['sortorder']);
+ $sortorder = (int) $data['sortorder'];
+ }
+
+ $yaxisside = null;
+ if (isset($data['yaxisside'])) {
+ Assert::integerish($data['yaxisside']);
+ $yaxisside = (int) $data['yaxisside'];
+ }
+
+ $calcFnc = null;
+ if (isset($data['calc_fnc'])) {
+ Assert::integerish($data['calc_fnc']);
+ $calcFnc = (int) $data['calc_fnc'];
+ }
+
+ $type = null;
+ if (isset($data['type'])) {
+ Assert::integerish($data['type']);
+ $type = (int) $data['type'];
+ }
+
return new self(
gitemid: $data['gitemid'],
itemid: $data['itemid'],
- drawtype: isset($data['drawtype']) ? (int) $data['drawtype'] : null,
- sortorder: isset($data['sortorder']) ? (int) $data['sortorder'] : null,
- color: $data['color'] ?? null,
- yaxisside: isset($data['yaxisside']) ? (int) $data['yaxisside'] : null,
- calc_fnc: isset($data['calc_fnc']) ? (int) $data['calc_fnc'] : null,
- type: isset($data['type']) ? (int) $data['type'] : null,
+ drawtype: $drawtype,
+ sortorder: $sortorder,
+ color: isset($data['color']) && is_string($data['color']) ? $data['color'] : null,
+ yaxisside: $yaxisside,
+ calc_fnc: $calcFnc,
+ type: $type,
);
}
diff --git a/src/Actions/Dto/HistoryDto.php b/src/Actions/Dto/HistoryDto.php
index 2098d7d..f8327e1 100644
--- a/src/Actions/Dto/HistoryDto.php
+++ b/src/Actions/Dto/HistoryDto.php
@@ -4,7 +4,7 @@
namespace BytesCommerce\ZabbixApi\Actions\Dto;
-use BytesCommerce\ZabbixApi\Enums\HistoryTypeEnum;
+use Webmozart\Assert\Assert;
final readonly class HistoryDto
{
@@ -23,16 +23,44 @@ public function __construct(
public static function fromArray(array $data): self
{
+ Assert::string($data['itemid'] ?? null);
+ Assert::integerish($data['clock'] ?? null);
+ Assert::string($data['value'] ?? null);
+
+ $ns = null;
+ if (isset($data['ns'])) {
+ Assert::integerish($data['ns']);
+ $ns = (int) $data['ns'];
+ }
+
+ $timestamp = null;
+ if (isset($data['timestamp'])) {
+ Assert::integerish($data['timestamp']);
+ $timestamp = (int) $data['timestamp'];
+ }
+
+ $logeventid = null;
+ if (isset($data['logeventid'])) {
+ Assert::integerish($data['logeventid']);
+ $logeventid = (int) $data['logeventid'];
+ }
+
+ $severity = null;
+ if (isset($data['severity'])) {
+ Assert::integerish($data['severity']);
+ $severity = (int) $data['severity'];
+ }
+
return new self(
itemid: $data['itemid'],
clock: (int) $data['clock'],
value: $data['value'],
- ns: isset($data['ns']) ? (int) $data['ns'] : null,
- timestamp: isset($data['timestamp']) ? (int) $data['timestamp'] : null,
- logeventid: isset($data['logeventid']) ? (int) $data['logeventid'] : null,
- severity: isset($data['severity']) ? (int) $data['severity'] : null,
- source: $data['source'] ?? null,
- eventid: $data['eventid'] ?? null,
+ ns: $ns,
+ timestamp: $timestamp,
+ logeventid: $logeventid,
+ severity: $severity,
+ source: isset($data['source']) && is_string($data['source']) ? $data['source'] : null,
+ eventid: isset($data['eventid']) && is_string($data['eventid']) ? $data['eventid'] : null,
);
}
diff --git a/src/Actions/Dto/HostDto.php b/src/Actions/Dto/HostDto.php
index 4bca768..28fad3b 100644
--- a/src/Actions/Dto/HostDto.php
+++ b/src/Actions/Dto/HostDto.php
@@ -5,6 +5,7 @@
namespace BytesCommerce\ZabbixApi\Actions\Dto;
use BytesCommerce\ZabbixApi\Enums\StatusEnum;
+use Webmozart\Assert\Assert;
final readonly class HostDto
{
@@ -23,16 +24,20 @@ public function __construct(
public static function fromArray(array $data): self
{
+ Assert::string($data['hostid'] ?? null);
+ Assert::string($data['host'] ?? null);
+ Assert::integerish($data['status'] ?? null);
+
return new self(
hostid: $data['hostid'],
host: $data['host'],
- name: $data['name'] ?? null,
- status: StatusEnum::from($data['status']),
- interfaces: $data['interfaces'] ?? null,
- groups: $data['groups'] ?? null,
- templates: $data['templates'] ?? null,
- macros: $data['macros'] ?? null,
- tags: $data['tags'] ?? null,
+ name: isset($data['name']) && is_string($data['name']) ? $data['name'] : null,
+ status: StatusEnum::from((int) $data['status']),
+ interfaces: isset($data['interfaces']) && is_array($data['interfaces']) ? $data['interfaces'] : null,
+ groups: isset($data['groups']) && is_array($data['groups']) ? $data['groups'] : null,
+ templates: isset($data['templates']) && is_array($data['templates']) ? $data['templates'] : null,
+ macros: isset($data['macros']) && is_array($data['macros']) ? $data['macros'] : null,
+ tags: isset($data['tags']) && is_array($data['tags']) ? $data['tags'] : null,
);
}
diff --git a/src/Actions/Dto/HostGroupDto.php b/src/Actions/Dto/HostGroupDto.php
index a047deb..10b5300 100644
--- a/src/Actions/Dto/HostGroupDto.php
+++ b/src/Actions/Dto/HostGroupDto.php
@@ -4,6 +4,8 @@
namespace BytesCommerce\ZabbixApi\Actions\Dto;
+use Webmozart\Assert\Assert;
+
final readonly class HostGroupDto
{
public function __construct(
@@ -16,11 +18,26 @@ public function __construct(
public static function fromArray(array $data): self
{
+ Assert::string($data['groupid'] ?? null);
+ Assert::string($data['name'] ?? null);
+
+ $flags = null;
+ if (isset($data['flags'])) {
+ Assert::integerish($data['flags']);
+ $flags = (int) $data['flags'];
+ }
+
+ $internal = null;
+ if (isset($data['internal'])) {
+ Assert::integerish($data['internal']);
+ $internal = (int) $data['internal'];
+ }
+
return new self(
groupid: $data['groupid'],
name: $data['name'],
- flags: isset($data['flags']) ? (int) $data['flags'] : null,
- internal: isset($data['internal']) ? (int) $data['internal'] : null,
+ flags: $flags,
+ internal: $internal,
);
}
diff --git a/src/Actions/Dto/ItemDto.php b/src/Actions/Dto/ItemDto.php
index 828faf8..d2f7408 100644
--- a/src/Actions/Dto/ItemDto.php
+++ b/src/Actions/Dto/ItemDto.php
@@ -4,9 +4,10 @@
namespace BytesCommerce\ZabbixApi\Actions\Dto;
+use BytesCommerce\ZabbixApi\Enums\ItemTypeEnum;
use BytesCommerce\ZabbixApi\Enums\StatusEnum;
-use BytesCommerce\ZabbixApi\ItemTypeEnum;
-use BytesCommerce\ZabbixApi\ValueTypeEnum;
+use BytesCommerce\ZabbixApi\Enums\ValueTypeEnum;
+use Webmozart\Assert\Assert;
final readonly class ItemDto
{
@@ -27,18 +28,32 @@ public function __construct(
public static function fromArray(array $data): self
{
+ Assert::string($data['itemid'] ?? null);
+ Assert::string($data['name'] ?? null);
+ Assert::string($data['key_'] ?? null);
+ Assert::string($data['hostid'] ?? null);
+ Assert::integerish($data['type'] ?? null);
+ Assert::integerish($data['value_type'] ?? null);
+ Assert::string($data['delay'] ?? null);
+
+ $status = null;
+ if (isset($data['status'])) {
+ Assert::integerish($data['status']);
+ $status = StatusEnum::from((int) $data['status']);
+ }
+
return new self(
itemid: $data['itemid'],
name: $data['name'],
key_: $data['key_'],
hostid: $data['hostid'],
- type: ItemTypeEnum::from($data['type']),
- value_type: ValueTypeEnum::from($data['value_type']),
+ type: ItemTypeEnum::from((int) $data['type']),
+ value_type: ValueTypeEnum::from((int) $data['value_type']),
delay: $data['delay'],
- interfaceid: $data['interfaceid'] ?? null,
- preprocessing: $data['preprocessing'] ?? null,
- tags: $data['tags'] ?? null,
- status: isset($data['status']) ? StatusEnum::from($data['status']) : null,
+ interfaceid: isset($data['interfaceid']) && is_string($data['interfaceid']) ? $data['interfaceid'] : null,
+ preprocessing: isset($data['preprocessing']) && is_array($data['preprocessing']) ? $data['preprocessing'] : null,
+ tags: isset($data['tags']) && is_array($data['tags']) ? $data['tags'] : null,
+ status: $status,
);
}
diff --git a/src/Actions/Dto/PushHistoryResponseDto.php b/src/Actions/Dto/PushHistoryResponseDto.php
index 33a8ff0..46eef98 100644
--- a/src/Actions/Dto/PushHistoryResponseDto.php
+++ b/src/Actions/Dto/PushHistoryResponseDto.php
@@ -17,9 +17,16 @@ public function __construct(
public static function fromArray(array $data): self
{
- $historyids = isset($data['historyids']) && is_array($data['historyids'])
- ? array_map(static fn (mixed $id): string => (string) $id, $data['historyids'])
- : [];
+ $historyids = [];
+ if (isset($data['historyids']) && is_array($data['historyids'])) {
+ foreach ($data['historyids'] as $id) {
+ if (is_string($id)) {
+ $historyids[] = $id;
+ } elseif (is_int($id)) {
+ $historyids[] = (string) $id;
+ }
+ }
+ }
return new self(
historyids: $historyids,
diff --git a/src/Actions/Dto/TriggerDto.php b/src/Actions/Dto/TriggerDto.php
index d5bcc6c..8d4f33f 100644
--- a/src/Actions/Dto/TriggerDto.php
+++ b/src/Actions/Dto/TriggerDto.php
@@ -5,6 +5,7 @@
namespace BytesCommerce\ZabbixApi\Actions\Dto;
use BytesCommerce\ZabbixApi\Enums\StatusEnum;
+use Webmozart\Assert\Assert;
final readonly class TriggerDto
{
@@ -23,16 +24,38 @@ public function __construct(
public static function fromArray(array $data): self
{
+ Assert::string($data['triggerid'] ?? null);
+ Assert::string($data['description'] ?? null);
+ Assert::string($data['expression'] ?? null);
+
+ $priority = null;
+ if (isset($data['priority'])) {
+ Assert::integerish($data['priority']);
+ $priority = (int) $data['priority'];
+ }
+
+ $status = null;
+ if (isset($data['status'])) {
+ Assert::integerish($data['status']);
+ $status = StatusEnum::from((int) $data['status']);
+ }
+
+ $type = null;
+ if (isset($data['type'])) {
+ Assert::integerish($data['type']);
+ $type = (int) $data['type'];
+ }
+
return new self(
triggerid: $data['triggerid'],
description: $data['description'],
expression: $data['expression'],
- priority: $data['priority'] ?? null,
- status: isset($data['status']) ? StatusEnum::from($data['status']) : null,
- comments: $data['comments'] ?? null,
- type: $data['type'] ?? null,
- dependencies: $data['dependencies'] ?? null,
- tags: $data['tags'] ?? null,
+ priority: $priority,
+ status: $status,
+ comments: isset($data['comments']) && is_string($data['comments']) ? $data['comments'] : null,
+ type: $type,
+ dependencies: isset($data['dependencies']) && is_array($data['dependencies']) ? $data['dependencies'] : null,
+ tags: isset($data['tags']) && is_array($data['tags']) ? $data['tags'] : null,
);
}
diff --git a/src/Actions/Dto/UpdateDashboardDto.php b/src/Actions/Dto/UpdateDashboardDto.php
index 2dd5624..d3d7259 100644
--- a/src/Actions/Dto/UpdateDashboardDto.php
+++ b/src/Actions/Dto/UpdateDashboardDto.php
@@ -7,7 +7,7 @@
final readonly class UpdateDashboardDto
{
/**
- * @param list, view_mode?: string}>, private?: int, userid?: string, display_period?: int, auto_start?: int}> $dashboards
+ * @param list> $dashboards
*/
public function __construct(
public array $dashboards,
@@ -15,7 +15,7 @@ public function __construct(
}
/**
- * @return list, view_mode?: string}>, private?: int, userid?: string, display_period?: int, auto_start?: int}>
+ * @return list>
*/
public function getDashboards(): array
{
diff --git a/src/Actions/Event.php b/src/Actions/Event.php
index ad5e54e..8026a8c 100644
--- a/src/Actions/Event.php
+++ b/src/Actions/Event.php
@@ -34,6 +34,9 @@ public static function getActionPrefix(): string
public const int ACTION_SEVERITY = 8;
+ /**
+ * @param array $params
+ */
public function get(array $params): GetEventResponseDto
{
if (!isset($params['output'])) {
@@ -42,9 +45,12 @@ public function get(array $params): GetEventResponseDto
$result = $this->client->call(ZabbixAction::EVENT_GET, $params);
- return GetEventResponseDto::fromArray($result);
+ return GetEventResponseDto::fromArray(is_array($result) ? $result : []);
}
+ /**
+ * @param array $params
+ */
public function acknowledge(array $params): mixed
{
if (!isset($params['eventids']) || !is_array($params['eventids'])) {
diff --git a/src/Actions/Graph.php b/src/Actions/Graph.php
index 0d1af3b..27014ba 100644
--- a/src/Actions/Graph.php
+++ b/src/Actions/Graph.php
@@ -44,7 +44,16 @@ public function create(CreateGraphDto $dto): CreateGraphResponseDto
$result = $this->client->call(ZabbixAction::GRAPH_CREATE, $graphs);
- return new CreateGraphResponseDto($result['graphids']);
+ $graphids = [];
+ if (is_array($result) && isset($result['graphids']) && is_array($result['graphids'])) {
+ foreach ($result['graphids'] as $id) {
+ if (is_string($id)) {
+ $graphids[] = $id;
+ }
+ }
+ }
+
+ return new CreateGraphResponseDto($graphids);
}
public function update(UpdateGraphDto $dto): UpdateGraphResponseDto
@@ -53,7 +62,16 @@ public function update(UpdateGraphDto $dto): UpdateGraphResponseDto
$result = $this->client->call(ZabbixAction::GRAPH_UPDATE, $graphs);
- return new UpdateGraphResponseDto($result['graphids']);
+ $graphids = [];
+ if (is_array($result) && isset($result['graphids']) && is_array($result['graphids'])) {
+ foreach ($result['graphids'] as $id) {
+ if (is_string($id)) {
+ $graphids[] = $id;
+ }
+ }
+ }
+
+ return new UpdateGraphResponseDto($graphids);
}
public function delete(DeleteGraphDto $dto): DeleteGraphResponseDto
diff --git a/src/Actions/History.php b/src/Actions/History.php
index a823f3d..1569131 100644
--- a/src/Actions/History.php
+++ b/src/Actions/History.php
@@ -266,12 +266,12 @@ public function push(array $historyData): PushHistoryResponseDto
return new PushHistoryResponseDto([], '');
}
- $params = array_map(
+ $items = array_map(
static fn (PushHistoryDto $dto) => $dto->toArray(),
$historyData,
);
- $result = $this->client->call(ZabbixAction::HISTORY_PUSH, $params);
+ $result = $this->client->call(ZabbixAction::HISTORY_PUSH, $items);
return PushHistoryResponseDto::fromArray(is_array($result) ? $result : []);
}
diff --git a/src/Actions/Host.php b/src/Actions/Host.php
index 007f437..de77a3b 100644
--- a/src/Actions/Host.php
+++ b/src/Actions/Host.php
@@ -16,6 +16,9 @@ public static function getActionPrefix(): string
return 'host';
}
+ /**
+ * @param array $params
+ */
public function get(array $params): GetHostResponseDto
{
if (!isset($params['output'])) {
@@ -24,12 +27,18 @@ public function get(array $params): GetHostResponseDto
$result = $this->client->call(ZabbixAction::HOST_GET, $params);
- return GetHostResponseDto::fromArray($result);
+ return GetHostResponseDto::fromArray(is_array($result) ? $result : []);
}
+ /**
+ * @param list> $hosts
+ */
public function create(array $hosts): mixed
{
foreach ($hosts as $host) {
+ if (!is_array($host)) {
+ throw new ZabbixApiException('Host must be an array', -1);
+ }
if (!isset($host['host']) || !isset($host['groups']) || !is_array($host['groups'])) {
throw new ZabbixApiException('Host creation requires host name and groups array', -1);
}
@@ -38,9 +47,15 @@ public function create(array $hosts): mixed
return $this->client->call(ZabbixAction::HOST_CREATE, $hosts);
}
+ /**
+ * @param list> $hosts
+ */
public function update(array $hosts): mixed
{
foreach ($hosts as $host) {
+ if (!is_array($host)) {
+ throw new ZabbixApiException('Host must be an array', -1);
+ }
if (!isset($host['hostid'])) {
throw new ZabbixApiException('Host update requires hostid', -1);
}
@@ -49,11 +64,17 @@ public function update(array $hosts): mixed
return $this->client->call(ZabbixAction::HOST_UPDATE, $hosts);
}
+ /**
+ * @param list $hostIds
+ */
public function delete(array $hostIds): mixed
{
return $this->client->call(ZabbixAction::HOST_DELETE, $hostIds);
}
+ /**
+ * @param array $params
+ */
public function massAdd(array $params): mixed
{
if (!isset($params['hosts']) || !is_array($params['hosts'])) {
@@ -63,6 +84,9 @@ public function massAdd(array $params): mixed
return $this->client->call(ZabbixAction::HOST_MASSADD, $params);
}
+ /**
+ * @param array $params
+ */
public function massUpdate(array $params): mixed
{
if (!isset($params['hosts']) || !is_array($params['hosts'])) {
@@ -72,6 +96,9 @@ public function massUpdate(array $params): mixed
return $this->client->call(ZabbixAction::HOST_MASSUPDATE, $params);
}
+ /**
+ * @param array $params
+ */
public function massRemove(array $params): mixed
{
if (!isset($params['hostids']) || !is_array($params['hostids'])) {
diff --git a/src/Actions/HostGroup.php b/src/Actions/HostGroup.php
index e01c15f..67ea433 100644
--- a/src/Actions/HostGroup.php
+++ b/src/Actions/HostGroup.php
@@ -54,7 +54,16 @@ public function create(CreateHostGroupDto $dto): CreateHostGroupResponseDto
$result = $this->client->call(ZabbixAction::HOSTGROUP_CREATE, $params);
- return new CreateHostGroupResponseDto($result['groupids']);
+ $groupids = [];
+ if (is_array($result) && isset($result['groupids']) && is_array($result['groupids'])) {
+ foreach ($result['groupids'] as $id) {
+ if (is_string($id)) {
+ $groupids[] = $id;
+ }
+ }
+ }
+
+ return new CreateHostGroupResponseDto($groupids);
}
public function update(UpdateHostGroupDto $dto): UpdateHostGroupResponseDto
@@ -63,7 +72,16 @@ public function update(UpdateHostGroupDto $dto): UpdateHostGroupResponseDto
$result = $this->client->call(ZabbixAction::HOSTGROUP_UPDATE, $params);
- return new UpdateHostGroupResponseDto($result['groupids']);
+ $groupids = [];
+ if (is_array($result) && isset($result['groupids']) && is_array($result['groupids'])) {
+ foreach ($result['groupids'] as $id) {
+ if (is_string($id)) {
+ $groupids[] = $id;
+ }
+ }
+ }
+
+ return new UpdateHostGroupResponseDto($groupids);
}
public function delete(DeleteHostGroupDto $dto): DeleteHostGroupResponseDto
@@ -115,7 +133,16 @@ public function massAdd(MassAddHostGroupDto $dto): MassAddHostGroupResponseDto
$result = $this->client->call(ZabbixAction::HOSTGROUP_MASSADD, $params);
- return new MassAddHostGroupResponseDto($result['groupids'] ?? []);
+ $groupids = [];
+ if (is_array($result) && isset($result['groupids']) && is_array($result['groupids'])) {
+ foreach ($result['groupids'] as $id) {
+ if (is_string($id)) {
+ $groupids[] = $id;
+ }
+ }
+ }
+
+ return new MassAddHostGroupResponseDto($groupids);
}
public function massRemove(MassRemoveHostGroupDto $dto): MassRemoveHostGroupResponseDto
@@ -124,7 +151,16 @@ public function massRemove(MassRemoveHostGroupDto $dto): MassRemoveHostGroupResp
$result = $this->client->call(ZabbixAction::HOSTGROUP_MASSREMOVE, $params);
- return new MassRemoveHostGroupResponseDto($result['groupids'] ?? []);
+ $groupids = [];
+ if (is_array($result) && isset($result['groupids']) && is_array($result['groupids'])) {
+ foreach ($result['groupids'] as $id) {
+ if (is_string($id)) {
+ $groupids[] = $id;
+ }
+ }
+ }
+
+ return new MassRemoveHostGroupResponseDto($groupids);
}
public function massUpdate(MassUpdateHostGroupDto $dto): MassUpdateHostGroupResponseDto
@@ -133,7 +169,16 @@ public function massUpdate(MassUpdateHostGroupDto $dto): MassUpdateHostGroupResp
$result = $this->client->call(ZabbixAction::HOSTGROUP_MASSUPDATE, $params);
- return new MassUpdateHostGroupResponseDto($result['groupids'] ?? []);
+ $groupids = [];
+ if (is_array($result) && isset($result['groupids']) && is_array($result['groupids'])) {
+ foreach ($result['groupids'] as $id) {
+ if (is_string($id)) {
+ $groupids[] = $id;
+ }
+ }
+ }
+
+ return new MassUpdateHostGroupResponseDto($groupids);
}
private function mapCreateHostGroup(array $hostGroup): array
diff --git a/src/Actions/Item.php b/src/Actions/Item.php
index 7ed67fd..cfc666f 100644
--- a/src/Actions/Item.php
+++ b/src/Actions/Item.php
@@ -30,7 +30,7 @@ public function get(GetItemDto $dto): GetItemResponseDto
$result = $this->client->call(ZabbixAction::ITEM_GET, $params);
- return GetItemResponseDto::fromArray($result);
+ return GetItemResponseDto::fromArray(is_array($result) ? $result : []);
}
public function create(CreateItemDto $dto): mixed
diff --git a/src/Actions/Trapper.php b/src/Actions/Trapper.php
index 37d1c16..d65f483 100644
--- a/src/Actions/Trapper.php
+++ b/src/Actions/Trapper.php
@@ -36,11 +36,15 @@ public function __construct(
*/
public function send(string $host, string $key, mixed $value, ?int $clock = null): array
{
+ $stringValue = is_scalar($value) || $value instanceof \Stringable
+ ? (string) $value
+ : json_encode($value, \JSON_THROW_ON_ERROR);
+
$data = [
[
'host' => $host,
'key' => $key,
- 'value' => (string) $value
+ 'value' => $stringValue
]
];
@@ -78,10 +82,15 @@ public function sendBatch(array $metrics): array
throw new ZabbixApiException('Each metric must have host, key, and value', -1);
}
+ $value = $metric['value'];
+ $stringValue = is_scalar($value) || $value instanceof \Stringable
+ ? (string) $value
+ : json_encode($value, \JSON_THROW_ON_ERROR);
+
$item = [
'host' => $metric['host'],
'key' => $metric['key'],
- 'value' => (string) $metric['value']
+ 'value' => $stringValue
];
if (isset($metric['clock'])) {
@@ -99,7 +108,9 @@ public function sendBatch(array $metrics): array
$json = json_encode($payload, \JSON_THROW_ON_ERROR);
$message = $this->buildMessage($json);
- return $this->sendToZabbix($message);
+ $result = $this->sendToZabbix($message);
+
+ return $result;
}
private function buildMessage(string $json): string
@@ -110,6 +121,18 @@ private function buildMessage(string $json): string
return self::ZABBIX_HEADER . $lengthPacked . $json;
}
+ /**
+ * @return array{
+ * response: string,
+ * info: string,
+ * processed?: int,
+ * failed?: int,
+ * total?: int,
+ * seconds_spent?: float
+ * }
+ *
+ * @throws ZabbixApiException
+ */
private function sendToZabbix(string $message): array
{
$socket = @stream_socket_client(
@@ -158,11 +181,46 @@ private function sendToZabbix(string $message): array
throw new ZabbixApiException('Invalid JSON response from Zabbix server: ' . $e->getMessage(), -1);
}
- if (isset($result['info'])) {
- $result = array_merge($result, $this->parseInfoString($result['info']));
+ if (!is_array($result)) {
+ throw new ZabbixApiException('Invalid response from Zabbix server: expected array', -1);
}
- return $result;
+ if (isset($result['info']) && is_string($result['info'])) {
+ $parsed = $this->parseInfoString($result['info']);
+ foreach ($parsed as $key => $value) {
+ $result[$key] = $value;
+ }
+ }
+
+ $response = $result['response'] ?? null;
+ $info = $result['info'] ?? null;
+
+ if (!is_string($response) || !is_string($info)) {
+ throw new ZabbixApiException('Invalid response from Zabbix server: missing required fields', -1);
+ }
+
+ $typedResult = [
+ 'response' => $response,
+ 'info' => $info,
+ ];
+
+ if (isset($result['processed']) && is_int($result['processed'])) {
+ $typedResult['processed'] = $result['processed'];
+ }
+
+ if (isset($result['failed']) && is_int($result['failed'])) {
+ $typedResult['failed'] = $result['failed'];
+ }
+
+ if (isset($result['total']) && is_int($result['total'])) {
+ $typedResult['total'] = $result['total'];
+ }
+
+ if (isset($result['seconds_spent']) && (is_float($result['seconds_spent']) || is_int($result['seconds_spent']))) {
+ $typedResult['seconds_spent'] = (float) $result['seconds_spent'];
+ }
+
+ return $typedResult;
}
private function parseInfoString(string $info): array
diff --git a/src/Actions/Trigger.php b/src/Actions/Trigger.php
index a1ef8f8..fffeb0d 100644
--- a/src/Actions/Trigger.php
+++ b/src/Actions/Trigger.php
@@ -16,6 +16,9 @@ public static function getActionPrefix(): string
return 'trigger';
}
+ /**
+ * @param array $params
+ */
public function get(array $params): GetTriggerResponseDto
{
if (!isset($params['output'])) {
@@ -24,13 +27,20 @@ public function get(array $params): GetTriggerResponseDto
$result = $this->client->call(ZabbixAction::TRIGGER_GET, $params);
+ if (!is_array($result)) {
+ throw new ZabbixApiException('Invalid response from Zabbix API: expected array', -1);
+ }
+
return GetTriggerResponseDto::fromArray($result);
}
+ /**
+ * @param array> $triggers
+ */
public function create(array $triggers): mixed
{
foreach ($triggers as $trigger) {
- if (!isset($trigger['description']) || !isset($trigger['expression'])) {
+ if (!is_array($trigger) || !isset($trigger['description']) || !isset($trigger['expression'])) {
throw new ZabbixApiException('Trigger creation requires description and expression', -1);
}
}
@@ -38,10 +48,13 @@ public function create(array $triggers): mixed
return $this->client->call(ZabbixAction::TRIGGER_CREATE, $triggers);
}
+ /**
+ * @param array> $triggers
+ */
public function update(array $triggers): mixed
{
foreach ($triggers as $trigger) {
- if (!isset($trigger['triggerid'])) {
+ if (!is_array($trigger) || !isset($trigger['triggerid'])) {
throw new ZabbixApiException('Trigger update requires triggerid', -1);
}
}
@@ -49,6 +62,9 @@ public function update(array $triggers): mixed
return $this->client->call(ZabbixAction::TRIGGER_UPDATE, $triggers);
}
+ /**
+ * @param array $triggerIds
+ */
public function delete(array $triggerIds): mixed
{
return $this->client->call(ZabbixAction::TRIGGER_DELETE, $triggerIds);
diff --git a/src/DependencyInjection/Configuration.php b/src/DependencyInjection/Configuration.php
index eeaa349..cb1e44d 100644
--- a/src/DependencyInjection/Configuration.php
+++ b/src/DependencyInjection/Configuration.php
@@ -37,7 +37,7 @@ public function getConfigTreeBuilder(): TreeBuilder
->info('Authentication TTL in seconds')
->end()
->scalarNode('app_name')
- ->defaultValue('%env(APP_NAME)%')
+ ->defaultNull()
->info('Application name for monitoring')
->end()
->scalarNode('host_group')
diff --git a/src/DependencyInjection/ZabbixApiExtension.php b/src/DependencyInjection/ZabbixApiExtension.php
index 084c55a..6e6b166 100644
--- a/src/DependencyInjection/ZabbixApiExtension.php
+++ b/src/DependencyInjection/ZabbixApiExtension.php
@@ -16,17 +16,26 @@ public function load(array $configs, ContainerBuilder $container): void
$configuration = new Configuration();
$config = $this->processConfiguration($configuration, $configs);
- $container->setParameter('zabbix_api.base_uri', $config['base_uri']);
- $container->setParameter('zabbix_api.api_token', $config['api_token']);
- $container->setParameter('zabbix_api.username', $config['username']);
- $container->setParameter('zabbix_api.password', $config['password']);
- $container->setParameter('zabbix_api.auth_ttl', $config['auth_ttl']);
- $container->setParameter('zabbix_api.app_name', $config['app_name']);
- $container->setParameter('zabbix_api.host_group', $config['host_group']);
- $container->setParameter('zabbix_api.dashboard_config_path', $config['dashboard_config_path']);
- $container->setParameter('zabbix_api.setup_enabled', $config['setup_enabled']);
+ $container->setParameter('zabbix_api.base_uri', $this->getConfigValue($config, 'base_uri'));
+ $container->setParameter('zabbix_api.api_token', $this->getConfigValue($config, 'api_token'));
+ $container->setParameter('zabbix_api.username', $this->getConfigValue($config, 'username'));
+ $container->setParameter('zabbix_api.password', $this->getConfigValue($config, 'password'));
+ $container->setParameter('zabbix_api.auth_ttl', $this->getConfigValue($config, 'auth_ttl'));
+ $container->setParameter('zabbix_api.app_name', $this->getConfigValue($config, 'app_name'));
+ $container->setParameter('zabbix_api.host_group', $this->getConfigValue($config, 'host_group'));
+ $container->setParameter('zabbix_api.dashboard_config_path', $this->getConfigValue($config, 'dashboard_config_path'));
+ $container->setParameter('zabbix_api.setup_enabled', $this->getConfigValue($config, 'setup_enabled'));
$loader = new YamlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config'));
$loader->load('services.yaml');
}
+
+ private function getConfigValue(array $config, string $key): array|bool|float|int|string|null
+ {
+ $value = $config[$key] ?? null;
+ if ($value === null || is_array($value) || is_bool($value) || is_float($value) || is_int($value) || is_string($value)) {
+ return $value;
+ }
+ return null;
+ }
}
diff --git a/src/Enums/CommandTypeEnum.php b/src/Enums/CommandTypeEnum.php
index 9d7946a..01aae5a 100644
--- a/src/Enums/CommandTypeEnum.php
+++ b/src/Enums/CommandTypeEnum.php
@@ -2,7 +2,7 @@
declare(strict_types=1);
-namespace BytesCommerce\ZabbixApi\Zabbix;
+namespace BytesCommerce\ZabbixApi\Enums;
enum CommandTypeEnum: int
{
diff --git a/src/Enums/DefaultMsgEnum.php b/src/Enums/DefaultMsgEnum.php
index e11194c..d3103e8 100644
--- a/src/Enums/DefaultMsgEnum.php
+++ b/src/Enums/DefaultMsgEnum.php
@@ -2,7 +2,7 @@
declare(strict_types=1);
-namespace BytesCommerce\ZabbixApi\Zabbix;
+namespace BytesCommerce\ZabbixApi\Enums;
enum DefaultMsgEnum: int
{
diff --git a/src/Enums/ExecuteOnEnum.php b/src/Enums/ExecuteOnEnum.php
index e9184db..1c8642f 100644
--- a/src/Enums/ExecuteOnEnum.php
+++ b/src/Enums/ExecuteOnEnum.php
@@ -2,7 +2,7 @@
declare(strict_types=1);
-namespace BytesCommerce\ZabbixApi\Zabbix;
+namespace BytesCommerce\ZabbixApi\Enums;
enum ExecuteOnEnum: int
{
diff --git a/src/Enums/InventoryModeEnum.php b/src/Enums/InventoryModeEnum.php
index 14ad670..b837d2a 100644
--- a/src/Enums/InventoryModeEnum.php
+++ b/src/Enums/InventoryModeEnum.php
@@ -2,7 +2,7 @@
declare(strict_types=1);
-namespace BytesCommerce\ZabbixApi\Zabbix;
+namespace BytesCommerce\ZabbixApi\Enums;
enum InventoryModeEnum: int
{
diff --git a/src/Enums/OperationTypeEnum.php b/src/Enums/OperationTypeEnum.php
index 6db48b4..612a783 100644
--- a/src/Enums/OperationTypeEnum.php
+++ b/src/Enums/OperationTypeEnum.php
@@ -2,7 +2,7 @@
declare(strict_types=1);
-namespace BytesCommerce\ZabbixApi\Zabbix;
+namespace BytesCommerce\ZabbixApi\Enums;
enum OperationTypeEnum: int
{
diff --git a/src/Enums/SeverityEnum.php b/src/Enums/SeverityEnum.php
index 78e2ef9..35dca55 100644
--- a/src/Enums/SeverityEnum.php
+++ b/src/Enums/SeverityEnum.php
@@ -2,7 +2,7 @@
declare(strict_types=1);
-namespace BytesCommerce\ZabbixApi\Zabbix;
+namespace BytesCommerce\ZabbixApi\Enums;
enum SeverityEnum: int
{
diff --git a/src/Enums/ValueEnum.php b/src/Enums/ValueEnum.php
index dca32aa..38be4f5 100644
--- a/src/Enums/ValueEnum.php
+++ b/src/Enums/ValueEnum.php
@@ -2,7 +2,7 @@
declare(strict_types=1);
-namespace BytesCommerce\ZabbixApi\Zabbix;
+namespace BytesCommerce\ZabbixApi\Enums;
enum ValueEnum: int
{
diff --git a/src/Provisioning/DashboardApi.php b/src/Provisioning/DashboardApi.php
index e4c4128..fc5eb12 100644
--- a/src/Provisioning/DashboardApi.php
+++ b/src/Provisioning/DashboardApi.php
@@ -123,7 +123,10 @@ public function get(DashboardId $id): ZabbixDashboard
Assert::isArray($result);
Assert::count($result, 1);
- return $this->parseDashboard($result[0]);
+ $dashboardData = $result[0];
+ Assert::isArray($dashboardData);
+
+ return $this->parseDashboard($dashboardData);
}
public function findHostById(string $hostId): ?HostInfo
@@ -139,7 +142,12 @@ public function findHostById(string $hostId): ?HostInfo
return null;
}
- return HostInfo::fromArray($result[0]);
+ $hostData = $result[0];
+ if (!\is_array($hostData)) {
+ return null;
+ }
+
+ return HostInfo::fromArray($hostData);
}
public function findHostByName(string $hostName): ?HostInfo
@@ -155,7 +163,12 @@ public function findHostByName(string $hostName): ?HostInfo
return null;
}
- return HostInfo::fromArray($result[0]);
+ $hostData = $result[0];
+ if (!\is_array($hostData)) {
+ return null;
+ }
+
+ return HostInfo::fromArray($hostData);
}
private function parseDashboard(array $data): ZabbixDashboard
@@ -164,9 +177,15 @@ private function parseDashboard(array $data): ZabbixDashboard
$managedKey = $this->extractManagedKey($widgets);
$hash = $this->extractHash($widgets);
+ $dashboardId = $data['dashboardid'] ?? null;
+ $name = $data['name'] ?? null;
+
+ Assert::string($dashboardId);
+ Assert::string($name);
+
return new ZabbixDashboard(
- dashboardId: DashboardId::fromString($data['dashboardid']),
- name: $data['name'],
+ dashboardId: DashboardId::fromString($dashboardId),
+ name: $name,
managedKey: $managedKey,
hash: $hash,
widgets: $widgets,
@@ -177,12 +196,22 @@ private function extractWidgetsFromPages(array $dashboardData): array
{
$widgets = [];
- foreach ($dashboardData['pages'] ?? [] as $page) {
+ $pages = $dashboardData['pages'] ?? [];
+ if (!\is_array($pages)) {
+ return $widgets;
+ }
+
+ foreach ($pages as $page) {
if (!\is_array($page)) {
continue;
}
- foreach ($page['widgets'] ?? [] as $widget) {
+ $pageWidgets = $page['widgets'] ?? [];
+ if (!\is_array($pageWidgets)) {
+ continue;
+ }
+
+ foreach ($pageWidgets as $widget) {
if (\is_array($widget)) {
$widgets[] = $widget;
}
@@ -199,15 +228,26 @@ private function extractManagedKey(array $widgets): ManagedKey
continue;
}
- if (($widget['type'] ?? '') === 'text' && str_contains((string) ($widget['name'] ?? ''), 'Managed')) {
- foreach ($widget['fields'] ?? [] as $field) {
+ $type = $widget['type'] ?? '';
+ $name = $widget['name'] ?? '';
+
+ if ($type === 'text' && is_string($name) && str_contains($name, 'Managed')) {
+ $fields = $widget['fields'] ?? [];
+ if (!\is_array($fields)) {
+ continue;
+ }
+
+ foreach ($fields as $field) {
if (!\is_array($field)) {
continue;
}
- if (($field['type'] ?? '0') === '1' && str_contains((string) ($field['value'] ?? ''), 'Key:')) {
- preg_match('/Key:\s*(\S+)/', (string) $field['value'], $matches);
- if (isset($matches[1])) {
+ $fieldType = $field['type'] ?? '0';
+ $fieldValue = $field['value'] ?? '';
+
+ if ($fieldType === '1' && is_string($fieldValue) && str_contains($fieldValue, 'Key:')) {
+ preg_match('/Key:\s*(\S+)/', $fieldValue, $matches);
+ if (isset($matches[1]) && is_string($matches[1])) {
return ManagedKey::fromString($matches[1]);
}
}
@@ -225,15 +265,26 @@ private function extractHash(array $widgets): DefinitionHash
continue;
}
- if (($widget['type'] ?? '') === 'text' && str_contains((string) ($widget['name'] ?? ''), 'Managed')) {
- foreach ($widget['fields'] ?? [] as $field) {
+ $type = $widget['type'] ?? '';
+ $name = $widget['name'] ?? '';
+
+ if ($type === 'text' && is_string($name) && str_contains($name, 'Managed')) {
+ $fields = $widget['fields'] ?? [];
+ if (!\is_array($fields)) {
+ continue;
+ }
+
+ foreach ($fields as $field) {
if (!\is_array($field)) {
continue;
}
- if (($field['type'] ?? '1') === '1' && str_contains((string) ($field['value'] ?? ''), 'Hash:')) {
- preg_match('/Hash:\s*(\S+)/', (string) $field['value'], $matches);
- if (isset($matches[1])) {
+ $fieldType = $field['type'] ?? '1';
+ $fieldValue = $field['value'] ?? '';
+
+ if ($fieldType === '1' && is_string($fieldValue) && str_contains($fieldValue, 'Hash:')) {
+ preg_match('/Hash:\s*(\S+)/', $fieldValue, $matches);
+ if (isset($matches[1]) && is_string($matches[1])) {
return DefinitionHash::fromString($matches[1]);
}
}
diff --git a/src/Provisioning/DashboardProvisioner.php b/src/Provisioning/DashboardProvisioner.php
index 32d2d37..606cdaf 100644
--- a/src/Provisioning/DashboardProvisioner.php
+++ b/src/Provisioning/DashboardProvisioner.php
@@ -52,8 +52,19 @@ public function provisionForHost(
$hash = $this->specHasher->hash($definition);
- $title = $this->specRenderer->renderTitleTemplate($definition['title_template'], $host);
- $widgets = $this->specRenderer->renderWidgets($definition['widgets'], $host);
+ $titleTemplate = $definition['title_template'] ?? '';
+ $widgetsDef = $definition['widgets'] ?? [];
+
+ if (!is_string($titleTemplate)) {
+ throw new RuntimeException('Invalid definition: title_template must be a string');
+ }
+
+ if (!is_array($widgetsDef)) {
+ throw new RuntimeException('Invalid definition: widgets must be an array');
+ }
+
+ $title = $this->specRenderer->renderTitleTemplate($titleTemplate, $host);
+ $widgets = $this->specRenderer->renderWidgets($widgetsDef, $host);
$spec = new DashboardSpec(
name: $title,
diff --git a/src/Provisioning/Dto/DashboardSpec.php b/src/Provisioning/Dto/DashboardSpec.php
index df345b5..a5ee580 100644
--- a/src/Provisioning/Dto/DashboardSpec.php
+++ b/src/Provisioning/Dto/DashboardSpec.php
@@ -6,6 +6,7 @@
use BytesCommerce\ZabbixApi\Provisioning\ValueObject\DefinitionHash;
use BytesCommerce\ZabbixApi\Provisioning\ValueObject\ManagedKey;
+use Webmozart\Assert\Assert;
final readonly class DashboardSpec
{
@@ -19,6 +20,11 @@ public function __construct(
public static function fromArray(array $data): self
{
+ Assert::string($data['name'] ?? null);
+ Assert::string($data['managed_key'] ?? null);
+ Assert::string($data['hash'] ?? null);
+ Assert::isArray($data['widgets'] ?? null);
+
return new self(
name: $data['name'],
managedKey: ManagedKey::fromString($data['managed_key']),
diff --git a/src/Provisioning/Dto/HostInfo.php b/src/Provisioning/Dto/HostInfo.php
index f7389c4..3c486a9 100644
--- a/src/Provisioning/Dto/HostInfo.php
+++ b/src/Provisioning/Dto/HostInfo.php
@@ -4,6 +4,8 @@
namespace BytesCommerce\ZabbixApi\Provisioning\Dto;
+use Webmozart\Assert\Assert;
+
final readonly class HostInfo
{
public function __construct(
@@ -15,6 +17,10 @@ public function __construct(
public static function fromArray(array $data): self
{
+ Assert::string($data['hostid'] ?? null);
+ Assert::string($data['host'] ?? null);
+ Assert::string($data['name'] ?? null);
+
return new self(
hostId: $data['hostid'],
host: $data['host'],
diff --git a/src/Provisioning/SpecRenderer.php b/src/Provisioning/SpecRenderer.php
index b6a7a47..794027b 100644
--- a/src/Provisioning/SpecRenderer.php
+++ b/src/Provisioning/SpecRenderer.php
@@ -36,13 +36,30 @@ private function renderArray(array $data, array $replacements): array
{
$result = [];
+ $search = array_keys($replacements);
+ $replace = array_values($replacements);
+
+ $searchStrings = [];
+ foreach ($search as $s) {
+ if (is_string($s)) {
+ $searchStrings[] = $s;
+ }
+ }
+
+ $replaceStrings = [];
+ foreach ($replace as $r) {
+ if (is_string($r)) {
+ $replaceStrings[] = $r;
+ }
+ }
+
foreach ($data as $key => $value) {
if (\is_array($value)) {
$result[$key] = $this->renderArray($value, $replacements);
} elseif (\is_string($value)) {
$result[$key] = str_replace(
- array_keys($replacements),
- array_values($replacements),
+ $searchStrings,
+ $replaceStrings,
$value,
);
} else {
diff --git a/src/Resources/config/services.yaml b/src/Resources/config/services.yaml
index a7661df..04b3e65 100644
--- a/src/Resources/config/services.yaml
+++ b/src/Resources/config/services.yaml
@@ -21,6 +21,7 @@ services:
arguments:
- '@http_client'
- '%zabbix_api.base_uri%'
+ autoconfigure: false
BytesCommerce\ZabbixApi\ZabbixClientInterface:
class: BytesCommerce\ZabbixApi\ZabbixClient
diff --git a/src/Service/ZabbixClientWithApiKey.php b/src/Service/ZabbixClientWithApiKey.php
index 504368e..c9eddb5 100644
--- a/src/Service/ZabbixClientWithApiKey.php
+++ b/src/Service/ZabbixClientWithApiKey.php
@@ -164,7 +164,14 @@ private function executeApiCall(ZabbixAction $action, array $params, ?string $au
$data = $response->toArray();
if (isset($data['error']) && \is_array($data['error'])) {
- $error = ResponseValidator::ensureErrorStructure($data['error']);
+ $errorData = $data['error'];
+ $typedError = [];
+ foreach ($errorData as $key => $value) {
+ if (is_string($key)) {
+ $typedError[$key] = $value;
+ }
+ }
+ $error = ResponseValidator::ensureErrorStructure($typedError);
throw new ZabbixApiException(
$error['message'],
diff --git a/src/Service/ZabbixItemRegistry.php b/src/Service/ZabbixItemRegistry.php
index db6b12f..87e82b6 100644
--- a/src/Service/ZabbixItemRegistry.php
+++ b/src/Service/ZabbixItemRegistry.php
@@ -25,7 +25,11 @@ public function getHostId(): ?string
$item = $this->cache->getItem(self::CACHE_KEY_HOST_ID);
$hostId = $item->get();
- return $hostId !== null ? (string) $hostId : null;
+ if ($hostId === null) {
+ return null;
+ }
+
+ return is_string($hostId) ? $hostId : null;
}
public function setHostId(string $hostId): void
@@ -43,7 +47,9 @@ public function getItemIdForKey(string $key): ?string
return null;
}
- return $itemIds[$key] ?? null;
+ $value = $itemIds[$key] ?? null;
+
+ return is_string($value) ? $value : null;
}
public function setItemId(string $key, string $itemId): void
@@ -62,10 +68,11 @@ public function getAllItemDefinitions(): array
{
return [
'tx.duration_ms' => [
- 'name' => 'Transaction Duration (ms)',
+ 'name' => 'Transaction Duration',
'type' => 2,
'value_type' => 0,
'history' => '7d',
+ 'units' => 'ms',
],
'tx.http_status' => [
'name' => 'HTTP Status Code',
@@ -73,6 +80,13 @@ public function getAllItemDefinitions(): array
'value_type' => 3,
'history' => '7d',
],
+ 'tx.error_rate' => [
+ 'name' => 'HTTP Error Rate',
+ 'type' => 2,
+ 'value_type' => 0,
+ 'history' => '7d',
+ 'units' => '%',
+ ],
'auth.login.success' => [
'name' => 'Login Success Count',
'type' => 2,
@@ -121,6 +135,51 @@ public function getAllItemDefinitions(): array
'value_type' => 4,
'history' => '7d',
],
+ 'messenger.queue.depth' => [
+ 'name' => 'Message Queue Depth',
+ 'type' => 2,
+ 'value_type' => 3,
+ 'history' => '7d',
+ ],
+ 'messenger.failed.count' => [
+ 'name' => 'Failed Messages Count',
+ 'type' => 2,
+ 'value_type' => 3,
+ 'history' => '7d',
+ ],
+ 'messenger.processing_ms' => [
+ 'name' => 'Message Processing Time',
+ 'type' => 2,
+ 'value_type' => 0,
+ 'history' => '7d',
+ 'units' => 'ms',
+ ],
+ 'messenger.received' => [
+ 'name' => 'Messages Received',
+ 'type' => 2,
+ 'value_type' => 3,
+ 'history' => '7d',
+ ],
+ 'messenger.handled' => [
+ 'name' => 'Messages Handled',
+ 'type' => 2,
+ 'value_type' => 3,
+ 'history' => '7d',
+ ],
+ 'cache.hit_rate' => [
+ 'name' => 'Cache Hit Rate',
+ 'type' => 2,
+ 'value_type' => 0,
+ 'history' => '7d',
+ 'units' => '%',
+ ],
+ 'db.query_time_ms' => [
+ 'name' => 'Database Query Time',
+ 'type' => 2,
+ 'value_type' => 0,
+ 'history' => '7d',
+ 'units' => 'ms',
+ ],
];
}
diff --git a/src/Setup/TriggerProvisioner.php b/src/Setup/TriggerProvisioner.php
new file mode 100644
index 0000000..60fb40d
--- /dev/null
+++ b/src/Setup/TriggerProvisioner.php
@@ -0,0 +1,150 @@
+getTriggerDefinitions($hostId, $hostName);
+
+ foreach ($triggerDefinitions as $triggerDef) {
+ if (is_array($triggerDef)) {
+ $this->ensureTrigger($triggerDef, $hostId);
+ }
+ }
+ }
+
+ /**
+ * @return array>
+ */
+ private function getTriggerDefinitions(string $hostId, string $hostName): array
+ {
+ $durationKey = $this->naming->getItemKey('tx.duration_ms');
+ $statusKey = $this->naming->getItemKey('tx.http_status');
+ $loginFailureKey = $this->naming->getItemKey('auth.login.failure');
+ $exceptionKey = $this->naming->getItemKey('error.exception');
+ $messengerFailedKey = $this->naming->getItemKey('messenger.failed.count');
+
+ return [
+ [
+ 'description' => 'High response time on ' . $hostName,
+ 'expression' => sprintf('last(/%s/%s) > 2000', $hostName, $durationKey),
+ 'priority' => self::PRIORITY_WARNING,
+ 'tags' => [
+ ['tag' => 'slo', 'value' => 'latency'],
+ ['tag' => 'class', 'value' => 'performance'],
+ ],
+ ],
+ [
+ 'description' => 'Critical response time on ' . $hostName,
+ 'expression' => sprintf('last(/%s/%s) > 5000', $hostName, $durationKey),
+ 'priority' => self::PRIORITY_HIGH,
+ 'tags' => [
+ ['tag' => 'slo', 'value' => 'latency'],
+ ['tag' => 'class', 'value' => 'performance'],
+ ],
+ ],
+ [
+ 'description' => 'HTTP 5xx error detected on ' . $hostName,
+ 'expression' => sprintf('last(/%s/%s) >= 500', $hostName, $statusKey),
+ 'priority' => self::PRIORITY_HIGH,
+ 'tags' => [
+ ['tag' => 'slo', 'value' => 'availability'],
+ ['tag' => 'class', 'value' => 'error'],
+ ],
+ ],
+ [
+ 'description' => 'HTTP 4xx error detected on ' . $hostName,
+ 'expression' => sprintf('last(/%s/%s) >= 400 and last(/%s/%s) < 500', $hostName, $statusKey, $hostName, $statusKey),
+ 'priority' => self::PRIORITY_AVERAGE,
+ 'tags' => [
+ ['tag' => 'class', 'value' => 'client-error'],
+ ],
+ ],
+ [
+ 'description' => 'Login failure spike detected on ' . $hostName,
+ 'expression' => sprintf('avg(/%s/%s,5m) > 10', $hostName, $loginFailureKey),
+ 'priority' => self::PRIORITY_HIGH,
+ 'tags' => [
+ ['tag' => 'security', 'value' => 'brute-force'],
+ ['tag' => 'class', 'value' => 'security'],
+ ],
+ ],
+ [
+ 'description' => 'Exception rate high on ' . $hostName,
+ 'expression' => sprintf('count(/%s/%s,5m) > 5', $hostName, $exceptionKey),
+ 'priority' => self::PRIORITY_WARNING,
+ 'tags' => [
+ ['tag' => 'class', 'value' => 'error'],
+ ],
+ ],
+ [
+ 'description' => 'Failed messages in queue on ' . $hostName,
+ 'expression' => sprintf('last(/%s/%s) > 0', $hostName, $messengerFailedKey),
+ 'priority' => self::PRIORITY_WARNING,
+ 'tags' => [
+ ['tag' => 'class', 'value' => 'messenger'],
+ ],
+ ],
+ ];
+ }
+
+ /**
+ * @param array $triggerDef
+ */
+ private function ensureTrigger(array $triggerDef, string $hostId): void
+ {
+ $existingTriggers = $this->triggerAction->get([
+ 'hostids' => [$hostId],
+ 'filter' => ['description' => $triggerDef['description']],
+ 'output' => ['triggerid'],
+ ]);
+
+ if (count($existingTriggers->triggers) > 0) {
+ $this->logger->debug('Trigger already exists', ['description' => $triggerDef['description']]);
+
+ return;
+ }
+
+ try {
+ $result = $this->triggerAction->create([$triggerDef]);
+
+ if (is_array($result)) {
+ Assert::keyExists($result, 'triggerids', 'Failed to create trigger');
+
+ $triggerIds = $result['triggerids'];
+ $triggerId = is_array($triggerIds) && isset($triggerIds[0]) ? $triggerIds[0] : null;
+
+ $this->logger->info('Trigger created', [
+ 'description' => $triggerDef['description'],
+ 'triggerid' => $triggerId,
+ ]);
+ }
+ } catch (\Throwable $e) {
+ $this->logger->error('Failed to create trigger', [
+ 'description' => $triggerDef['description'],
+ 'error' => $e->getMessage(),
+ ]);
+ }
+ }
+}
diff --git a/src/Setup/ZabbixSetup.php b/src/Setup/ZabbixSetup.php
index 48e326c..eae9e13 100644
--- a/src/Setup/ZabbixSetup.php
+++ b/src/Setup/ZabbixSetup.php
@@ -23,6 +23,7 @@ public function __construct(
private LoggerInterface $logger,
#[Autowire('%zabbix_api.setup_enabled%')]
private bool $setupEnabled,
+ private ?TriggerProvisioner $triggerProvisioner = null,
) {
}
@@ -50,6 +51,8 @@ public function ensureAll(): void
$this->registry->setHostId($hostId);
$this->ensureItems($hostId);
+
+ $this->ensureTriggers($hostId);
}
public function ensureHost(): string
@@ -62,8 +65,13 @@ public function ensureHost(): string
'output' => ['hostid'],
]);
- if (\count($result) > 0 && !empty($result[0]['hostid'])) {
- return $result[0]['hostid'];
+ Assert::isArray($result);
+ if (count($result) > 0) {
+ Assert::isArray($result[0]);
+ $hostId = $result[0]['hostid'] ?? null;
+ if (is_string($hostId) && $hostId !== '') {
+ return $hostId;
+ }
}
$groupId = $this->ensureHostGroup();
@@ -89,7 +97,11 @@ public function ensureHost(): string
],
]);
+ Assert::isArray($result);
+ Assert::keyExists($result, 'hostids');
+ Assert::isArray($result['hostids']);
$hostId = $result['hostids'][0];
+ Assert::string($hostId);
$this->logger->info('Zabbix host created', ['host' => $hostName, 'hostid' => $hostId]);
return $hostId;
@@ -104,16 +116,24 @@ private function ensureHostGroup(): string
'output' => ['groupid'],
]);
- if (\count($result) > 0 && !empty($result[0]['groupid'])) {
- return $result[0]['groupid'];
+ Assert::isArray($result);
+ if (count($result) > 0) {
+ Assert::isArray($result[0]);
+ $groupId = $result[0]['groupid'] ?? null;
+ if (is_string($groupId) && $groupId !== '') {
+ return $groupId;
+ }
}
$result = $this->client->call(ZabbixAction::HOSTGROUP_CREATE, [
'name' => $hostGroup,
]);
+ Assert::isArray($result);
Assert::keyExists($result, 'groupids', 'Failed to create Zabbix host group');
+ Assert::isArray($result['groupids']);
$groupId = $result['groupids'][0];
+ Assert::string($groupId);
$this->logger->info('Zabbix host group created', ['group' => $hostGroup, 'groupid' => $groupId]);
return $groupId;
@@ -124,6 +144,7 @@ private function ensureItems(string $hostId): void
$itemDefinitions = $this->registry->getAllItemDefinitions();
foreach ($itemDefinitions as $suffix => $definition) {
+ Assert::isArray($definition);
$key = $this->registry->getFullItemKey($suffix);
$result = $this->client->call(ZabbixAction::ITEM_GET, [
@@ -132,22 +153,36 @@ private function ensureItems(string $hostId): void
'output' => ['itemid'],
]);
- if (\count($result) > 0 && !empty($result[0]['itemid'])) {
- $itemId = $result[0]['itemid'];
- $this->registry->setItemId($key, $itemId);
-
- continue;
+ Assert::isArray($result);
+ if (count($result) > 0) {
+ Assert::isArray($result[0]);
+ $itemId = $result[0]['itemid'] ?? null;
+ if (is_string($itemId) && $itemId !== '') {
+ $this->registry->setItemId($key, $itemId);
+ continue;
+ }
}
try {
- $result = $this->client->call(ZabbixAction::ITEM_CREATE, [
+ Assert::keyExists($definition, 'name');
+ Assert::keyExists($definition, 'type');
+ Assert::keyExists($definition, 'value_type');
+ Assert::keyExists($definition, 'history');
+
+ $itemData = [
'name' => $definition['name'],
'key_' => $key,
'hostid' => $hostId,
'type' => $definition['type'],
'value_type' => $definition['value_type'],
'history' => $definition['history'],
- ]);
+ ];
+
+ if (isset($definition['units'])) {
+ $itemData['units'] = $definition['units'];
+ }
+
+ $result = $this->client->call(ZabbixAction::ITEM_CREATE, $itemData);
} catch (ZabbixApiException $e) {
$this->logger->error('Failed to create Zabbix item', [
'key' => $key,
@@ -158,10 +193,23 @@ private function ensureItems(string $hostId): void
continue;
}
- Assert::keyExists($result, 'itemids', \sprintf('Failed to create Zabbix item, expected key "itemids" in response, got %s', implode(',', array_keys($result))));
+ Assert::isArray($result);
+ Assert::keyExists($result, 'itemids', sprintf('Failed to create Zabbix item, expected key "itemids" in response, got %s', implode(',', array_keys($result))));
+ Assert::isArray($result['itemids']);
$itemId = $result['itemids'][0];
+ Assert::string($itemId);
$this->registry->setItemId($key, $itemId);
$this->logger->info('Zabbix item created', ['key' => $key, 'itemid' => $itemId]);
}
}
+
+ private function ensureTriggers(string $hostId): void
+ {
+ if ($this->triggerProvisioner === null) {
+ return;
+ }
+
+ $hostName = $this->naming->getHostName();
+ $this->triggerProvisioner->provisionTriggers($hostId, $hostName);
+ }
}
diff --git a/src/Subscriber/DoctrineEntitySubscriber.php b/src/Subscriber/DoctrineEntitySubscriber.php
index 67fd168..fb9f8ad 100644
--- a/src/Subscriber/DoctrineEntitySubscriber.php
+++ b/src/Subscriber/DoctrineEntitySubscriber.php
@@ -41,25 +41,25 @@ public function __construct(
$excluded[] = $class;
}
}
- $this->excludedEntityClasses = array_unique($excluded);
+ $this->excludedEntityClasses = array_values(array_unique($excluded));
}
public function postPersist(PostPersistEventArgs $event): void
{
- $this->dispatch($event->getObject(), 'insert');
+ $this->dispatch($event->getObject(), 'entity.persist.success');
}
public function postUpdate(PostUpdateEventArgs $event): void
{
- $this->dispatch($event->getObject(), 'update');
+ $this->dispatch($event->getObject(), 'entity.update.success');
}
public function postRemove(PostRemoveEventArgs $event): void
{
- $this->dispatch($event->getObject(), 'delete');
+ $this->dispatch($event->getObject(), 'entity.remove.success');
}
- private function dispatch(object $entity, string $operation): void
+ private function dispatch(object $entity, string $metricKey): void
{
if ($this->isExcluded($entity)) {
return;
@@ -68,12 +68,11 @@ private function dispatch(object $entity, string $operation): void
$entityClass = str_replace('\\', '.', $entity::class);
$this->bus->dispatch(new PushMetricMessage(
- key: $this->naming->getItemKey('doctrine.entity_change'),
+ key: $this->naming->getItemKey($metricKey),
value: 1,
tags: [
'env' => $this->appEnv,
'entity' => $entityClass,
- 'operation' => $operation,
],
));
}
diff --git a/src/Subscriber/ExceptionSubscriber.php b/src/Subscriber/ExceptionSubscriber.php
index eb47a54..dbecd35 100644
--- a/src/Subscriber/ExceptionSubscriber.php
+++ b/src/Subscriber/ExceptionSubscriber.php
@@ -37,7 +37,7 @@ public function __construct(
$excluded[] = $class;
}
}
- $this->excludedExceptionClasses = array_unique($excluded);
+ $this->excludedExceptionClasses = array_values(array_unique($excluded));
}
public static function getSubscribedEvents(): array
@@ -50,8 +50,10 @@ public static function getSubscribedEvents(): array
public function onException(ExceptionEvent $event): void
{
$req = $event->getRequest();
- $cid = (string) $req->attributes->get('_mon_cid', '');
- $route = (string) ($req->attributes->get('_route') ?? 'unknown');
+ $cidRaw = $req->attributes->get('_mon_cid', '');
+ $cid = is_string($cidRaw) ? $cidRaw : '';
+ $routeRaw = $req->attributes->get('_route');
+ $route = is_string($routeRaw) ? $routeRaw : 'unknown';
$exception = $event->getThrowable();
diff --git a/src/Subscriber/MessengerMonitoringSubscriber.php b/src/Subscriber/MessengerMonitoringSubscriber.php
new file mode 100644
index 0000000..88a5763
--- /dev/null
+++ b/src/Subscriber/MessengerMonitoringSubscriber.php
@@ -0,0 +1,86 @@
+ 'onMessageReceived',
+ WorkerMessageHandledEvent::class => 'onMessageHandled',
+ WorkerMessageFailedEvent::class => 'onMessageFailed',
+ ];
+ }
+
+ public function onMessageReceived(WorkerMessageReceivedEvent $event): void
+ {
+ $messageClass = $this->getShortClassName($event->getEnvelope()->getMessage()::class);
+
+ $this->bus->dispatch(new PushMetricMessage(
+ key: $this->naming->getItemKey('messenger.received'),
+ value: 1,
+ tags: [
+ 'env' => $this->appEnv,
+ 'message_class' => $messageClass,
+ 'transport' => $event->getReceiverName(),
+ ],
+ ));
+ }
+
+ public function onMessageHandled(WorkerMessageHandledEvent $event): void
+ {
+ $messageClass = $this->getShortClassName($event->getEnvelope()->getMessage()::class);
+
+ $this->bus->dispatch(new PushMetricMessage(
+ key: $this->naming->getItemKey('messenger.handled'),
+ value: 1,
+ tags: [
+ 'env' => $this->appEnv,
+ 'message_class' => $messageClass,
+ ],
+ ));
+ }
+
+ public function onMessageFailed(WorkerMessageFailedEvent $event): void
+ {
+ $messageClass = $this->getShortClassName($event->getEnvelope()->getMessage()::class);
+ $errorClass = $this->getShortClassName($event->getThrowable()::class);
+
+ $this->bus->dispatch(new PushMetricMessage(
+ key: $this->naming->getItemKey('messenger.failed.count'),
+ value: 1,
+ tags: [
+ 'env' => $this->appEnv,
+ 'message_class' => $messageClass,
+ 'error_class' => $errorClass,
+ ],
+ ));
+ }
+
+ private function getShortClassName(string $fqcn): string
+ {
+ $parts = explode('\\', $fqcn);
+
+ return end($parts);
+ }
+}
diff --git a/src/Subscriber/RequestTransactionSubscriber.php b/src/Subscriber/RequestTransactionSubscriber.php
index bd53831..7c144e3 100644
--- a/src/Subscriber/RequestTransactionSubscriber.php
+++ b/src/Subscriber/RequestTransactionSubscriber.php
@@ -68,10 +68,12 @@ public function onTerminate(TerminateEvent $event): void
return;
}
- $cid = (string) $req->attributes->get('_mon_cid', '');
+ $cidRaw = $req->attributes->get('_mon_cid', '');
+ $cid = is_string($cidRaw) ? $cidRaw : '';
$durationMs = (hrtime(true) - $start) / 1_000_000;
- $route = (string) ($req->attributes->get('_route') ?? 'unknown');
+ $routeRaw = $req->attributes->get('_route');
+ $route = is_string($routeRaw) ? $routeRaw : 'unknown';
if (isset($this->excludedRoutes[$route])) {
return;
}
diff --git a/src/Subscriber/SecurityAuthSubscriber.php b/src/Subscriber/SecurityAuthSubscriber.php
index 1f2c100..6435893 100644
--- a/src/Subscriber/SecurityAuthSubscriber.php
+++ b/src/Subscriber/SecurityAuthSubscriber.php
@@ -74,7 +74,7 @@ public function onFailure(LoginFailureEvent $event): void
private function extractUserId(mixed $user): string
{
if (!\is_object($user)) {
- return (string) $user;
+ return is_string($user) ? $user : 'unknown';
}
if (method_exists($user, 'getId')) {
@@ -85,7 +85,8 @@ private function extractUserId(mixed $user): string
}
if (method_exists($user, 'getUserIdentifier')) {
- return (string) $user->getUserIdentifier();
+ $identifier = $user->getUserIdentifier();
+ return is_string($identifier) ? $identifier : 'unknown';
}
return 'unknown';
@@ -95,7 +96,8 @@ private function extractUserIdentifier(AuthenticationException $exception, mixed
{
if (\is_object($user)) {
if (method_exists($user, 'getUserIdentifier')) {
- return (string) $user->getUserIdentifier();
+ $identifier = $user->getUserIdentifier();
+ return is_string($identifier) ? $identifier : 'unknown';
}
if (method_exists($user, 'getId')) {
diff --git a/src/Support/ResponseValidator.php b/src/Support/ResponseValidator.php
index 48c0ba2..42c5dfc 100644
--- a/src/Support/ResponseValidator.php
+++ b/src/Support/ResponseValidator.php
@@ -26,7 +26,13 @@ public static function ensureArray(mixed $result): array
);
}
- return $result;
+ $typed = [];
+ foreach ($result as $key => $value) {
+ Assert::string($key, sprintf('Array key must be string, got %s', get_debug_type($key)));
+ $typed[$key] = $value;
+ }
+
+ return $typed;
}
/**
diff --git a/src/ZabbixClient.php b/src/ZabbixClient.php
index 1e3eb4c..2a90385 100644
--- a/src/ZabbixClient.php
+++ b/src/ZabbixClient.php
@@ -133,6 +133,9 @@ private function doLogin(): string
return $result;
}
+ /**
+ * @param array $params
+ */
private function executeApiCall(ZabbixAction $action, array $params, ?string $authToken): mixed
{
$method = $action->value;
@@ -166,7 +169,9 @@ private function executeApiCall(ZabbixAction $action, array $params, ?string $au
$data = $response->toArray();
if (isset($data['error']) && is_array($data['error'])) {
- $error = ResponseValidator::ensureErrorStructure($data['error']);
+ $error = ResponseValidator::ensureErrorStructure(
+ ResponseValidator::ensureArray($data['error']),
+ );
throw new ZabbixApiException(
$error['message'],
$error['code'],
diff --git a/src/ZabbixClientInterface.php b/src/ZabbixClientInterface.php
index 47741a3..3cbdab0 100644
--- a/src/ZabbixClientInterface.php
+++ b/src/ZabbixClientInterface.php
@@ -8,5 +8,8 @@
interface ZabbixClientInterface
{
+ /**
+ * @param array $params
+ */
public function call(ZabbixAction $action, array $params = []): mixed;
}
diff --git a/tests/ActionTest.php b/tests/ActionTest.php
index 55b0d14..9631c99 100644
--- a/tests/ActionTest.php
+++ b/tests/ActionTest.php
@@ -5,9 +5,16 @@
namespace BytesCommerce\ZabbixApi\Tests;
use BytesCommerce\ZabbixApi\Actions\Action;
+use BytesCommerce\ZabbixApi\Actions\Dto\CreateActionDto;
+use BytesCommerce\ZabbixApi\Actions\Dto\CreateSingleActionDto;
+use BytesCommerce\ZabbixApi\Actions\Dto\DeleteActionDto;
+use BytesCommerce\ZabbixApi\Actions\Dto\GetActionDto;
use BytesCommerce\ZabbixApi\Actions\Dto\GetActionResponseDto;
+use BytesCommerce\ZabbixApi\Actions\Dto\UpdateActionDto;
+use BytesCommerce\ZabbixApi\Actions\Dto\UpdateSingleActionDto;
+use BytesCommerce\ZabbixApi\Enums\EventSourceEnum;
+use BytesCommerce\ZabbixApi\Enums\StatusEnum;
use BytesCommerce\ZabbixApi\Enums\ZabbixAction;
-use BytesCommerce\ZabbixApi\ZabbixApiException;
use BytesCommerce\ZabbixApi\ZabbixClientInterface;
use PHPUnit\Framework\TestCase;
@@ -25,7 +32,7 @@ protected function setUp(): void
public function testGetWithDefaultOutput(): void
{
- $params = ['filter' => ['eventsource' => 0]];
+ $dto = new GetActionDto(filter: ['eventsource' => 0]);
$expectedParams = ['filter' => ['eventsource' => 0], 'output' => 'extend'];
$apiResult = [['actionid' => '1', 'name' => 'Test Action', 'eventsource' => 0, 'esc_period' => '1h']];
@@ -34,7 +41,7 @@ public function testGetWithDefaultOutput(): void
->with(ZabbixAction::ACTION_GET, $expectedParams)
->willReturn($apiResult);
- $result = $this->action->get($params);
+ $result = $this->action->get($dto);
self::assertInstanceOf(GetActionResponseDto::class, $result);
self::assertCount(1, $result->actions);
@@ -44,15 +51,16 @@ public function testGetWithDefaultOutput(): void
public function testGetWithCustomOutput(): void
{
- $params = ['output' => ['actionid', 'name'], 'filter' => ['eventsource' => 0]];
+ $dto = new GetActionDto(output: 'extend', filter: ['eventsource' => 0]);
+ $expectedParams = ['output' => 'extend', 'filter' => ['eventsource' => 0]];
$apiResult = [['actionid' => '1', 'name' => 'Test Action', 'eventsource' => 0, 'esc_period' => '1h']];
$this->zabbixClient->expects(self::once())
->method('call')
- ->with(ZabbixAction::ACTION_GET, $params)
+ ->with(ZabbixAction::ACTION_GET, $expectedParams)
->willReturn($apiResult);
- $result = $this->action->get($params);
+ $result = $this->action->get($dto);
self::assertInstanceOf(GetActionResponseDto::class, $result);
self::assertCount(1, $result->actions);
@@ -62,88 +70,56 @@ public function testGetWithCustomOutput(): void
public function testCreateValid(): void
{
- $actions = [
- [
- 'name' => 'Auto-notify Admin',
- 'eventsource' => 0,
- 'esc_period' => '1h',
- 'operations' => [['operationtype' => 0, 'opmessage' => ['default_msg' => 1], 'opmessage_grp' => [['usrgrpid' => '7']]]]
- ]
- ];
+ $singleAction = new CreateSingleActionDto(
+ name: 'Auto-notify Admin',
+ eventsource: EventSourceEnum::TRIGGER,
+ esc_period: '1h',
+ operations: [['operationtype' => 0, 'opmessage' => ['default_msg' => 1], 'opmessage_grp' => [['usrgrpid' => '7']]]]
+ );
+ $dto = new CreateActionDto([$singleAction]);
+
$expectedResult = ['actionids' => ['15']];
$this->zabbixClient->expects(self::once())
->method('call')
- ->with(ZabbixAction::ACTION_CREATE, $actions)
+ ->with(ZabbixAction::ACTION_CREATE, self::anything())
->willReturn($expectedResult);
- $result = $this->action->create($actions);
-
- self::assertSame($expectedResult, $result);
- }
+ $result = $this->action->create($dto);
- public function testCreateInvalidMissingName(): void
- {
- $actions = [
- [
- 'eventsource' => 0,
- 'esc_period' => '1h',
- 'operations' => []
- ]
- ];
-
- $this->expectException(ZabbixApiException::class);
- $this->expectExceptionMessage('Action creation requires name, eventsource, esc_period, and operations');
-
- $this->action->create($actions);
+ self::assertSame(['15'], $result->actionids);
}
public function testUpdateValid(): void
{
- $actions = [
- [
- 'actionid' => '15',
- 'status' => 1
- ]
- ];
+ $singleAction = new UpdateSingleActionDto(
+ actionid: '15',
+ status: StatusEnum::DISABLED
+ );
+ $dto = new UpdateActionDto([$singleAction]);
+
$expectedResult = ['actionids' => ['15']];
$this->zabbixClient->expects(self::once())
->method('call')
- ->with(ZabbixAction::ACTION_UPDATE, $actions)
+ ->with(ZabbixAction::ACTION_UPDATE, self::anything())
->willReturn($expectedResult);
- $result = $this->action->update($actions);
+ $result = $this->action->update($dto);
- self::assertSame($expectedResult, $result);
- }
-
- public function testUpdateInvalidMissingActionId(): void
- {
- $actions = [
- [
- 'status' => 1
- ]
- ];
-
- $this->expectException(ZabbixApiException::class);
- $this->expectExceptionMessage('Action update requires actionid');
-
- $this->action->update($actions);
+ self::assertSame(['15'], $result->actionids);
}
public function testDelete(): void
{
- $actionIds = ['17', '18'];
- $expectedResult = ['actionids' => ['17', '18']];
+ $dto = new DeleteActionDto(['17', '18']);
$this->zabbixClient->expects(self::once())
->method('call')
- ->with(ZabbixAction::ACTION_DELETE, $actionIds)
- ->willReturn($expectedResult);
+ ->with(ZabbixAction::ACTION_DELETE, ['17', '18']);
- $result = $this->action->delete($actionIds);
+ $this->action->delete($dto);
- self::assertSame($expectedResult, $result);
+ self::assertTrue(true);
}
}
diff --git a/tests/AlertTest.php b/tests/AlertTest.php
index a010605..c0044a4 100644
--- a/tests/AlertTest.php
+++ b/tests/AlertTest.php
@@ -27,7 +27,21 @@ public function testGetWithDefaultOutput(): void
{
$params = ['userids' => '5'];
$expectedParams = ['userids' => '5', 'output' => 'extend'];
- $expectedResult = [['alertid' => '1', 'subject' => 'Test Alert']];
+ $expectedResult = [[
+ 'alertid' => '1',
+ 'actionid' => '10',
+ 'eventid' => '100',
+ 'userid' => '5',
+ 'clock' => 1672531200,
+ 'mediatypeid' => 1,
+ 'sendto' => 'admin@example.com',
+ 'subject' => 'Test Alert',
+ 'message' => 'Test Message',
+ 'status' => 0,
+ 'retries' => 0,
+ 'error' => '',
+ 'esc_step' => 1,
+ ]];
$this->zabbixClient->expects(self::once())
->method('call')
@@ -42,7 +56,21 @@ public function testGetWithDefaultOutput(): void
public function testGetWithCustomOutput(): void
{
$params = ['output' => ['alertid', 'subject', 'message'], 'status' => 1];
- $expectedResult = [['alertid' => '1', 'subject' => 'Test Alert', 'message' => 'Test Message']];
+ $expectedResult = [[
+ 'alertid' => '1',
+ 'actionid' => '10',
+ 'eventid' => '100',
+ 'userid' => '5',
+ 'clock' => 1672531200,
+ 'mediatypeid' => 1,
+ 'sendto' => 'admin@example.com',
+ 'subject' => 'Test Alert',
+ 'message' => 'Test Message',
+ 'status' => 1,
+ 'retries' => 0,
+ 'error' => '',
+ 'esc_step' => 1,
+ ]];
$this->zabbixClient->expects(self::once())
->method('call')
@@ -60,7 +88,21 @@ public function testGetWithDateTimeConversion(): void
$timeTill = new DateTime('2023-01-02 00:00:00');
$params = ['time_from' => $timeFrom, 'time_till' => $timeTill];
$expectedParams = ['time_from' => $timeFrom->getTimestamp(), 'time_till' => $timeTill->getTimestamp(), 'output' => 'extend'];
- $expectedResult = [['alertid' => '1']];
+ $expectedResult = [[
+ 'alertid' => '1',
+ 'actionid' => '10',
+ 'eventid' => '100',
+ 'userid' => '5',
+ 'clock' => 1672531200,
+ 'mediatypeid' => 1,
+ 'sendto' => 'admin@example.com',
+ 'subject' => 'Test',
+ 'message' => 'Test',
+ 'status' => 0,
+ 'retries' => 0,
+ 'error' => '',
+ 'esc_step' => 1,
+ ]];
$this->zabbixClient->expects(self::once())
->method('call')
@@ -76,7 +118,21 @@ public function testGetWithUnixTimestamps(): void
{
$params = ['time_from' => 1672531200, 'time_till' => 1672617600];
$expectedParams = ['time_from' => 1672531200, 'time_till' => 1672617600, 'output' => 'extend'];
- $expectedResult = [['alertid' => '1']];
+ $expectedResult = [[
+ 'alertid' => '1',
+ 'actionid' => '10',
+ 'eventid' => '100',
+ 'userid' => '5',
+ 'clock' => 1672531200,
+ 'mediatypeid' => 1,
+ 'sendto' => 'admin@example.com',
+ 'subject' => 'Test',
+ 'message' => 'Test',
+ 'status' => 0,
+ 'retries' => 0,
+ 'error' => '',
+ 'esc_step' => 1,
+ ]];
$this->zabbixClient->expects(self::once())
->method('call')
diff --git a/tests/AuditLogTest.php b/tests/AuditLogTest.php
index 3712c88..80cba39 100644
--- a/tests/AuditLogTest.php
+++ b/tests/AuditLogTest.php
@@ -31,7 +31,15 @@ public function testGetWithDefaultOutputAndSelectDetails(): void
'output' => 'extend',
'selectDetails' => 'extend',
];
- $expectedResult = [['auditid' => '1', 'clock' => '1672531200']];
+ $expectedResult = [[
+ 'auditid' => '1',
+ 'userid' => '1',
+ 'clock' => 1672531200,
+ 'action' => '1',
+ 'resourcetype' => '4',
+ 'resourceid' => '100',
+ 'resourcename' => 'Test Host',
+ ]];
$this->zabbixClient->expects(self::once())
->method('call')
@@ -50,7 +58,15 @@ public function testGetWithCustomOutputAndSelectDetails(): void
'selectDetails' => ['field', 'oldvalue', 'newvalue'],
'filter' => ['resourcetype' => 4, 'action' => 1],
];
- $expectedResult = [['auditid' => '1', 'clock' => '1672531200', 'resourcename' => 'Test Host']];
+ $expectedResult = [[
+ 'auditid' => '1',
+ 'userid' => '1',
+ 'clock' => 1672531200,
+ 'action' => '1',
+ 'resourcetype' => '4',
+ 'resourceid' => '100',
+ 'resourcename' => 'Test Host',
+ ]];
$this->zabbixClient->expects(self::once())
->method('call')
@@ -73,7 +89,15 @@ public function testGetWithDateTimeConversion(): void
'output' => 'extend',
'selectDetails' => 'extend',
];
- $expectedResult = [['auditid' => '1']];
+ $expectedResult = [[
+ 'auditid' => '1',
+ 'userid' => '1',
+ 'clock' => 1672531200,
+ 'action' => '1',
+ 'resourcetype' => '4',
+ 'resourceid' => '100',
+ 'resourcename' => 'Test Host',
+ ]];
$this->zabbixClient->expects(self::once())
->method('call')
@@ -95,7 +119,15 @@ public function testGetWithUnixTimestamps(): void
'output' => 'extend',
'selectDetails' => 'extend',
];
- $expectedResult = [['auditid' => '1']];
+ $expectedResult = [[
+ 'auditid' => '1',
+ 'userid' => '1',
+ 'clock' => 1672531200,
+ 'action' => '1',
+ 'resourcetype' => '4',
+ 'resourceid' => '100',
+ 'resourcename' => 'Test Host',
+ ]];
$this->zabbixClient->expects(self::once())
->method('call')
@@ -116,7 +148,15 @@ public function testGetWithResourceTypeAndAction(): void
'output' => 'extend',
'selectDetails' => 'extend',
];
- $expectedResult = [['auditid' => '1', 'resourcename' => 'Updated Host']];
+ $expectedResult = [[
+ 'auditid' => '1',
+ 'userid' => '1',
+ 'clock' => 1672531200,
+ 'action' => '1',
+ 'resourcetype' => '4',
+ 'resourceid' => '100',
+ 'resourcename' => 'Updated Host',
+ ]];
$this->zabbixClient->expects(self::once())
->method('call')
diff --git a/tests/EventTest.php b/tests/EventTest.php
index b5e2a90..ada87d3 100644
--- a/tests/EventTest.php
+++ b/tests/EventTest.php
@@ -27,7 +27,16 @@ public function testGetWithDefaultOutput(): void
{
$params = ['source' => Event::SOURCE_TRIGGER, 'value' => 1];
$expectedParams = ['source' => Event::SOURCE_TRIGGER, 'value' => 1, 'output' => 'extend'];
- $expectedResult = [['eventid' => '1', 'clock' => '1672531200']];
+ $expectedResult = [[
+ 'eventid' => '1',
+ 'source' => 0,
+ 'object' => 0,
+ 'objectid' => 100,
+ 'clock' => 1672531200,
+ 'value' => 1,
+ 'acknowledged' => 0,
+ 'ns' => 0,
+ ]];
$this->zabbixClient->expects(self::once())
->method('call')
@@ -46,7 +55,16 @@ public function testGetWithCustomOutput(): void
'selectHosts' => ['hostid', 'name'],
'source' => Event::SOURCE_TRIGGER,
];
- $expectedResult = [['eventid' => '1', 'clock' => '1672531200', 'value' => 1]];
+ $expectedResult = [[
+ 'eventid' => '1',
+ 'source' => 0,
+ 'object' => 0,
+ 'objectid' => 100,
+ 'clock' => 1672531200,
+ 'value' => 1,
+ 'acknowledged' => 0,
+ 'ns' => 0,
+ ]];
$this->zabbixClient->expects(self::once())
->method('call')
@@ -132,20 +150,17 @@ public function testAcknowledgeInvalidActionNotInt(): void
public function testConstants(): void
{
- // Test source constants
self::assertSame(0, Event::SOURCE_TRIGGER);
self::assertSame(1, Event::SOURCE_DISCOVERY);
self::assertSame(2, Event::SOURCE_AUTOREGISTRATION);
self::assertSame(3, Event::SOURCE_INTERNAL);
self::assertSame(4, Event::SOURCE_SERVICE);
- // Test action constants
self::assertSame(1, Event::ACTION_CLOSE);
self::assertSame(2, Event::ACTION_ACKNOWLEDGE);
self::assertSame(4, Event::ACTION_MESSAGE);
self::assertSame(8, Event::ACTION_SEVERITY);
- // Test bitmask combinations
$acknowledgeAndMessage = Event::ACTION_ACKNOWLEDGE | Event::ACTION_MESSAGE;
self::assertSame(6, $acknowledgeAndMessage);
diff --git a/tests/HostTest.php b/tests/HostTest.php
index 1e1c7af..fd4fe08 100644
--- a/tests/HostTest.php
+++ b/tests/HostTest.php
@@ -27,7 +27,11 @@ public function testGetWithDefaultOutput(): void
{
$params = ['filter' => ['status' => 0]];
$expectedParams = ['filter' => ['status' => 0], 'output' => 'extend'];
- $expectedResult = [['hostid' => '1', 'host' => 'Test Host']];
+ $expectedResult = [[
+ 'hostid' => '1',
+ 'host' => 'Test Host',
+ 'status' => 0,
+ ]];
$this->zabbixClient->expects(self::once())
->method('call')
@@ -46,7 +50,12 @@ public function testGetWithCustomOutput(): void
'selectInterfaces' => 'extend',
'selectGroups' => 'extend',
];
- $expectedResult = [['hostid' => '1', 'host' => 'Test Host', 'name' => 'Test Host Display']];
+ $expectedResult = [[
+ 'hostid' => '1',
+ 'host' => 'Test Host',
+ 'name' => 'Test Host Display',
+ 'status' => 0,
+ ]];
$this->zabbixClient->expects(self::once())
->method('call')
diff --git a/tests/Integration/ZabbixApiBundleTest.php b/tests/Integration/ZabbixApiBundleTest.php
index d84c68b..3850608 100644
--- a/tests/Integration/ZabbixApiBundleTest.php
+++ b/tests/Integration/ZabbixApiBundleTest.php
@@ -23,14 +23,13 @@ public function testServicesAreRegistered(): void
$container = new ContainerBuilder(new ParameterBag([
'kernel.debug' => false,
'kernel.environment' => 'test',
+ 'kernel.project_dir' => '/tmp',
+ 'env(ZABBIX_API_URL)' => 'https://zabbix.test/api_jsonrpc.php',
]));
- // Register required framework services as synthetic
$container->setDefinition('http_client', (new Definition(HttpClientInterface::class))->setSynthetic(true));
$container->setDefinition(LoggerInterface::class, (new Definition(LoggerInterface::class))->setSynthetic(true));
$container->setDefinition('cache.app', (new Definition(CacheInterface::class))->setSynthetic(true));
-
- // Set autowiring aliases so the container can resolve typed parameters
$container->setAlias(HttpClientInterface::class, 'http_client')->setPublic(false);
$container->setAlias(CacheInterface::class, 'cache.app')->setPublic(false);
@@ -48,7 +47,6 @@ public function testServicesAreRegistered(): void
],
], $container);
- // Make services public so we can verify they exist after compilation
$container->getDefinition(ZabbixClientInterface::class)->setPublic(true);
$container->getDefinition(ZabbixServiceInterface::class)->setPublic(true);
$container->getDefinition(ActionServiceInterface::class)->setPublic(true);
@@ -65,6 +63,8 @@ public function testParametersAreSet(): void
$container = new ContainerBuilder(new ParameterBag([
'kernel.debug' => false,
'kernel.environment' => 'test',
+ 'kernel.project_dir' => '/tmp',
+ 'env(ZABBIX_API_URL)' => 'https://zabbix.test/api_jsonrpc.php',
]));
$container->setDefinition('http_client', (new Definition(HttpClientInterface::class))->setSynthetic(true));
@@ -100,6 +100,8 @@ public function testDefaultConfigValues(): void
$container = new ContainerBuilder(new ParameterBag([
'kernel.debug' => false,
'kernel.environment' => 'test',
+ 'kernel.project_dir' => '/tmp',
+ 'env(ZABBIX_API_URL)' => 'https://zabbix.test/api_jsonrpc.php',
]));
$container->setDefinition('http_client', (new Definition(HttpClientInterface::class))->setSynthetic(true));
@@ -115,7 +117,9 @@ public function testDefaultConfigValues(): void
self::assertNotNull($extension);
$extension->load([
- ['base_uri' => 'https://zabbix.test/api_jsonrpc.php'],
+ [
+ 'base_uri' => 'https://zabbix.test/api_jsonrpc.php',
+ ],
], $container);
self::assertNull($container->getParameter('zabbix_api.username'));
diff --git a/tests/TriggerTest.php b/tests/TriggerTest.php
index d9c8e61..3f16b91 100644
--- a/tests/TriggerTest.php
+++ b/tests/TriggerTest.php
@@ -27,7 +27,11 @@ public function testGetWithDefaultOutput(): void
{
$params = ['filter' => ['value' => 1]];
$expectedParams = ['filter' => ['value' => 1], 'output' => 'extend'];
- $expectedResult = [['triggerid' => '1', 'description' => 'Test Trigger']];
+ $expectedResult = [[
+ 'triggerid' => '1',
+ 'description' => 'Test Trigger',
+ 'expression' => 'last(/host/system.cpu.load)>5',
+ ]];
$this->zabbixClient->expects(self::once())
->method('call')
@@ -42,7 +46,12 @@ public function testGetWithDefaultOutput(): void
public function testGetWithCustomOutput(): void
{
$params = ['output' => ['triggerid', 'description', 'priority'], 'selectHosts' => ['hostid', 'name']];
- $expectedResult = [['triggerid' => '1', 'description' => 'Test Trigger', 'priority' => 4]];
+ $expectedResult = [[
+ 'triggerid' => '1',
+ 'description' => 'Test Trigger',
+ 'expression' => 'last(/host/system.cpu.load)>5',
+ 'priority' => 4,
+ ]];
$this->zabbixClient->expects(self::once())
->method('call')
diff --git a/tests/ZabbixClientTest.php b/tests/ZabbixClientTest.php
index a8f5c25..3ae4d5c 100644
--- a/tests/ZabbixClientTest.php
+++ b/tests/ZabbixClientTest.php
@@ -30,7 +30,10 @@ protected function setUp(): void
$this->logger = $this->createMock(LoggerInterface::class);
$this->cache = $this->createMock(CacheInterface::class);
- // Use API token auth for simple test cases (bypasses cache/login flow)
+ $this->cache->method('get')
+ ->with('zabbix_bearer_token', self::anything())
+ ->willReturn('test-api-token');
+
$this->zabbixClient = new ZabbixClient(
username: null,
password: null,
@@ -72,9 +75,9 @@ public function testCallWithError(): void
->method('toArray')
->willReturn([
'error' => [
- 'code' => -32602,
- 'message' => 'Invalid params',
- 'data' => 'Invalid parameter',
+ 'code' => -32700,
+ 'message' => 'Parse error',
+ 'data' => 'Invalid JSON',
],
]);
@@ -83,7 +86,7 @@ public function testCallWithError(): void
->willReturn($response);
$this->expectException(ZabbixApiException::class);
- $this->expectExceptionMessage('Invalid params');
+ $this->expectExceptionMessage('Parse error');
$this->zabbixClient->call(ZabbixAction::HOST_GET);
}
@@ -102,28 +105,26 @@ public function testCallWithHttpError(): void
public function testCallWithUsernamePasswordAuth(): void
{
+ $cache = $this->createMock(CacheInterface::class);
+ $cache->method('get')
+ ->with('zabbix_bearer_token', self::anything())
+ ->willReturnCallback(function (string $key, callable $callback): string {
+ $item = $this->createMock(ItemInterface::class);
+ $item->method('expiresAfter');
+
+ return $callback($item);
+ });
+
$client = new ZabbixClient(
username: 'testuser',
password: 'testpass',
apiToken: null,
httpClient: $this->httpClient,
logger: $this->logger,
- cache: $this->cache,
+ cache: $cache,
authTtl: 3600,
);
- // Mock cache->get() to simulate login and return a token
- $this->cache->expects(self::once())
- ->method('get')
- ->with('zabbix_bearer_token', self::anything())
- ->willReturnCallback(function (string $key, callable $callback): string {
- $item = $this->createMock(ItemInterface::class);
- $item->expects(self::once())->method('expiresAfter')->with(3600);
-
- return $callback($item);
- });
-
- // Expect two HTTP calls: one for login, one for the actual API call
$loginResponse = $this->createMock(ResponseInterface::class);
$loginResponse->method('toArray')->willReturn(['result' => 'auth-token-123']);