Skip to content

Commit 7e197a8

Browse files
authored
Major refactor of codebase for PHP8+ (#29)
- Removes Helper/Helper class - Constants moved into Facades, Helpers moved into Helper/Converter - Removes Model/*.php classes - Removes root GA4Exception.php - Updates Composer.json to better represent model and tested packages/PHP-Versions - Updates workflow to only test on Linux (Windows too slow) - Adds Type Facades to expected functionality and simplicy Paramter Type Expectation - Adds ArrayAccess and Iterator to classes with magic-references to set_ or add_ methods - Adds further Exceptions for more specific tryCatch optionality - Adds better testing of functionality, both core classes and Events - Adds output of PHPUnit Coverage in actions (No further usage atm) - Moves Facades into subfolder named Group/ - Deprecated badly named and bad usage methods * Requires custom events to be updated to implement `AlexWestergaard\PhpGa4\Facade\Type\Event` or extend `AlexWestergaard\PhpGa4\Helper\AbstractEvent` * Requires tryCatch to to use `AlexWestergaard\PhpGa4\Exception\Ga4Exception` for all exceptions thrown inside this library
2 parents 3e5c990 + 7408967 commit 7e197a8

File tree

96 files changed

+2300
-801
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

96 files changed

+2300
-801
lines changed

.github/workflows/ci.yml

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ jobs:
2929
max-parallel: 3
3030
fail-fast: true
3131
matrix:
32-
operating-system: ["ubuntu-latest", "windows-latest"]
32+
operating-system: ["ubuntu-latest"]
3333
php-versions: ["8.0", "8.1", "8.2"]
3434
phpunit-versions: ["latest"]
3535

@@ -45,7 +45,7 @@ jobs:
4545
php-version: ${{ matrix.php-versions }}
4646
extensions: mbstring, intl
4747
ini-values: post_max_size=256M, max_execution_time=180
48-
coverage: none
48+
coverage: xdebug
4949
tools: phpunit:${{ matrix.phpunit-versions }}
5050

5151
- name: "Composer State"
@@ -74,4 +74,7 @@ jobs:
7474
run: chmod -R 0755 vendor
7575

7676
- name: "PHPUnit"
77-
run: ./vendor/bin/phpunit --testdox
77+
run: ./vendor/bin/phpunit
78+
79+
- name: "Coverage"
80+
run: echo "$(cat ./.phpunit/coverage.txt)"

.github/workflows/clear-cache.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
1-
name: Clear all Github actions caches on sundays
1+
name: Clear Caches
22
on:
33
schedule:
4-
- cron: "0 0 * * 0"
4+
- cron: "30 2 1,15 * *"
55
workflow_dispatch:
66

77
jobs:
8-
my-job:
8+
remove-caches:
99
name: Delete all caches
1010
runs-on: ubuntu-20.04
1111

1212
steps:
1313
- name: Clear caches
14-
uses: easimon/wipe-cache@main
14+
uses: easimon/wipe-cache@main

.gitignore

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,4 @@
33
/composer.lock
44

55
# PHPUnit
6-
/.phpunit.cache
7-
/.phpunit.result.cache
6+
/.phpunit*

README.md

Lines changed: 79 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -4,25 +4,23 @@ PHP Wrapper for Google Analytics 4 with Server Side Tracking
44
[![Release Size](https://img.shields.io/github/languages/code-size/alexwestergaard/php-ga4?color=blue&style=for-the-badge)](https://github.com/AlexWestergaard/php-ga4/releases/latest)
55
[![Issues](https://img.shields.io/github/issues-raw/alexwestergaard/php-ga4?color=red&style=for-the-badge)](https://github.com/AlexWestergaard/php-ga4/issues)
66

7-
`composer require alexwestergaard/php-ga4`
8-
9-
- [GDPR](#gdpr-notice)
10-
- [Events](#events)
11-
- [Default](#default)
12-
- [E-commerce](#e-commerce)
13-
- [Engagement / Gaming](#engagement--gaming)
14-
- [Frontend \& Backend Communication](#frontend--backend-communication)
15-
- [Logged/Queued Events](#loggedqueued-events)
16-
- [Frontend to Backend communication](#frontend-to-backend-communication)
17-
- [Custom Events](#custom-events)
18-
- [Documentation](#documentation)
19-
20-
**LEGACY WARNING**
7+
```sh
8+
composer require alexwestergaard/php-ga4
9+
```
10+
11+
- [LEGACY](#legacy)
12+
13+
14+
## LEGACY
2115
- `PHP 7` should only use `1.0.*` versions of this library
2216

2317
## GDPR Notice
2418

25-
*European Union have noticed that default setup of Google Analytics does not comply with GDPR as data is sent unrestricted to an american service possibly outside of Europe. This includes the use of 'GTAG.js' as JavaScript pushes the request from visitors device including IP-Address. Server Side Tracking, however, does only send information specified inside the body and of your server. Relying solely on Google Analytics 4 Events - that is not pushed through the GTAG.js script - can be scraped of GDPR-related information.*
19+
> European Union have noticed that default setup of Google Analytics does not comply with GDPR as data is sent unrestricted to an american service possibly outside of Europe.
20+
>
21+
> This includes the use of `gtag.js`/`gtm.js` as JavaScript pushes the request from visitors device including their IP-Address. Server Side Tracking, however, does only send information specified inside the body and about your server.
22+
>
23+
> Relying solely on Google Analytics 4 Events - that is not pushed through the `gtag.js`/`gtm.js` script - can be scraped of GDPR-related information.
2624
2725
- Source: Europe, GDPR, Schrems II
2826
- https://support.google.com/analytics/answer/9019185?hl=en
@@ -72,120 +70,107 @@ This is a list of prebuilt events as shown in the documentation.
7270
## Frontend & Backend Communication
7371

7472
This library is built for backend server side tracking, but you will probably trigger most events through frontend with Javascript or Websockets. There will be 2 examples, one as pure backend for logged/queued events and one for frontend to backend communication.
75-
76-
### Logged/Queued Events
73+
74+
### Logging / Queues
7775

7876
```php
79-
use AlexWestergaard\PhpGa4\GA4Exception;
77+
78+
use AlexWestergaard\PhpGa4\Exception;
8079
use AlexWestergaard\PhpGa4\Analytics;
8180
use AlexWestergaard\PhpGa4\Event;
8281
use AlexWestergaard\PhpGa4\Item;
8382

84-
require_once __DIR__ . '/vendor/autoload.php';
83+
// require vendor/autoload.php
8584

86-
try {
87-
$analytics = Analytics::new('G-XXXXX', 'secret_api_key')
88-
->setClientId('session_id');
89-
// ^ If gtag.js, this can be the _ga or _gid cookie
90-
91-
if ($user) {
92-
$analytics->setUserId($user->id);
93-
// ^ This can be any kind of identifier, readable is easier for you
94-
}
85+
// If gtag.js, this can be the _ga or _gid cookie
86+
// This can be any kind of session identifier
87+
$session = $_COOKIE['_ga'] ?? $_COOKIE['_gid'] ?? $_COOKIE['PHPSESSID'];
9588

96-
$addToCart = Event\AddToCart::new()
97-
->setCurrency($cart->currency)
98-
->setValue($cart->total);
99-
100-
foreach($cart->products as $product) {
101-
$addToCart->addItem(
102-
Item::new()
103-
->setItemId($product['id'])
104-
->setItemName($product['name'])
105-
->setQuantity($product['quantity'])
106-
->setPrice($product['price_total'])
107-
->setItemVariant($product['colorName'])
108-
);
109-
}
89+
// Render events grouped on time
90+
foreach ($groups as $time => $data) {
91+
try {
92+
$analytics = Analytics::new($measurementId, $apiSecret)
93+
->setClientId($session)
94+
->setTimestampMicros($time);
11095

111-
$analytics->addEvent($addToCart);
96+
// load logged in user/visitor
97+
if ($auth) {
98+
// This can be any kind of identifier, readable is easier for you
99+
// Just be wary not to use GDPR sensitive information
100+
$analytics->setUserId($auth->id);
101+
}
112102

113-
// Errors are served as exceptions on pre-exit
114-
$analytics->post();
115-
} catch (GA4Exception $exception) {
116-
// Handle exception
117-
// Exceptions might be stacked, check: $exception->getPrevious();
118-
}
103+
$analytics->addUserParameter(...$data['userParameters']);
104+
$analytics->addEvent(...$data['events']);
119105

120-
//// ==============================================================
121-
// You can instanciate events with 'fromArray' method as of v1.0.9
122-
// This allows for quick-events by recursive instanciation
123-
$analytics->addEvent(
124-
Event\AddToCart::fromEvent([
125-
'currency' => $cart->currency,
126-
'value' => $cart->total,
127-
// Items must be array of Items models
128-
'items' => array_map(
129-
function ($product) {
130-
return Item::fromArray([
131-
'item_id' => $product->id,
132-
'item_name' => $product->name,
133-
'quantity' => $product->quantity,
134-
'price' => $product->price,
135-
]);
136-
},
137-
$cart->products
138-
),
139-
])
140-
);
141-
//// ==============================================================
106+
$analytics->post();
107+
} catch (Exception\Ga4Exception $exception) {
108+
// Handle exception
109+
// Exceptions might be stacked, check: $exception->getPrevious();
110+
}
111+
}
142112
```
143113

144-
### Frontend to Backend communication
114+
### Frontend => Backend
115+
116+
#### Frontend
145117

146118
```js
147-
axios.post('/api/ga4', {
148-
addToCart: {
149-
currency: 'EUR',
150-
value: 13.37,
151-
items: [
152-
{
153-
'item_id': 1,
154-
'item_name': 'Cup',
155-
'price': 13.37,
156-
'quantity': 1
157-
}
158-
]
119+
axios.post('/api/ga4', [
120+
{
121+
addToCart: {
122+
currency: 'EUR',
123+
value: 13.37,
124+
items: [
125+
{
126+
'item_id': 1,
127+
'item_name': 'Cup',
128+
'price': 13.37,
129+
'quantity': 1
130+
}
131+
]
132+
}
159133
}
160-
})
134+
])
161135
```
162136

137+
#### Backend
138+
163139
```php
140+
use AlexWestergaard\PhpGa4\Helper\Converter;
141+
use AlexWestergaard\PhpGa4\Exception;
164142
use AlexWestergaard\PhpGa4\Analytics;
165143
use AlexWestergaard\PhpGa4\Event;
166144

145+
// require vendor/autoload.php
146+
167147
try {
168-
$addToCart = Event\AddToCart::fromArray($_POST['addToCart']);
148+
$events = Converter::parseEvents($_POST);
149+
169150
Analytics::new($measurementId, $apiSecret)
170-
->addEvent($addToCart)
151+
->addEvent(...$events)
171152
->post();
172-
} catch (GA4Exception $exception) {
153+
} catch (Exception\Ga4Exception $exception) {
173154
// Handle exception
174155
// Exceptions might be stacked, check: $exception->getPrevious();
175156
}
176157
```
177158

178159
## Custom Events
179160

180-
You can build your own custom events, but be careful to follow this structure. It is important that you extend the Model\Event class because Analytics checks inheritance towards that class on addEvent.
161+
You can build your own custom events. All you need is to implement and fullfill the `AlexWestergaard\PhpGa4\Facade\Type\Event` facade/interface. If you want ease of life features, then you can extend your event from `AlexWestergaard\PhpGa4\Helper\AbstractEvent` and overwrite as you see fit.
181162

182163
```php
183-
use AlexWestergaard\PhpGa4\Model;
184164

185-
class ExampleEvent extends Model\Event
165+
// AbstractEvent implements AlexWestergaard\PhpGa4\Facade\Type\TypeEvent
166+
class ExampleEvent extends AlexWestergaard\PhpGa4\Helper\AbstractEvent
186167
{
187-
protected $my_variable;
188-
protected $my_required_variable;
168+
// variables should be nullable as unset() will set variable as null
169+
protected null|mixed $my_variable;
170+
protected null|mixed $my_required_variable;
171+
172+
// Arrays should always be instanciated empty
173+
protected array $my_array = [];
189174

190175
public function getName(): string
191176
{
@@ -196,6 +181,7 @@ class ExampleEvent extends Model\Event
196181
{
197182
return [
198183
'my_variable',
184+
'my_array',
199185
];
200186
}
201187

composer.json

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,18 @@
3535
"AlexWestergaard\\PhpGa4\\": "src/"
3636
}
3737
},
38+
"autoload-dev": {
39+
"psr-4": {
40+
"AlexWestergaard\\PhpGa4Test\\": "test/"
41+
}
42+
},
3843
"require": {
39-
"php": "^8.0",
44+
"php": "^8.0.0|8.1.0|8.2.0",
4045
"guzzlehttp/guzzle": "^7.0"
4146
},
4247
"require-dev": {
4348
"phpunit/phpunit": "^9.0|^10.0"
44-
}
45-
}
49+
},
50+
"minimum-stability": "stable",
51+
"prefer-stable": true
52+
}

phpunit.xml

Lines changed: 26 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,29 @@
1-
<?xml version="1.0" encoding="UTF-8" ?>
2-
<phpunit
3-
bootstrap="vendor/autoload.php"
4-
colors="true"
5-
stopOnFailure="false">
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" bootstrap="vendor/autoload.php"
3+
colors="true" stopOnFailure="false"
4+
beStrictAboutTestsThatDoNotTestAnything="true"
5+
beStrictAboutTodoAnnotatedTests="true"
6+
testdox="true"
7+
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/10.0/phpunit.xsd"
8+
cacheDirectory=".phpunit.cache">
69
<testsuites>
7-
<testsuite name="Analytics">
8-
<directory>test</directory>
10+
<testsuite name="Units">
11+
<directory>test/Unit</directory>
912
</testsuite>
1013
</testsuites>
11-
<coverage>
12-
<include>
13-
<directory suffix=".php">./src</directory>
14-
</include>
15-
</coverage>
16-
</phpunit>
14+
<coverage includeUncoveredFiles="true" pathCoverage="true">
15+
<include>
16+
<directory suffix=".php">./src</directory>
17+
</include>
18+
<exclude>
19+
<directory suffix=".php">./src/Facade</directory>
20+
</exclude>
21+
<report>
22+
<text outputFile="./.phpunit/coverage.txt" showUncoveredFiles="true"
23+
showOnlySummary="false" />
24+
</report>
25+
</coverage>
26+
<php>
27+
<env name="XDEBUG" value="coverage" />
28+
</php>
29+
</phpunit>

0 commit comments

Comments
 (0)