Skip to content

Commit d04a112

Browse files
authored
Update Exception Handling (#15)
Internalize `GA4Exception`'s when iterating data without wanting to throw midways.
2 parents 19c3c7a + 6e08a74 commit d04a112

File tree

5 files changed

+124
-58
lines changed

5 files changed

+124
-58
lines changed

src/Analytics.php

Lines changed: 13 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -144,15 +144,10 @@ public function addEvent(Model\Event $event)
144144
*/
145145
public function post()
146146
{
147-
$errorStack = null;
148-
149147
$url = $this->debug ? $this::URL_DEBUG : $this::URL_LIVE;
150148
$url .= '?' . http_build_query(['measurement_id' => $this->measurement_id, 'api_secret' => $this->api_secret]);
151149

152-
$catch = parent::toArray(true, $errorStack);
153-
$errorStack = $catch['error'];
154-
$reqBody = $catch['data'];
155-
150+
$reqBody = parent::toArray(true);
156151

157152
$eventsList = array_chunk($reqBody['events'] ?? [], 25);
158153

@@ -161,40 +156,40 @@ public function post()
161156

162157
$kB = 1024;
163158
if (mb_strlen(json_encode($reqBody)) > ($kB * 130)) {
164-
$errorStack = new GA4Exception("Request body exceeds 130kB", $errorStack);
165-
}
166-
167-
if ($errorStack instanceof GA4Exception) {
168-
throw $errorStack;
159+
GA4Exception::push("Request body exceeds 130kB");
169160
}
170161

171162
$guzzle = new Guzzle();
172163
$res = $guzzle->request('POST', $url, ['json' => $reqBody]);
173164

174165
$resCode = $res->getStatusCode() ?? 0;
175166
if ($resCode !== 200) {
176-
$errorStack = new GA4Exception("Request received code {$resCode}", $errorStack);
167+
GA4Exception::push("Request received code {$resCode}");
177168
}
178169

179170
$resBody = $res->getBody()->getContents();
180171
$data = @json_decode($resBody, true);
181172

182173
if (empty($resBody)) {
183-
$errorStack = new GA4Exception("Received not body", $errorStack);
174+
GA4Exception::push("Received not body");
184175
} elseif (json_last_error() != JSON_ERROR_NONE || $data === null) {
185-
$errorStack = new GA4Exception("Could not parse response", $errorStack);
176+
GA4Exception::push("Could not parse response");
186177
} elseif (!empty($data['validationMessages'])) {
187178
foreach ($data['validationMessages'] as $msg) {
188-
$errorStack = new GA4Exception('Validation Message: ' . $msg['validationCode'] . '[' . $msg['fieldPath'] . ']: ' . $msg['description'], $errorStack);
179+
GA4Exception::push(
180+
'Validation Message: ' . $msg['validationCode']
181+
. (isset($msg['fieldPath']) ? '[' . $msg['fieldPath'] . ']: ' : ':')
182+
. $msg['description']
183+
);
189184
}
190185
}
191186
}
192187

193-
if ($errorStack instanceof GA4Exception) {
194-
throw $errorStack;
188+
if (GA4Exception::hasStack()) {
189+
throw GA4Exception::getFinalStack();
195190
}
196191

197-
$this->events = []; // Reset events list
192+
$this->events = [];
198193

199194
return true;
200195
}

src/GA4Exception.php

Lines changed: 88 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,94 @@
44

55
class GA4Exception extends \Exception
66
{
7-
public function __construct(string $message = "", $previous = null)
7+
/**
8+
* Contains ongoing stack of errors that can be returned as chained exceptions
9+
*
10+
* @var AlexWestergaard\PhpGa4\GA4Exception
11+
*/
12+
private static $GA4ExceptionStack;
13+
14+
public function __construct(string $message = "", int $code = 0)
15+
{
16+
parent::__construct($message, $code, static::getStack());
17+
}
18+
19+
/**
20+
* Add current exception to stack
21+
*
22+
* @return static
23+
*/
24+
public function add()
25+
{
26+
self::$GA4ExceptionStack = $this;
27+
return $this;
28+
}
29+
30+
/**
31+
* Clean stack - will not affect current instance
32+
*
33+
* @return static
34+
*/
35+
public function clean()
36+
{
37+
static::resetStack();
38+
return $this;
39+
}
40+
41+
/**
42+
* Add new GA4Exception to stack without further action
43+
*
44+
* @param string $message
45+
* @param int $code
46+
* @return void
47+
*/
48+
public static function push(string $message, int $code = 0)
49+
{
50+
self::$GA4ExceptionStack = new static($message, $code);
51+
}
52+
53+
/**
54+
* Check if the stack has any instances
55+
*
56+
* @return bool
57+
*/
58+
public static function hasStack()
59+
{
60+
return static::getStack() !== null;
61+
}
62+
63+
/**
64+
* Return unmodified stack
65+
*
66+
* @return AlexWestergaard\PhpGa4\GA4Exception|null
67+
*/
68+
public static function getStack()
69+
{
70+
return isset(self::$GA4ExceptionStack) && self::$GA4ExceptionStack instanceof GA4Exception ? self::$GA4ExceptionStack : null;
71+
}
72+
73+
/**
74+
* Returns stack and resets/cleans the stack; this one should be used when throwing \
75+
* to avoid future try-catch blocks will hit the stack build during current run.
76+
*
77+
* @return AlexWestergaard\PhpGa4\GA4Exception|null
78+
*/
79+
public static function getFinalStack()
80+
{
81+
$stack = static::getStack();
82+
83+
static::resetStack();
84+
85+
return $stack;
86+
}
87+
88+
/**
89+
* Resets/cleans the stack
90+
*
91+
* @return void
92+
*/
93+
public static function resetStack()
894
{
9-
parent::__construct($message, 0, $previous);
95+
self::$GA4ExceptionStack = null;
1096
}
1197
}

src/Model/Event.php

Lines changed: 11 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -17,24 +17,20 @@ abstract public function getName(): string;
1717
/**
1818
* @param GA4Exception $childErrors
1919
*/
20-
public function toArray(bool $isParent = false, $childErrors = null): array
20+
public function toArray(bool $isParent = false): array
2121
{
22-
if (!($childErrors instanceof GA4Exception) && $childErrors !== null) {
23-
throw new GA4Exception("$childErrors is neither NULL of instance of GA4Exception");
24-
}
2522
$return = [];
26-
$errorStack = null;
2723

2824
if (!method_exists($this, 'getName')) {
29-
$errorStack = new GA4Exception("'self::getName()' does not exist", $errorStack);
25+
GA4Exception::push("'self::getName()' does not exist");
3026
} else {
3127
$name = $this->getName();
3228
if (empty($name)) {
33-
$errorStack = new GA4Exception("Name '{$name}' can not be empty", $errorStack);
29+
GA4Exception::push("Name '{$name}' can not be empty");
3430
} elseif (strlen($name) > 40) {
35-
$errorStack = new GA4Exception("Name '{$name}' can not be longer than 40 characters", $errorStack);
31+
GA4Exception::push("Name '{$name}' can not be longer than 40 characters");
3632
} elseif (preg_match('/[^\w\d\-]/', $name)) {
37-
$errorStack = new GA4Exception("Name '{$name}' can only be letters, numbers, underscores and dashes", $errorStack);
33+
GA4Exception::push("Name '{$name}' can only be letters, numbers, underscores and dashes");
3834
} elseif (in_array($name, [
3935
'ad_activeview',
4036
'ad_click',
@@ -59,21 +55,20 @@ public function toArray(bool $isParent = false, $childErrors = null): array
5955
'session_start',
6056
'user_engagement',
6157
])) {
62-
$errorStack = new GA4Exception("Name '{$name}' is reserved", $errorStack);
58+
GA4Exception::push("Name '{$name}' is reserved");
6359
} else {
6460
$return['name'] = $name;
6561
}
6662
}
6763

68-
$catch = parent::toArray(true, $errorStack);
69-
$errorStack = $catch['error'];
64+
$parent = parent::toArray(true);
7065

71-
if (is_array($catch['data']) && !empty($catch['data'])) {
72-
$return['params'] = $catch['data'];
66+
if (!$isParent && GA4Exception::hasStack()) {
67+
throw GA4Exception::getFinalStack();
7368
}
7469

75-
if ($errorStack instanceof GA4Exception) {
76-
throw $errorStack;
70+
if (!empty($parent)) {
71+
$return['params'] = $parent;
7772
}
7873

7974
return $return;

src/Model/ToArray.php

Lines changed: 8 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -14,59 +14,45 @@ abstract public function getRequiredParams(): array;
1414
/**
1515
* @param GA4Exception $childErrors
1616
*/
17-
public function toArray(bool $isParent = false, $childErrors = null): array
17+
public function toArray(bool $isParent = false): array
1818
{
19-
if (!($childErrors instanceof GA4Exception) && $childErrors !== null) {
20-
throw new GA4Exception("$childErrors is neither NULL of instance of GA4Exception");
21-
}
22-
2319
$return = [];
24-
$errorStack = null;
25-
26-
if ($isParent !== null) {
27-
$errorStack = $childErrors;
28-
}
2920

3021
$required = $this->getRequiredParams();
3122
$params = array_unique(array_merge($this->getParams(), $required));
3223

3324
foreach ($params as $param) {
3425
if (!property_exists($this, $param)) {
35-
$errorStack = new GA4Exception("Param '{$param}' is not defined", $errorStack);
26+
GA4Exception::push("Param '{$param}' is not defined");
3627
continue;
3728
} elseif (!isset($this->{$param})) {
3829
if (in_array($param, $required)) {
39-
$errorStack = new GA4Exception("Param '{$param}' is required but not set", $errorStack);
30+
GA4Exception::push("Param '{$param}' is required but not set");
4031
}
4132
continue;
4233
} elseif (empty($this->{$param}) && (is_array($this->{$param}) || strval($this->{$param}) !== '0')) {
4334
if (in_array($param, $required)) {
44-
$errorStack = new GA4Exception("Param '{$param}' is required but empty", $errorStack);
35+
GA4Exception::push("Param '{$param}' is required but empty");
4536
}
4637
continue;
4738
}
4839

4940
if (strlen($param) > 40) {
50-
$errorStack = new GA4Exception("Param '{$param}' is too long, maximum is 40 characters", $errorStack);
41+
GA4Exception::push("Param '{$param}' is too long, maximum is 40 characters");
5142
}
5243

5344
$value = $this->{$param};
5445

5546
// Array values be handled and validated within setter, fx addItem/setItem
5647
if (!is_array($value) && mb_strlen($value) > 100) {
57-
$errorStack = new GA4Exception("Value '{$value}' is too long, maximum is 100 characters", $errorStack);
48+
GA4Exception::push("Value '{$value}' is too long, maximum is 100 characters");
5849
}
5950

6051
$return[$param] = $value;
6152
}
6253

63-
if ($isParent) {
64-
return [
65-
'data' => $return,
66-
'error' => $errorStack
67-
];
68-
} elseif ($errorStack instanceof GA4Exception) {
69-
throw $errorStack;
54+
if (!$isParent && GA4Exception::hasStack()) {
55+
throw GA4Exception::getFinalStack();
7056
}
7157

7258
return $return;

test/AnalyticsTest.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,10 @@ public function testPrebuildEvents()
192192
$event->setAchievementId('achievement_buy_5_items');
193193
}
194194

195+
if (in_array('group_id', $params)) {
196+
$event->setGroupId('999');
197+
}
198+
195199
$this->assertTrue(is_array($event->toArray()), $eventName);
196200

197201
$this->analytics->addEvent($event);

0 commit comments

Comments
 (0)