Skip to content
/ php-ga4 Public

PHP Wrapper for Google Analytics 4 with Server Side Tracking

License

Notifications You must be signed in to change notification settings

aawnu/php-ga4

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Version Version Code Coverage Badge PHPVersion Size Contributors Issues Pulls LastCommit

composer require alexwestergaard/php-ga4

Europe - GDPR Notice

The European Union have notified that Google Analytics does not comply with GDPR by default. This is because the frontend Client sends visitor details like their IP Address and device information with events. This can be avoided with a middle-man server inside the European Region.

Getting started

Setup requires a Measurement ID and API Secret. Go to Administrator (Bottom left) -> Account -> Data Streams -> {Your Stream}. Here you should find Measurement ID at top and "Api Secrets for Measurement Protocol" a little down the page, where you can create yourself an API secret.

Go to Administrator (bottom left) and then select your Account -> Data Streams -> your stream.
Here you will find Measurement-ID at top from and further down Api Secrets for Measurement Protocol, in there you can create yourself an API Secret.

Once you have obtained the credentials, you can initialise the Analytics like this:

use AlexWestergaard\PhpGa4\Analytics;

$analytics = Analytics::new(
    measurement_id: 'G-XXXXXXXX',
    api_secret: 'xYzzX_xYzzXzxyZxX',
    debug: true|false #Default: False
);

Data flow

Server Side Tagging is not supposed to replace the frontend Client and session initiation should happen through the gtag.js Client. The default flow is supposed to happen as follows:

  1. Obtain proper GDPR Consent
  2. Client/GTAG.js initiates session with Google Analytics
  3. Google Analytics sends _ga and _gid cookies back to Client/GTAG.js
  4. Server uses _ga (or _gid) to send/populate events
    • Eg. GenerateLead, Purchase, Refund and other backend handled events.

Note: It is entirely possible to push events to backend without acquiring the session cookies from Google Analytics; you will, however, lose information bundled inside the GTAG.js request if you do not figure out how to push that via backend too. You can replace the _ga and _gid sessions with your own uniquely generated id.

All requests should follow this structure and contain at least 1 event for Google Analytics to accept it.

Analytics [
    Events [
        Event {
            Parameters
            ? Items [
                Item Parameters
            ]
        }
    ]
    User Properties [
        Properties {
            Key: Value
        }
    ]
    ? Consent {
      Key: Value
    }
    ? User Data {
      Key: Value
    }
]

Events

This is a list of prebuilt events as shown in the documentation. All events have the following parameters to locate trigger location of each event.

// Manual setting of each event
$event->setLanguage(string $var);
$event->setPageLocation(string $var);
$event->setPageReferrer(string $var);
$event->setPageTitle(string $var);
$event->setScreenResolution(string $var);
// Fillable for multiple events
$eventPage = AlexWestergaard\PhpGa4\Helper\EventParamsHelper();
$event->setEventPage($eventPage);

Default

badge badge badge badge badge badge badge badge badge badge badge badge

E-commerce

badge badge badge badge badge badge badge badge badge badge

Engagement / Gaming

badge badge badge badge badge badge badge badge

Frontend & Backend Communication

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.

Logging / Queue

use AlexWestergaard\PhpGa4\Exception;
use AlexWestergaard\PhpGa4\Analytics;
use AlexWestergaard\PhpGa4\Event;
use AlexWestergaard\PhpGa4\Item;

// require vendor/autoload.php

$visitors = getVisitorsAndEvents(); // pseudo function, make your own logic here

foreach ($visitors as $collection) {
    // Group of events, perhaps need logic to change from json or array to event objects
    // Maybe its formatted well for the > ConvertHelper::parseEvents([...]) < helper
    $groups = $collection['events'];

    // If gtag.js, this can be the _ga or _gid cookie
    // This can be any kind of session identifier
    // Usually derives from $_COOKIE['_ga'] or $_COOKIE['_gid'] set by GTAG.js
    $visitor = $collection['session_id'];

    // load logged in user/visitor
    // This can be any kind of unique identifier, readable is easier for you
    // Just be wary not to use GDPR sensitive information
    $user = $collection['user_id'];

    // Render events grouped on time (max offset is 3 days from NOW)
    foreach ($groups as $time => $data) {
        try {
                $analytics = Analytics::new($measurementId, $apiSecret)
                    ->setClientId($visitor)
                    ->setTimestampMicros($time);

                if ($user !== null) {
                    $analytics->setUserId($user);
                }

                $analytics->addUserParameter(...$data['userParameters']); // pseudo logic for adding user parameters
                $analytics->addEvent(...$data['events']); // pseudo logic for adding events

                $analytics->post(); // send events to Google Analytics
        } catch (Exception\Ga4Exception $exception) {
            // Handle exception
            // Exceptions might be stacked, check: $exception->getPrevious();
        }
    }
}

Frontend => Backend

Frontend

// array< array< eventName, array<eventParams> > >
axios.post("/your-api-endpoint/ga4-event-receiver", [
  // Note each event is its own object inside an array as
  // this allows to pass the same event type multiple times
  {
    addToCart: {
      currency: "EUR",
      value: 13.37,
      items: [
        {
          item_id: 1,
          item_name: "Cup",
          price: 13.37,
          quantity: 1,
        },
      ],
    },
  },
]);

Backend

use AlexWestergaard\PhpGa4\Helper\ConvertHelper;
use AlexWestergaard\PhpGa4\Exception;
use AlexWestergaard\PhpGa4\Analytics;
use AlexWestergaard\PhpGa4\Event;

// require vendor/autoload.php

try {
    $events = ConvertHelper::parseEvents($_POST);

    Analytics::new($measurementId, $apiSecret)
        ->addEvent(...$events)
        ->post();
} catch (Exception\Ga4Exception $exception) {
    // Handle exception
    // Exceptions might be stacked, check: $exception->getPrevious();
}

Custom Events

You can build your own custom events. All you need is to implement and fullfill the AlexWestergaard\PhpGa4\Facade\Type\EventType facade/interface. If you want ease of life features, then you can extend your event from AlexWestergaard\PhpGa4\Helper\EventHelper and overwrite as you see fit.

// EventHelper implements AlexWestergaard\PhpGa4\Facade\Type\EventType
class ExampleEvent extends AlexWestergaard\PhpGa4\Helper\EventHelper
{
    // variables should be nullable as unset() will set variable as null
    protected null|mixed $my_variable;
    protected null|mixed $my_required_variable;

    // Arrays should always be instanciated empty
    protected array $my_array = [];

    public function getName(): string
    {
        return 'example_event';
    }

    public function getParams(): array
    {
        return [
            'my_variable',
            'my_array',
        ];
    }

    public function getRequiredParams(): array
    {
        return [
            'my_required_variable',
        ];
    }

    public function setMyVariable(string $value)
    {
        $this->my_variable = $value;
        return $this; // Allows chained events
    }

    public function setMyRequiredVariable(string $value)
    {
        $this->my_required_variable = $value;
        return $this; // Allows chained events
    }
}

Debug

Measurement protocol for GA4 has debug functionality that can be enabled with the debug parameter in the Analytics constructor.

$analytics = Analytics::new(
    measurement_id: 'G-XXXXXXXX',
    api_secret: 'xYzzX_xYzzXzxyZxX',
    debug: true // default: false
);

When Debug is enabled then events are sent to https://www.google-analytics.com/debug/mp/collect where issues will be caught with GA4Exception (Be aware of $exception->getPrevious() stacks); such response will look as follows:

{
  "validationMessages": [
    {
      "fieldPath": "events",
      "description": "Event at index: [0] has invalid name [_badEventName]. Names must start with an alphabetic character.",
      "validationCode": "NAME_INVALID"
    }
  ]
}

Notice: This library already validates that events are properly formatted when added to analytics ($analytics->addEvent(...)).

Two important points:

  • Events sent to the Validation Server will not show up in reports.
  • There is no way for events sent through measurement protocol (Server Side) to show up in the debugView in Google Analytics Admin.

Additional information

  • Geographic information is only available via automatic collection from gtag, Google Tag Manager, or Google Analytics for Firebase.
  • The page_view event works, however it's not documented in the official documentation, so do not rely on it.

Documentation