first commit

This commit is contained in:
2024-11-05 12:22:50 +01:00
commit e5682a3912
19641 changed files with 2948548 additions and 0 deletions

View File

@@ -0,0 +1,10 @@
# Apache 2.2
<IfModule !mod_authz_core.c>
Order deny,allow
Deny from all
</IfModule>
# Apache 2.4
<IfModule mod_authz_core.c>
Require all denied
</IfModule>

View File

@@ -0,0 +1,7 @@
<?php
// autoload.php @generated by Composer
require_once __DIR__ . '/composer/autoload_real.php';
return ComposerAutoloaderInit2e04d69c8d2e7acdacaf965c4df1ec3c::getLoader();

View File

@@ -0,0 +1 @@
service_name: travis-ci

View File

@@ -0,0 +1,14 @@
# Drupal editor configuration normalization
# @see http://editorconfig.org/
# This is the top-most .editorconfig file; do not search in parent directories.
root = true
# All files.
[*]
end_of_line = LF
indent_style = space
indent_size = 4
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true

View File

@@ -0,0 +1,5 @@
composer.phar
composer.lock
/vendor/
.idea
.DS_Store

View File

@@ -0,0 +1,14 @@
# .travis.yml
language: php
php:
- 7.4
install:
- curl -s http://getcomposer.org/installer | php
- php composer.phar update --dev --no-interaction
script:
- mkdir -p build/logs
- XDEBUG_MODE=coverage vendor/bin/phpunit -c phpunit.xml.dist
after_success:
- travis_retry php vendor/bin/php-coveralls

View File

@@ -0,0 +1,249 @@
# Google Analytics 4 Measurement Protocol PHP Library
[![Coverage Status](https://coveralls.io/repos/github/br33f/php-GA4-Measurement-Protocol/badge.svg?branch=master)](https://coveralls.io/github/br33f/php-GA4-Measurement-Protocol?branch=master)
[![Latest Stable Version](https://poser.pugx.org/br33f/php-ga4-mp/v/stable.png)](https://packagist.org/packages/br33f/php-ga4-mp)
[![Total Downloads](https://poser.pugx.org/br33f/php-ga4-mp/downloads.png)](https://packagist.org/packages/br33f/php-ga4-mp)
## Overview
This is a PHP Library facilitating the use of Google Analytics 4 (GA4) Measurement Protocol. Measurement Protocol allows developers to send events directly from server-side PHP to Google Analytics.
Full documentation is available here:
https://developers.google.com/analytics/devguides/collection/protocol/ga4
## Requirements
- PHP >= 7.1
- ext-json
- guzzlehttp/guzzle: ^6.5.5 || ^7.0.0
dev:
- phpunit/phpunit: "^9.5"
- fakerphp/faker: "^1.14"
## Installation
The recommended way to install this library is via [Composer](https://getcomposer.org/ "Composer") (packagist package: [br33f/php-ga4-mp](https://packagist.org/packages/br33f/php-ga4-mp "br33f/php-ga4-mp")).
Install by composer command:
```
composer require br33f/php-ga4-mp
```
or `package.json`
```
{
"require": {
"br33f/php-ga4-mp": "^0.1.0"
}
}
```
## Usage
### Send View Item Event
```php
use Br33f\Ga4\MeasurementProtocol\Service;
use Br33f\Ga4\MeasurementProtocol\Dto\Request\BaseRequest;
use Br33f\Ga4\MeasurementProtocol\Dto\Event\ViewItemEvent;
use Br33f\Ga4\MeasurementProtocol\Dto\Parameter\ItemParameter;
// Create service instance
$ga4Service = new Service('MEASUREMENT_PROTOCOL_API_SECRET');
$ga4Service->setMeasurementId('MEASUREMENT_ID');
// Create base request
$baseRequest = new BaseRequest();
$baseRequest->setClientId('CLIENT_ID');
// Create Event Data
$viewItemEventData = new ViewItemEvent();
$viewItemEventData
->setValue(51.10)
->setCurrency('EUR');
// Create Item
$viewedItem = new ItemParameter();
$viewedItem
->setItemId('ITEM_ID')
->setItemName('ITEM_NAME')
->setPrice(25.55)
->setQuantity(2);
// Add this item to viewItemEventData
$viewItemEventData->addItem($viewedItem);
// Add event to base request (you can add up to 25 events to single request)
$baseRequest->addEvent($viewItemEventData);
// We have all the data we need. Just send the request.
$ga4Service->send($baseRequest);
```
### Send Purchase Event
```php
use Br33f\Ga4\MeasurementProtocol\Service;
use Br33f\Ga4\MeasurementProtocol\Dto\Request\BaseRequest;
use Br33f\Ga4\MeasurementProtocol\Dto\Event\PurchaseEvent;
use Br33f\Ga4\MeasurementProtocol\Dto\Parameter\ItemParameter;
// Create service instance
$ga4Service = new Service('MEASUREMENT_PROTOCOL_API_SECRET');
$ga4Service->setMeasurementId('MEASUREMENT_ID');
// Create base request
$baseRequest = new BaseRequest();
$baseRequest->setClientId('CLIENT_ID');
// Create Event Data
$purchaseEventData = new PurchaseEvent();
$purchaseEventData
->setValue(250.00)
->setCurrency('USD');
// Create Item
$purchasedItem1 = new ItemParameter();
$purchasedItem1
->setItemId('FIRST_ITEM_ID')
->setItemName('FIRST_ITEM_NAME')
->setPrice(100.00)
->setQuantity(2);
// Add this item to purchaseEventData
$purchaseEventData->addItem($purchasedItem1);
// You can also fill item data via constructor
$purchaseEventData->addItem(new ItemParameter([
'item_id' => 'SECOND_ITEM_ID',
'item_name' => 'SECOND_ITEM_NAME',
'price' => 50.00,
'quantity' => 1
]));
// Add event to base request (you can add up to 25 events to single request)
$baseRequest->addEvent($purchaseEventData);
// We have all the data we need. Just send the request.
$ga4Service->send($baseRequest);
```
At the moment, the library contains the defined structures of the following events:
| Event name | Structure | Documentation |
| ---------- | --------- | --------------|
| add_payment_info | AddPaymentInfoEvent | [see documentation](https://developers.google.com/analytics/devguides/collection/protocol/ga4/reference/events#add_payment_info)
| add_shipping_info | AddShippingInfoEvent | [see documentation](https://developers.google.com/analytics/devguides/collection/protocol/ga4/reference/events#add_shipping_info)
| add_to_cart | AddToCartEvent | [see documentation](https://developers.google.com/analytics/devguides/collection/protocol/ga4/reference/events#add_to_cart)
| begin_checkout | BeginCheckoutEvent | [see documentation](https://developers.google.com/analytics/devguides/collection/protocol/ga4/reference/events#begin_checkout)
| login | LoginEvent | [see documentation](https://developers.google.com/analytics/devguides/collection/protocol/ga4/reference/events#login)
| purchase | PurchaseEvent | [see documentation](https://developers.google.com/analytics/devguides/collection/protocol/ga4/reference/events#purchase)
| refund | RefundEvent | [see documentation](https://developers.google.com/analytics/devguides/collection/protocol/ga4/reference/events#refund)
| remove_from_cart | RemoveFromCartEvent | [see documentation](https://developers.google.com/analytics/devguides/collection/protocol/ga4/reference/events#remove_from_cart)
| search | SearchEvent | [see documentation](https://developers.google.com/analytics/devguides/collection/protocol/ga4/reference/events#search)
| select_item | SelectItemEvent | [see documentation](https://developers.google.com/analytics/devguides/collection/protocol/ga4/reference/events#select_item)
| sign_up | SignUpEvent | [see documentation](https://developers.google.com/analytics/devguides/collection/protocol/ga4/reference/events#sign_up)
| view_cart | ViewCartEvent | [see documentation](https://developers.google.com/analytics/devguides/collection/protocol/ga4/reference/events#view_cart)
| view_item | ViewItemEvent | [see documentation](https://developers.google.com/analytics/devguides/collection/protocol/ga4/reference/events#view_item)
| view_search_results | ViewSearchResultsEvent | [see documentation](https://developers.google.com/analytics/devguides/collection/protocol/ga4/reference/events#view_search_results)
These events are sent analogously to the examples presented above.
### Other events
In order to send any event one can use `BaseEvent` structure and add any data. Please note that specific event structure should be used instead if already defined, since BaseEvent does not force any structure or provide data validation.
```php
use Br33f\Ga4\MeasurementProtocol\Service;
use Br33f\Ga4\MeasurementProtocol\Dto\Request\BaseRequest;
use Br33f\Ga4\MeasurementProtocol\Dto\Event\BaseEvent;
// Create Service and request same as above
// ...
// Create Base Event Data (for example: 'share' event - https://developers.google.com/analytics/devguides/collection/protocol/ga4/reference/events#share)
$eventName = 'share';
$anyEventData = new BaseEvent($eventName);
$anyEventData
->setMethod('Twitter')
->setContentType('Post')
->setItemId('example_item_id')
->setAnyParamYouWish('test'); // means 'any_param_you_wish' is set
// Add event to base request (you can add up to 25 events to single request) and send, same as above
// ...
```
### Firebase Support
It is possible to use this library to send Firebase events. To do so, just initialize Service and BaseRequest as in following example:
```php
use Br33f\Ga4\MeasurementProtocol\Service;
use Br33f\Ga4\MeasurementProtocol\Dto\Request\BaseRequest;
// Create service instance
$ga4Service = new Service('MEASUREMENT_PROTOCOL_API_SECRET');
$ga4Service->setFirebaseId('FIREBASE_APP_ID'); // instead of setMeasurementId(...)
// Create base request
$baseRequest = new BaseRequest();
$baseRequest->setAppInstanceId('APP_INSTANCE_ID'); // instead of setClientId(...)
```
## Debug event data and requests
Debuging event data is possible by sending them to debug endpoint (Measurement Protocol Validation Server), since default endpoint for Google Analytics 4 Measurement Protocol does not return any HTTP error codes or messages. In order to validate event one should use `sendDebug($request)` method instead of `send($request)`.
Method `sendDebug($request)` returns `DebugResponse` object, which is hydrated with response data such as: `status_code` and `validation_messages`.
### Example:
```php
use Br33f\Ga4\MeasurementProtocol\Service;
use Br33f\Ga4\MeasurementProtocol\Dto\Request\BaseRequest;
use Br33f\Ga4\MeasurementProtocol\Dto\Event\AddToCartEvent;
use Br33f\Ga4\MeasurementProtocol\Dto\Parameter\ItemParameter;
// Create service instance
$ga4Service = new Service('MEASUREMENT_PROTOCOL_API_SECRET');
$ga4Service->setMeasurementId('MEASUREMENT_ID');
// Create base request
$baseRequest = new BaseRequest();
$baseRequest->setClientId('CLIENT_ID');
// Create Invalid Event Data
$addToCartEventData = new AddToCartEvent();
$addToCartEventData
->setValue(99.99)
->setCurrency('SOME_INVALID_CURRENCY_CODE'); // invalid currency code
// addItem
$addToCartEventData->addItem(new ItemParameter([
'item_id' => 'ITEM_ID',
'item_name' => 'ITEM_NAME',
'price' => 99.99,
'quantity' => 1
]));
// Add event to base request (you can add up to 25 events to single request)
$baseRequest->addEvent($addToCartEventData);
// Instead of sending data to production Measurement Protocol endpoint
// $ga4Service->send($baseRequest);
// Send data to validation endpoint, which responds with status cude and validation messages.
$debugResponse = $ga4Service->sendDebug($baseRequest);
// Now debug response contains status code, and validation messages if request is invalid
var_dump($debugResponse->getStatusCode());
var_dump($debugResponse->getValidationMessages());
```
## Unit Testing
Unit Testing for this module is done using PHPUnit 9.
Running unit tests:
```
composer install
php vendor/bin/phpunit
```
## License
This library is released under the MIT License.

View File

@@ -0,0 +1,32 @@
{
"name": "br33f/php-ga4-mp",
"description": "PHP GoogleAnalytics4 Measurement Protocol Library",
"minimum-stability": "stable",
"license": "MIT",
"authors": [
{
"name": "Damian Zamojski",
"email": "damian.zamojski1@gmail.com"
}
],
"require": {
"php": ">=7.1",
"guzzlehttp/guzzle": "^6.5.5 || ^7.0.0",
"ext-json": "*"
},
"autoload": {
"psr-4": {
"Br33f\\Ga4\\MeasurementProtocol\\": "src/"
}
},
"require-dev": {
"phpunit/phpunit": "^9.5",
"fakerphp/faker": "^1.14",
"php-coveralls/php-coveralls": "^2.4"
},
"autoload-dev": {
"psr-4": {
"Tests\\": "tests/"
}
}
}

View File

@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.5/phpunit.xsd"
bootstrap="vendor/autoload.php"
cacheResultFile=".phpunit.cache/test-results"
executionOrder="depends,defects"
forceCoversAnnotation="false"
beStrictAboutCoversAnnotation="true"
beStrictAboutOutputDuringTests="true"
beStrictAboutTodoAnnotatedTests="true"
failOnRisky="true"
failOnWarning="false"
verbose="true">
<testsuites>
<testsuite name="default">
<directory>tests</directory>
</testsuite>
</testsuites>
<coverage cacheDirectory=".phpunit.cache/code-coverage"
processUncoveredFiles="true">
<include>
<directory suffix=".php">src</directory>
</include>
<report>
<clover outputFile="build/logs/clover.xml"></clover>
</report>
</coverage>
</phpunit>

View File

@@ -0,0 +1,78 @@
<?php
/**
* User: Damian Zamojski (br33f)
* Date: 22.06.2021
* Time: 13:51
*/
namespace Br33f\Ga4\MeasurementProtocol\Dto\Common;
use Br33f\Ga4\MeasurementProtocol\Dto\Event\AbstractEvent;
use Br33f\Ga4\MeasurementProtocol\Dto\ExportableInterface;
use Br33f\Ga4\MeasurementProtocol\Dto\ValidateInterface;
use Br33f\Ga4\MeasurementProtocol\Enum\ErrorCode;
use Br33f\Ga4\MeasurementProtocol\Exception\ValidationException;
use InvalidArgumentException;
class EventCollection implements ExportableInterface, ValidateInterface
{
/**
* @var AbstractEvent[]
*/
protected $eventList = [];
/**
* @param AbstractEvent $event
*/
public function addEvent(AbstractEvent $event)
{
if (count($this->eventList) >= 25) {
throw new InvalidArgumentException('Event list must not exceed 25 items', ErrorCode::MAX_EVENT_COUNT_EXCEED);
}
$this->eventList[] = $event;
}
/**
* @return array
*/
public function export(): array
{
return array_map(function ($userProperty) {
return $userProperty->export();
}, $this->getEventList());
}
/**
* @return array
*/
public function getEventList(): array
{
return $this->eventList;
}
/**
* @param array $eventList
*/
public function setEventList(array $eventList)
{
if (count($eventList) > 25) {
throw new InvalidArgumentException('Event list must not exceed 25 items', ErrorCode::MAX_EVENT_COUNT_EXCEED);
}
$this->eventList = $eventList;
}
/**
* @throws ValidationException
*/
public function validate()
{
if (count($this->getEventList()) === 0) {
throw new ValidationException('Event list must not be empty', ErrorCode::VALIDATION_EVENTS_MUST_NOT_BE_EMPTY, 'events');
}
return true;
}
}

View File

@@ -0,0 +1,62 @@
<?php
/**
* User: Damian Zamojski (br33f)
* Date: 22.06.2021
* Time: 12:23
*/
namespace Br33f\Ga4\MeasurementProtocol\Dto\Common;
use Br33f\Ga4\MeasurementProtocol\Dto\ExportableInterface;
class UserProperties implements ExportableInterface
{
/**
* @var UserProperty[]
*/
protected $userPropertiesList;
/**
* UserProperties constructor.
* @param UserProperty[] $userPropertiesList
*/
public function __construct(array $userPropertiesList = null)
{
$this->userPropertiesList = $userPropertiesList ?? [];
}
/**
* @param UserProperty $userProperty
*/
public function addUserProperty(UserProperty $userProperty)
{
$this->userPropertiesList[] = $userProperty;
}
/**
* @return array
*/
public function export(): array
{
return array_reduce($this->getUserPropertiesList(), function ($last, UserProperty $userProperty) {
return array_merge($last, $userProperty->export());
}, []);
}
/**
* @return UserProperty[]
*/
public function getUserPropertiesList(): array
{
return $this->userPropertiesList;
}
/**
* @param UserProperty[] $userPropertiesList
*/
public function setUserPropertiesList(array $userPropertiesList)
{
$this->userPropertiesList = $userPropertiesList;
}
}

View File

@@ -0,0 +1,78 @@
<?php
/**
* User: Damian Zamojski (br33f)
* Date: 22.06.2021
* Time: 12:23
*/
namespace Br33f\Ga4\MeasurementProtocol\Dto\Common;
use Br33f\Ga4\MeasurementProtocol\Dto\ExportableInterface;
class UserProperty implements ExportableInterface
{
/**
* User property name
* @var string
*/
protected $name;
/**
* User property value
* @var mixed
*/
protected $value;
/**
* UserProperty constructor.
* @param string|null $name
* @param mixed $value
*/
public function __construct(?string $name = null, $value = null)
{
$this->name = $name;
$this->value = $value;
}
public function export(): array
{
return [
$this->getName() => [
'value' => $this->getValue()
]
];
}
/**
* @return string|null
*/
public function getName(): ?string
{
return $this->name;
}
/**
* @param string|null $name
*/
public function setName(?string $name)
{
$this->name = $name;
}
/**
* @return mixed
*/
public function getValue()
{
return $this->value;
}
/**
* @param mixed $value
*/
public function setValue($value)
{
$this->value = $value;
}
}

View File

@@ -0,0 +1,102 @@
<?php
/**
* User: Damian Zamojski (br33f)
* Date: 22.06.2021
* Time: 12:23
*/
namespace Br33f\Ga4\MeasurementProtocol\Dto\Common;
use Br33f\Ga4\MeasurementProtocol\Dto\HydratableInterface;
use Br33f\Ga4\MeasurementProtocol\Enum\ValidationCode;
use Psr\Http7\Message\ResponseInterface;
class ValidationMessage implements HydratableInterface
{
/**
* The path to the field that was invalid
* @var string|null
*/
protected $fieldPath;
/**
* A description of the error
* @var string|null
*/
protected $description;
/**
* A ValidationCode that corresponds to the error
* @var string|null
* @see ValidationCode
*/
protected $validationCode;
/**
* ValidationMessage constructor.
* @param array|null $blueprint
*/
public function __construct(?array $blueprint = null)
{
if ($blueprint !== null) {
$this->hydrate($blueprint);
}
}
/**
* @param array|ResponseInterface $blueprint
*/
public function hydrate($blueprint)
{
$this->setFieldPath(array_key_exists('fieldPath', $blueprint) ? $blueprint['fieldPath'] : null);
$this->setDescription(array_key_exists('description', $blueprint) ? $blueprint['description'] : null);
$this->setValidationCode(array_key_exists('validationCode', $blueprint) ? $blueprint['validationCode'] : null);
}
/**
* @return string|null
*/
public function getFieldPath(): ?string
{
return $this->fieldPath;
}
/**
* @param string|null $fieldPath
*/
public function setFieldPath(?string $fieldPath)
{
$this->fieldPath = $fieldPath;
}
/**
* @return string|null
*/
public function getDescription(): ?string
{
return $this->description;
}
/**
* @param string|null $description
*/
public function setDescription(?string $description)
{
$this->description = $description;
}
/**
* @return string|null
*/
public function getValidationCode(): ?string
{
return $this->validationCode;
}
/**
* @param string|null $validationCode
*/
public function setValidationCode(?string $validationCode)
{
$this->validationCode = $validationCode;
}
}

View File

@@ -0,0 +1,195 @@
<?php
/**
* User: Damian Zamojski (br33f)
* Date: 22.06.2021
* Time: 14:16
*/
namespace Br33f\Ga4\MeasurementProtocol\Dto\Event;
use BadMethodCallException;
use Br33f\Ga4\MeasurementProtocol\Dto\ExportableInterface;
use Br33f\Ga4\MeasurementProtocol\Dto\Parameter\AbstractParameter;
use Br33f\Ga4\MeasurementProtocol\Dto\Parameter\BaseParameter;
use Br33f\Ga4\MeasurementProtocol\Dto\ValidateInterface;
use InvalidArgumentException;
abstract class AbstractEvent implements ExportableInterface, ValidateInterface
{
/**
* @var string
*/
protected $name;
/**
* @var AbstractParameter[]
*/
protected $paramList;
/**
* AbstractEvent constructor.
* @param string|null $name
* @param AbstractParameter[] $paramList
*/
public function __construct(?string $name = null, array $paramList = [])
{
$this->name = $name;
$this->paramList = $paramList ?? [];
}
/**
* @param string $methodName
* @param array $methodArguments
* @return mixed|null
*/
public function __call(string $methodName, array $methodArguments)
{
$methodPrefix = substr($methodName, 0, 3);
$paramName = $this->convertCamelCaseToSnakeCase(substr($methodName, 3));
if ($methodPrefix === "set") {
if (!isset($methodArguments[0])) {
throw new InvalidArgumentException('First argument is expected to be paramter value, none specified.');
}
return $this->setParamValue($paramName, $methodArguments[0]);
}
if ($methodPrefix === "get") {
return $this->getParamValue($paramName);
}
throw new BadMethodCallException('Method ' . $methodName . ' is not defined.');
}
/**
* @param string $input
* @return string
*/
protected function convertCamelCaseToSnakeCase(string $input)
{
preg_match_all('!([A-Z][A-Z0-9]*(?=$|[A-Z][a-z0-9])|[A-Za-z][a-z0-9]+)!', $input, $matches);
$ret = $matches[0];
foreach ($ret as &$match) {
$match = $match == strtoupper($match) ? strtolower($match) : lcfirst($match);
}
return implode('_', $ret);
}
/**
* @param string $paramName
* @param mixed $paramValue
* @return AbstractEvent
*/
public function setParamValue(string $paramName, $paramValue)
{
$this->findOrCreateParameter($paramName)->setValue($paramValue);
return $this;
}
/**
* @param string $paramName
* @return AbstractParameter
*/
public function findOrCreateParameter(string $paramName)
{
$foundParameter = $this->findParameter($paramName);
if ($foundParameter === null) {
$foundParameter = new BaseParameter();
$this->addParam($paramName, $foundParameter);
}
return $foundParameter;
}
/**
* @param string $paramName
* @return AbstractParameter|null
*/
public function findParameter(string $paramName)
{
if (array_key_exists($paramName, $this->getParamList())) {
return $this->getParamList()[$paramName];
} else {
return null;
}
}
/**
* @return AbstractParameter[]
*/
public function getParamList(): array
{
return $this->paramList;
}
/**
* @param AbstractParameter[] $paramList
*/
public function setParamList(array $paramList)
{
$this->paramList = $paramList;
}
/**
* @param string $parameterName
* @param AbstractParameter $parameter
*/
public function addParam(string $parameterName, AbstractParameter $parameter)
{
$this->paramList[$parameterName] = $parameter;
}
/**
* @param string $paramName
* @return mixed|null
*/
public function getParamValue(string $paramName)
{
return $this->findOrCreateParameter($paramName)->getValue();
}
/**
* @param string $paramName
*/
public function deleteParameter(string $paramName)
{
if (array_key_exists($paramName, $this->getParamList())) {
unset($this->paramList[$paramName]);
}
}
public function export(): array
{
$preparedParams = [];
foreach ($this->getParamList() as $parameterName => $parameter) {
$parameterExportedValue = $parameter->export();
if (!is_null($parameterExportedValue)) {
$preparedParams[$parameterName] = $parameterExportedValue;
}
}
return [
'name' => $this->getName(),
// Note that we need to return an \ArrayObject here. As otherwise json_encode will serialize params to `[]`. And
// Google Analytics will error on this, as it expects a map. Whereas new \ArrayObject will export correctly to `{}`.
// See https://github.com/br33f/php-GA4-Measurement-Protocol/issues/10.
'params' => new \ArrayObject($preparedParams),
];
}
/**
* @return string|null
*/
public function getName(): ?string
{
return $this->name;
}
/**
* @param string|null $name
*/
protected function setName(?string $name)
{
$this->name = $name;
}
}

View File

@@ -0,0 +1,51 @@
<?php
/**
* User: Damian Zamojski (br33f)
* Date: 25.06.2021
* Time: 13:33
*/
namespace Br33f\Ga4\MeasurementProtocol\Dto\Event;
use Br33f\Ga4\MeasurementProtocol\Dto\Parameter\AbstractParameter;
use Br33f\Ga4\MeasurementProtocol\Enum\ErrorCode;
use Br33f\Ga4\MeasurementProtocol\Exception\ValidationException;
/**
* Class AddPaymentInfoEvent
* @package Br33f\Ga4\MeasurementProtocol\Dto\Event
* @method string getCurrency()
* @method AddPaymentInfoEvent setCurrency(string $currency)
* @method float getValue()
* @method AddPaymentInfoEvent setValue(float $value)
* @method string getCoupon()
* @method AddPaymentInfoEvent setCoupon(string $coupon)
* @method string getPaymentType()
* @method AddPaymentInfoEvent setPaymentType(string $paymentType)
*/
class AddPaymentInfoEvent extends ItemBaseEvent
{
private $eventName = 'add_payment_info';
/**
* AddPaymentInfoEvent constructor.
* @param AbstractParameter[] $paramList
*/
public function __construct(array $paramList = [])
{
parent::__construct($this->eventName, $paramList);
}
public function validate()
{
parent::validate();
if (!empty($this->getValue())) {
if (empty($this->getCurrency())) {
throw new ValidationException('Field "currency" is required if "value" is set', ErrorCode::VALIDATION_FIELD_REQUIRED, 'currency');
}
}
return true;
}
}

View File

@@ -0,0 +1,51 @@
<?php
/**
* User: Damian Zamojski (br33f)
* Date: 25.06.2021
* Time: 13:33
*/
namespace Br33f\Ga4\MeasurementProtocol\Dto\Event;
use Br33f\Ga4\MeasurementProtocol\Dto\Parameter\AbstractParameter;
use Br33f\Ga4\MeasurementProtocol\Enum\ErrorCode;
use Br33f\Ga4\MeasurementProtocol\Exception\ValidationException;
/**
* Class AddShippingInfoEvent
* @package Br33f\Ga4\MeasurementProtocol\Dto\Event
* @method string getCurrency()
* @method AddShippingInfoEvent setCurrency(string $currency)
* @method float getValue()
* @method AddShippingInfoEvent setValue(float $value)
* @method string getCoupon()
* @method AddShippingInfoEvent setCoupon(string $coupon)
* @method string getShippingTier()
* @method AddShippingInfoEvent setShippingTier(string $shippingTier)
*/
class AddShippingInfoEvent extends ItemBaseEvent
{
private $eventName = 'add_shipping_info';
/**
* AddShippingInfoEvent constructor.
* @param AbstractParameter[] $paramList
*/
public function __construct(array $paramList = [])
{
parent::__construct($this->eventName, $paramList);
}
public function validate()
{
parent::validate();
if (!empty($this->getValue())) {
if (empty($this->getCurrency())) {
throw new ValidationException('Field "currency" is required if "value" is set', ErrorCode::VALIDATION_FIELD_REQUIRED, 'currency');
}
}
return true;
}
}

View File

@@ -0,0 +1,47 @@
<?php
/**
* User: Damian Zamojski (br33f)
* Date: 25.06.2021
* Time: 13:33
*/
namespace Br33f\Ga4\MeasurementProtocol\Dto\Event;
use Br33f\Ga4\MeasurementProtocol\Dto\Parameter\AbstractParameter;
use Br33f\Ga4\MeasurementProtocol\Enum\ErrorCode;
use Br33f\Ga4\MeasurementProtocol\Exception\ValidationException;
/**
* Class AddToCartEvent
* @package Br33f\Ga4\MeasurementProtocol\Dto\Event
* @method string getCurrency()
* @method AddToCartEvent setCurrency(string $currency)
* @method float getValue()
* @method AddToCartEvent setValue(float $value)
*/
class AddToCartEvent extends ItemBaseEvent
{
private $eventName = 'add_to_cart';
/**
* AddToCartEvent constructor.
* @param AbstractParameter[] $paramList
*/
public function __construct(array $paramList = [])
{
parent::__construct($this->eventName, $paramList);
}
public function validate()
{
parent::validate();
if (!empty($this->getValue())) {
if (empty($this->getCurrency())) {
throw new ValidationException('Field "currency" is required if "value" is set', ErrorCode::VALIDATION_FIELD_REQUIRED, 'currency');
}
}
return true;
}
}

View File

@@ -0,0 +1,34 @@
<?php
/**
* User: Damian Zamojski (br33f)
* Date: 22.06.2021
* Time: 13:52
*/
namespace Br33f\Ga4\MeasurementProtocol\Dto\Event;
use Br33f\Ga4\MeasurementProtocol\Exception\ValidationException;
class BaseEvent extends AbstractEvent
{
/**
* @param string|null $name
*/
public function setName(?string $name)
{
parent::setName($name);
}
/**
* @return bool
* @throws ValidationException
*/
public function validate()
{
foreach ($this->getParamList() as $parameter) {
$parameter->validate();
}
return true;
}
}

View File

@@ -0,0 +1,49 @@
<?php
/**
* User: Damian Zamojski (br33f)
* Date: 25.06.2021
* Time: 13:33
*/
namespace Br33f\Ga4\MeasurementProtocol\Dto\Event;
use Br33f\Ga4\MeasurementProtocol\Dto\Parameter\AbstractParameter;
use Br33f\Ga4\MeasurementProtocol\Enum\ErrorCode;
use Br33f\Ga4\MeasurementProtocol\Exception\ValidationException;
/**
* Class AddToCartEvent
* @package Br33f\Ga4\MeasurementProtocol\Dto\Event
* @method string getCurrency()
* @method BeginCheckoutEvent setCurrency(string $currency)
* @method float getValue()
* @method BeginCheckoutEvent setValue(float $value)
* @method string getCoupon()
* @method BeginCheckoutEvent setCoupon(string $coupon)
*/
class BeginCheckoutEvent extends ItemBaseEvent
{
private $eventName = 'begin_checkout';
/**
* BeginCheckoutEvent constructor.
* @param AbstractParameter[] $paramList
*/
public function __construct(array $paramList = [])
{
parent::__construct($this->eventName, $paramList);
}
public function validate()
{
parent::validate();
if (!empty($this->getValue())) {
if (empty($this->getCurrency())) {
throw new ValidationException('Field "currency" is required if "value" is set', ErrorCode::VALIDATION_FIELD_REQUIRED, 'currency');
}
}
return true;
}
}

View File

@@ -0,0 +1,67 @@
<?php
/**
* User: Damian Zamojski (br33f)
* Date: 25.06.2021
* Time: 13:25
*/
namespace Br33f\Ga4\MeasurementProtocol\Dto\Event;
use Br33f\Ga4\MeasurementProtocol\Dto\Parameter\ItemCollectionParameter;
use Br33f\Ga4\MeasurementProtocol\Dto\Parameter\ItemParameter;
use Br33f\Ga4\MeasurementProtocol\Exception\ValidationException;
/**
* Class ItemBaseEvent
* @package Br33f\Ga4\MeasurementProtocol\Dto\Event
*/
abstract class ItemBaseEvent extends AbstractEvent
{
/**
* @param ItemParameter $item
* @return self
*/
public function addItem(ItemParameter $item)
{
$this->getItems()->addItem($item);
return $this;
}
/**
* @return ItemCollectionParameter
*/
public function getItems()
{
$items = $this->findParameter('items');
if ($items === null) {
$items = new ItemCollectionParameter();
$this->setItems($items);
}
return $items;
}
/**
* @param ItemCollectionParameter|null $items
* @return self
*/
public function setItems(?ItemCollectionParameter $items)
{
$this->deleteParameter('items');
$this->addParam('items', $items);
return $this;
}
/**
* @return bool
* @throws ValidationException
*/
public function validate()
{
$this->getItems()->validate();
return true;
}
}

View File

@@ -0,0 +1,38 @@
<?php
/**
* User: Damian Zamojski (br33f)
* Date: 25.06.2021
* Time: 13:52
*/
namespace Br33f\Ga4\MeasurementProtocol\Dto\Event;
use Br33f\Ga4\MeasurementProtocol\Dto\Parameter\AbstractParameter;
/**
* Class LoginEvent
* @package Br33f\Ga4\MeasurementProtocol\Dto\Event
* @method string getMethod()
* @method LoginEvent setMethod(string $method)
*/
class LoginEvent extends AbstractEvent
{
private $eventName = 'login';
/**
* LoginEvent constructor.
* @param AbstractParameter[] $paramList
*/
public function __construct(array $paramList = [])
{
parent::__construct($this->eventName, $paramList);
}
/**
* @return bool
*/
public function validate()
{
return true;
}
}

View File

@@ -0,0 +1,61 @@
<?php
/**
* User: Damian Zamojski (br33f)
* Date: 25.06.2021
* Time: 13:33
*/
namespace Br33f\Ga4\MeasurementProtocol\Dto\Event;
use Br33f\Ga4\MeasurementProtocol\Dto\Parameter\AbstractParameter;
use Br33f\Ga4\MeasurementProtocol\Enum\ErrorCode;
use Br33f\Ga4\MeasurementProtocol\Exception\ValidationException;
/**
* Class PurchaseEvent
* @package Br33f\Ga4\MeasurementProtocol\Dto\Event
* @method string getCurrency()
* @method PurchaseEvent setCurrency(string $currency)
* @method string getTransactionId()
* @method PurchaseEvent setTransactionId(string $transactionId)
* @method float getValue()
* @method PurchaseEvent setValue(float $value)
* @method string getAffiliation()
* @method PurchaseEvent setAffiliation(string $affiliation)
* @method string getCoupon()
* @method PurchaseEvent setCoupon(string $coupon)
* @method float getShipping()
* @method PurchaseEvent setShipping(float $shipping)
* @method float getTax()
* @method PurchaseEvent setTax(float $tax)
*/
class PurchaseEvent extends ItemBaseEvent
{
private $eventName = 'purchase';
/**
* PurchaseEvent constructor.
* @param AbstractParameter[] $paramList
*/
public function __construct(array $paramList = [])
{
parent::__construct($this->eventName, $paramList);
}
public function validate()
{
parent::validate();
if (empty($this->getTransactionId())) {
throw new ValidationException('Field "transaction_id" is required if "value" is set', ErrorCode::VALIDATION_FIELD_REQUIRED, 'curtransaction_idrency');
}
if (!empty($this->getValue())) {
if (empty($this->getCurrency())) {
throw new ValidationException('Field "currency" is required if "value" is set', ErrorCode::VALIDATION_FIELD_REQUIRED, 'currency');
}
}
return true;
}
}

View File

@@ -0,0 +1,61 @@
<?php
/**
* User: Damian Zamojski (br33f)
* Date: 25.06.2021
* Time: 13:33
*/
namespace Br33f\Ga4\MeasurementProtocol\Dto\Event;
use Br33f\Ga4\MeasurementProtocol\Dto\Parameter\AbstractParameter;
use Br33f\Ga4\MeasurementProtocol\Enum\ErrorCode;
use Br33f\Ga4\MeasurementProtocol\Exception\ValidationException;
/**
* Class RefundEvent
* @package Br33f\Ga4\MeasurementProtocol\Dto\Event
* @method string getCurrency()
* @method RefundEvent setCurrency(string $currency)
* @method string getTransactionId()
* @method RefundEvent setTransactionId(string $transactionId)
* @method float getValue()
* @method RefundEvent setValue(float $value)
* @method string getAffiliation()
* @method RefundEvent setAffiliation(string $affiliation)
* @method string getCoupon()
* @method RefundEvent setCoupon(string $coupon)
* @method float getShipping()
* @method RefundEvent setShipping(float $shipping)
* @method float getTax()
* @method RefundEvent setTax(float $tax)
*/
class RefundEvent extends ItemBaseEvent
{
private $eventName = 'refund';
/**
* RefundEvent constructor.
* @param AbstractParameter[] $paramList
*/
public function __construct(array $paramList = [])
{
parent::__construct($this->eventName, $paramList);
}
public function validate()
{
parent::validate();
if (empty($this->getTransactionId())) {
throw new ValidationException('Field "transaction_id" is required if "value" is set', ErrorCode::VALIDATION_FIELD_REQUIRED, 'curtransaction_idrency');
}
if (!empty($this->getValue())) {
if (empty($this->getCurrency())) {
throw new ValidationException('Field "currency" is required if "value" is set', ErrorCode::VALIDATION_FIELD_REQUIRED, 'currency');
}
}
return true;
}
}

View File

@@ -0,0 +1,47 @@
<?php
/**
* User: Damian Zamojski (br33f)
* Date: 25.06.2021
* Time: 13:33
*/
namespace Br33f\Ga4\MeasurementProtocol\Dto\Event;
use Br33f\Ga4\MeasurementProtocol\Dto\Parameter\AbstractParameter;
use Br33f\Ga4\MeasurementProtocol\Enum\ErrorCode;
use Br33f\Ga4\MeasurementProtocol\Exception\ValidationException;
/**
* Class RemoveFromCartEvent
* @package Br33f\Ga4\MeasurementProtocol\Dto\Event
* @method string getCurrency()
* @method RemoveFromCartEvent setCurrency(string $currency)
* @method float getValue()
* @method RemoveFromCartEvent setValue(float $value)
*/
class RemoveFromCartEvent extends ItemBaseEvent
{
private $eventName = 'remove_from_cart';
/**
* AddToCartEvent constructor.
* @param AbstractParameter[] $paramList
*/
public function __construct(array $paramList = [])
{
parent::__construct($this->eventName, $paramList);
}
public function validate()
{
parent::validate();
if (!empty($this->getValue())) {
if (empty($this->getCurrency())) {
throw new ValidationException('Field "currency" is required if "value" is set', ErrorCode::VALIDATION_FIELD_REQUIRED, 'currency');
}
}
return true;
}
}

View File

@@ -0,0 +1,45 @@
<?php
/**
* User: Damian Zamojski (br33f)
* Date: 25.06.2021
* Time: 13:52
*/
namespace Br33f\Ga4\MeasurementProtocol\Dto\Event;
use Br33f\Ga4\MeasurementProtocol\Dto\Parameter\AbstractParameter;
use Br33f\Ga4\MeasurementProtocol\Enum\ErrorCode;
use Br33f\Ga4\MeasurementProtocol\Exception\ValidationException;
/**
* Class SearchEvent
* @package Br33f\Ga4\MeasurementProtocol\Dto\Event
* @method string getSearchTerm()
* @method SearchEvent setSearchTerm(string $searchTerm)
*/
class SearchEvent extends AbstractEvent
{
private $eventName = 'search';
/**
* SearchEvent constructor.
* @param AbstractParameter[] $paramList
*/
public function __construct(array $paramList = [])
{
parent::__construct($this->eventName, $paramList);
}
/**
* @return bool
* @throws ValidationException
*/
public function validate()
{
if (empty($this->getSearchTerm())) {
throw new ValidationException('Field "search_term" is required if "value" is set', ErrorCode::VALIDATION_FIELD_REQUIRED, 'search_term');
}
return true;
}
}

View File

@@ -0,0 +1,32 @@
<?php
/**
* User: Damian Zamojski (br33f)
* Date: 25.06.2021
* Time: 13:33
*/
namespace Br33f\Ga4\MeasurementProtocol\Dto\Event;
use Br33f\Ga4\MeasurementProtocol\Dto\Parameter\AbstractParameter;
/**
* Class SelectItemEvent
* @package Br33f\Ga4\MeasurementProtocol\Dto\Event
* @method string getItemListId()
* @method SelectItemEvent setItemListId(string $itemListId)
* @method string getItemListName()
* @method SelectItemEvent setItemListName(string $itemListName)
*/
class SelectItemEvent extends ItemBaseEvent
{
private $eventName = 'select_item';
/**
* SelectItemEvent constructor.
* @param AbstractParameter[] $paramList
*/
public function __construct(array $paramList = [])
{
parent::__construct($this->eventName, $paramList);
}
}

View File

@@ -0,0 +1,38 @@
<?php
/**
* User: Damian Zamojski (br33f)
* Date: 25.06.2021
* Time: 13:52
*/
namespace Br33f\Ga4\MeasurementProtocol\Dto\Event;
use Br33f\Ga4\MeasurementProtocol\Dto\Parameter\AbstractParameter;
/**
* Class SignUpEvent
* @package Br33f\Ga4\MeasurementProtocol\Dto\Event
* @method string getMethod()
* @method SignUpEvent setMethod(string $method)
*/
class SignUpEvent extends AbstractEvent
{
private $eventName = 'sign_up';
/**
* SignUpEvent constructor.
* @param AbstractParameter[] $paramList
*/
public function __construct(array $paramList = [])
{
parent::__construct($this->eventName, $paramList);
}
/**
* @return bool
*/
public function validate()
{
return true;
}
}

View File

@@ -0,0 +1,47 @@
<?php
/**
* User: Damian Zamojski (br33f)
* Date: 25.06.2021
* Time: 13:33
*/
namespace Br33f\Ga4\MeasurementProtocol\Dto\Event;
use Br33f\Ga4\MeasurementProtocol\Dto\Parameter\AbstractParameter;
use Br33f\Ga4\MeasurementProtocol\Enum\ErrorCode;
use Br33f\Ga4\MeasurementProtocol\Exception\ValidationException;
/**
* Class ViewCartEvent
* @package Br33f\Ga4\MeasurementProtocol\Dto\Event
* @method string getCurrency()
* @method ViewCartEvent setCurrency(string $currency)
* @method float getValue()
* @method ViewCartEvent setValue(float $value)
*/
class ViewCartEvent extends ItemBaseEvent
{
private $eventName = 'view_cart';
/**
* ViewCartEvent constructor.
* @param AbstractParameter[] $paramList
*/
public function __construct(array $paramList = [])
{
parent::__construct($this->eventName, $paramList);
}
public function validate()
{
parent::validate();
if (!empty($this->getValue())) {
if (empty($this->getCurrency())) {
throw new ValidationException('Field "currency" is required if "value" is set', ErrorCode::VALIDATION_FIELD_REQUIRED, 'currency');
}
}
return true;
}
}

View File

@@ -0,0 +1,32 @@
<?php
/**
* User: Damian Zamojski (br33f)
* Date: 24.06.2021
* Time: 15:22
*/
namespace Br33f\Ga4\MeasurementProtocol\Dto\Event;
use Br33f\Ga4\MeasurementProtocol\Dto\Parameter\AbstractParameter;
/**
* Class ViewItemEvent
* @package Br33f\Ga4\MeasurementProtocol\Dto\Event
* @method string getCurrency()
* @method ViewItemEvent setCurrency(string $currency)
* @method string getValue()
* @method ViewItemEvent setValue(string $value)
*/
class ViewItemEvent extends ItemBaseEvent
{
private $eventName = 'view_item';
/**
* ViewItemEvent constructor.
* @param AbstractParameter[] $paramList
*/
public function __construct(array $paramList = [])
{
parent::__construct($this->eventName, $paramList);
}
}

View File

@@ -0,0 +1,32 @@
<?php
/**
* User: Damian Zamojski (br33f)
* Date: 25.06.2021
* Time: 13:33
*/
namespace Br33f\Ga4\MeasurementProtocol\Dto\Event;
use Br33f\Ga4\MeasurementProtocol\Dto\Parameter\AbstractParameter;
/**
* Class ViewItemListEvent
* @package Br33f\Ga4\MeasurementProtocol\Dto\Event
* @method string getItemListId()
* @method ViewItemListEvent setItemListId(string $itemListId)
* @method string getItemListName()
* @method ViewItemListEvent setItemListName(string $itemListName)
*/
class ViewItemListEvent extends ItemBaseEvent
{
private $eventName = 'view_item_list';
/**
* ViewItemListEvent constructor.
* @param AbstractParameter[] $paramList
*/
public function __construct(array $paramList = [])
{
parent::__construct($this->eventName, $paramList);
}
}

View File

@@ -0,0 +1,30 @@
<?php
/**
* User: Damian Zamojski (br33f)
* Date: 25.06.2021
* Time: 13:33
*/
namespace Br33f\Ga4\MeasurementProtocol\Dto\Event;
use Br33f\Ga4\MeasurementProtocol\Dto\Parameter\AbstractParameter;
/**
* Class ViewSearchResultsEvent
* @package Br33f\Ga4\MeasurementProtocol\Dto\Event
* @method string getSearchTerm()
* @method ViewSearchResultsEvent setSearchTerm(string $itemListId)
*/
class ViewSearchResultsEvent extends ItemBaseEvent
{
private $eventName = 'view_search_results';
/**
* ViewSearchResultsEvent constructor.
* @param AbstractParameter[] $paramList
*/
public function __construct(array $paramList = [])
{
parent::__construct($this->eventName, $paramList);
}
}

View File

@@ -0,0 +1,17 @@
<?php
/**
* User: Damian Zamojski (br33f)
* Date: 22.06.2021
* Time: 11:10
*/
namespace Br33f\Ga4\MeasurementProtocol\Dto;
interface ExportableInterface
{
/**
* Method returns prepared data
* @return mixed
*/
public function export();
}

View File

@@ -0,0 +1,21 @@
<?php
/**
* User: Damian Zamojski (br33f)
* Date: 22.06.2021
* Time: 13:42
*/
namespace Br33f\Ga4\MeasurementProtocol\Dto;
use Br33f\Ga4\MeasurementProtocol\Exception\HydrationException;
use Psr\Http7\Message\ResponseInterface;
interface HydratableInterface
{
/**
* Method hydrates DTO with data from blueprint
* @param ResponseInterface|array $blueprint
* @throws HydrationException
*/
public function hydrate($blueprint);
}

View File

@@ -0,0 +1,15 @@
<?php
/**
* User: Damian Zamojski (br33f)
* Date: 22.06.2021
* Time: 13:56
*/
namespace Br33f\Ga4\MeasurementProtocol\Dto\Parameter;
use Br33f\Ga4\MeasurementProtocol\Dto\ExportableInterface;
use Br33f\Ga4\MeasurementProtocol\Dto\ValidateInterface;
abstract class AbstractParameter implements ExportableInterface, ValidateInterface
{
}

View File

@@ -0,0 +1,63 @@
<?php
/**
* User: Damian Zamojski (br33f)
* Date: 22.06.2021
* Time: 13:56
*/
namespace Br33f\Ga4\MeasurementProtocol\Dto\Parameter;
use Br33f\Ga4\MeasurementProtocol\Dto\ExportableInterface;
class BaseParameter extends AbstractParameter
{
/**
* @var mixed
*/
protected $value;
/**
* BaseParameter constructor.
* @param mixed|null $value
*/
public function __construct($value = null)
{
$this->value = $value;
}
/**
* @return mixed
*/
public function export()
{
if ($this->getValue() instanceof ExportableInterface) {
return $this->getValue()->export();
} else {
return $this->getValue();
}
}
/**
* @return mixed
*/
public function getValue()
{
return $this->value;
}
/**
* @param mixed $value
*/
public function setValue($value)
{
$this->value = $value;
}
/**
* @return bool
*/
public function validate()
{
return true;
}
}

View File

@@ -0,0 +1,86 @@
<?php
/**
* User: Damian Zamojski (br33f)
* Date: 24.06.2021
* Time: 15:40
*/
namespace Br33f\Ga4\MeasurementProtocol\Dto\Parameter;
use Br33f\Ga4\MeasurementProtocol\Exception\ValidationException;
/**
* Class ItemCollectionParameter
* @package Br33f\Ga4\MeasurementProtocol\Dto\Parameter
*/
class ItemCollectionParameter extends AbstractParameter
{
/**
* @var ItemParameter[]
*/
protected $itemList;
/**
* ItemCollectionParameter constructor.
* @param ItemParameter[]|null $itemList
*/
public function __construct(?array $itemList = [])
{
$this->itemList = $itemList;
}
/**
* @param ItemParameter $item
* @return ItemCollectionParameter
*/
public function addItem(ItemParameter $item)
{
$this->itemList[] = $item;
return $this;
}
/**
* @return bool
* @throws ValidationException
*/
public function validate()
{
foreach ($this->getItemList() as $item) {
$item->validate();
}
return true;
}
/**
* @return ItemParameter[]
*/
public function getItemList(): array
{
return $this->itemList;
}
/**
* @param ItemParameter[] $itemList
* @return ItemCollectionParameter
*/
public function setItemList(array $itemList)
{
$this->itemList = $itemList;
return $this;
}
/**
* @return array
*/
public function export()
{
$exportableObject = [];
foreach ($this->getItemList() as $item) {
$exportableObject[] = $item->export();
}
return $exportableObject;
}
}

View File

@@ -0,0 +1,626 @@
<?php
/**
* User: Damian Zamojski (br33f)
* Date: 24.06.2021
* Time: 15:40
*/
namespace Br33f\Ga4\MeasurementProtocol\Dto\Parameter;
use Br33f\Ga4\MeasurementProtocol\Dto\HydratableInterface;
use Br33f\Ga4\MeasurementProtocol\Enum\ErrorCode;
use Br33f\Ga4\MeasurementProtocol\Exception\ValidationException;
/**
* Class ItemParameter
* @package Br33f\Ga4\MeasurementProtocol\Dto\Parameter
*/
class ItemParameter extends AbstractParameter implements HydratableInterface
{
/**
* The ID of the item
* One of itemId or itemName is required.
* @var string|null
*/
protected $itemId;
/**
* The name of the item
* One of itemId or itemName is required.
* @var string|null
*/
protected $itemName;
/**
* A product affiliation to designate a supplying company or brick and mortar store location
* Not required
* @var string|null
*/
protected $affiliation;
/**
* The coupon name/code associated with the item
* Not required
* @var string|null
*/
protected $coupon;
/**
* The currency in 3-lettery ISO 4217 format
* Not required
* @var string|null
*/
protected $currency;
/**
* The monetary discount value associated with the item
* Not required
* @var float|null
*/
protected $discount;
/**
* The index/position of the item in a list
* Not required
* @var float|null
*/
protected $index;
/**
* The brand of the item
* Not required
* @var string|null
*/
protected $itemBrand;
/**
* The category of the item.
* If used as part of a category hierarchy or taxonomy then this will be the first category.
* Not required
* @var string|null
*/
protected $itemCategory;
/**
* The second category of the item.
* If used as part of a category hierarchy or taxonomy then this will be the first category.
* Not required
* @var string|null
*/
protected $itemCategory2;
/**
* The third category of the item.
* If used as part of a category hierarchy or taxonomy then this will be the first category.
* Not required
* @var string|null
*/
protected $itemCategory3;
/**
* The fourth category of the item.
* If used as part of a category hierarchy or taxonomy then this will be the first category.
* Not required
* @var string|null
*/
protected $itemCategory4;
/**
* The fifth category of the item.
* If used as part of a category hierarchy or taxonomy then this will be the first category.
* Not required
* @var string|null
*/
protected $itemCategory5;
/**
* The ID of the list in which the item was presented to the user
* Not required
* @var string|null
*/
protected $itemListId;
/**
* The name of the list in which the item was presented to the user
* Not required
* @var string|null
*/
protected $itemListName;
/**
* The item variant or unique code or description for additional item details/options.
* Not required
* @var string|null
*/
protected $itemVariant;
/**
* The location associated with the item. It's recommended to use the Google Place ID that corresponds to the associated item. A custom location ID can also be used
* Not required
* @var string|null
*/
protected $locationId;
/**
* The monetary price of the item, in units of the specified currency parameter
* Not required
* @var float|null
*/
protected $price;
/**
* Item quantity
* Not required
* @var float|null
*/
protected $quantity;
/**
* ItemParameter constructor.
* @param array|null $blueprint
*/
public function __construct(?array $blueprint = null)
{
if ($blueprint !== null) {
$this->hydrate($blueprint);
}
}
/**
* @param array $blueprint
*/
public function hydrate($blueprint)
{
if (array_key_exists('item_id', $blueprint)) {
$this->setItemId($blueprint['item_id']);
}
if (array_key_exists('item_name', $blueprint)) {
$this->setItemName($blueprint['item_name']);
}
if (array_key_exists('affiliation', $blueprint)) {
$this->setAffiliation($blueprint['affiliation']);
}
if (array_key_exists('coupon', $blueprint)) {
$this->setCoupon($blueprint['coupon']);
}
if (array_key_exists('currency', $blueprint)) {
$this->setCurrency($blueprint['currency']);
}
if (array_key_exists('discount', $blueprint)) {
$this->setDiscount($blueprint['discount']);
}
if (array_key_exists('index', $blueprint)) {
$this->setIndex($blueprint['index']);
}
if (array_key_exists('item_brand', $blueprint)) {
$this->setItemBrand($blueprint['item_brand']);
}
if (array_key_exists('item_category', $blueprint)) {
$this->setItemCategory($blueprint['item_category']);
}
if (array_key_exists('item_category2', $blueprint)) {
$this->setItemCategory2($blueprint['item_category2']);
}
if (array_key_exists('item_category3', $blueprint)) {
$this->setItemCategory3($blueprint['item_category3']);
}
if (array_key_exists('item_category4', $blueprint)) {
$this->setItemCategory4($blueprint['item_category4']);
}
if (array_key_exists('item_category5', $blueprint)) {
$this->setItemCategory5($blueprint['item_category5']);
}
if (array_key_exists('item_list_id', $blueprint)) {
$this->setItemListId($blueprint['item_list_id']);
}
if (array_key_exists('item_list_name', $blueprint)) {
$this->setItemListName($blueprint['item_list_name']);
}
if (array_key_exists('item_variant', $blueprint)) {
$this->setItemVariant($blueprint['item_variant']);
}
if (array_key_exists('location_id', $blueprint)) {
$this->setLocationId($blueprint['location_id']);
}
if (array_key_exists('price', $blueprint)) {
$this->setPrice($blueprint['price']);
}
if (array_key_exists('quantity', $blueprint)) {
$this->setQuantity($blueprint['quantity']);
}
}
/**
* @return bool
* @throws ValidationException
*/
public function validate()
{
if (empty($this->getItemId()) && empty($this->getItemName())) {
throw new ValidationException("At least one of item_id or item_name is required", ErrorCode::VALIDATION_ITEM_AT_LEAST_ITEM_ID_OR_ITEM_NAME_REQUIRED, 'item_id|item_name');
}
return true;
}
/**
* @return string|null
*/
public function getItemId(): ?string
{
return $this->itemId;
}
/**
* @param string|null $itemId
* @return ItemParameter
*/
public function setItemId(?string $itemId): ItemParameter
{
$this->itemId = $itemId;
return $this;
}
/**
* @return string|null
*/
public function getItemName(): ?string
{
return $this->itemName;
}
/**
* @param string|null $itemName
* @return ItemParameter
*/
public function setItemName(?string $itemName): ItemParameter
{
$this->itemName = $itemName;
return $this;
}
/**
* @return array
*/
public function export()
{
$exportableObject = [
'item_id' => $this->getItemId(),
'item_name' => $this->getItemName(),
'affiliation' => $this->getAffiliation(),
'coupon' => $this->getCoupon(),
'currency' => $this->getCurrency(),
'discount' => $this->getDiscount(),
'index' => $this->getIndex(),
'item_brand' => $this->getItemBrand(),
'item_category' => $this->getItemCategory(),
'item_category2' => $this->getItemCategory2(),
'item_category3' => $this->getItemCategory3(),
'item_category4' => $this->getItemCategory4(),
'item_category5' => $this->getItemCategory5(),
'item_list_id' => $this->getItemListId(),
'item_list_name' => $this->getItemListName(),
'item_variant' => $this->getItemVariant(),
'location_id' => $this->getLocationId(),
'price' => $this->getPrice(),
'quantity' => $this->getQuantity()
];
$preparedExportableObject = [];
foreach ($exportableObject as $exportableItemName => $exportableItemValue) {
if (!is_null($exportableItemValue)) {
$preparedExportableObject[$exportableItemName] = $exportableItemValue;
}
}
return $preparedExportableObject;
}
/**
* @return string|null
*/
public function getAffiliation(): ?string
{
return $this->affiliation;
}
/**
* @param string|null $affiliation
* @return ItemParameter
*/
public function setAffiliation(?string $affiliation): ItemParameter
{
$this->affiliation = $affiliation;
return $this;
}
/**
* @return string|null
*/
public function getCoupon(): ?string
{
return $this->coupon;
}
/**
* @param string|null $coupon
* @return ItemParameter
*/
public function setCoupon(?string $coupon): ItemParameter
{
$this->coupon = $coupon;
return $this;
}
/**
* @return string|null
*/
public function getCurrency(): ?string
{
return $this->currency;
}
/**
* @param string|null $currency
* @return ItemParameter
*/
public function setCurrency(?string $currency): ItemParameter
{
$this->currency = $currency;
return $this;
}
/**
* @return float|null
*/
public function getDiscount(): ?float
{
return $this->discount;
}
/**
* @param float|null $discount
* @return ItemParameter
*/
public function setDiscount(?float $discount): ItemParameter
{
$this->discount = $discount;
return $this;
}
/**
* @return float|null
*/
public function getIndex(): ?float
{
return $this->index;
}
/**
* @param float|null $index
* @return ItemParameter
*/
public function setIndex(?float $index): ItemParameter
{
$this->index = $index;
return $this;
}
/**
* @return string|null
*/
public function getItemBrand(): ?string
{
return $this->itemBrand;
}
/**
* @param string|null $itemBrand
* @return ItemParameter
*/
public function setItemBrand(?string $itemBrand): ItemParameter
{
$this->itemBrand = $itemBrand;
return $this;
}
/**
* @return string|null
*/
public function getItemCategory(): ?string
{
return $this->itemCategory;
}
/**
* @param string|null $itemCategory
* @return ItemParameter
*/
public function setItemCategory(?string $itemCategory): ItemParameter
{
$this->itemCategory = $itemCategory;
return $this;
}
/**
* @return string|null
*/
public function getItemCategory2(): ?string
{
return $this->itemCategory2;
}
/**
* @param string|null $itemCategory2
* @return ItemParameter
*/
public function setItemCategory2(?string $itemCategory2): ItemParameter
{
$this->itemCategory2 = $itemCategory2;
return $this;
}
/**
* @return string|null
*/
public function getItemCategory3(): ?string
{
return $this->itemCategory3;
}
/**
* @param string|null $itemCategory3
* @return ItemParameter
*/
public function setItemCategory3(?string $itemCategory3): ItemParameter
{
$this->itemCategory3 = $itemCategory3;
return $this;
}
/**
* @return string|null
*/
public function getItemCategory4(): ?string
{
return $this->itemCategory4;
}
/**
* @param string|null $itemCategory4
* @return ItemParameter
*/
public function setItemCategory4(?string $itemCategory4): ItemParameter
{
$this->itemCategory4 = $itemCategory4;
return $this;
}
/**
* @return string|null
*/
public function getItemCategory5(): ?string
{
return $this->itemCategory5;
}
/**
* @param string|null $itemCategory5
* @return ItemParameter
*/
public function setItemCategory5(?string $itemCategory5): ItemParameter
{
$this->itemCategory5 = $itemCategory5;
return $this;
}
/**
* @return string|null
*/
public function getItemListId(): ?string
{
return $this->itemListId;
}
/**
* @param string|null $itemListId
* @return ItemParameter
*/
public function setItemListId(?string $itemListId): ItemParameter
{
$this->itemListId = $itemListId;
return $this;
}
/**
* @return string|null
*/
public function getItemListName(): ?string
{
return $this->itemListName;
}
/**
* @param string|null $itemListName
* @return ItemParameter
*/
public function setItemListName(?string $itemListName): ItemParameter
{
$this->itemListName = $itemListName;
return $this;
}
/**
* @return string|null
*/
public function getItemVariant(): ?string
{
return $this->itemVariant;
}
/**
* @param string|null $itemVariant
* @return ItemParameter
*/
public function setItemVariant(?string $itemVariant): ItemParameter
{
$this->itemVariant = $itemVariant;
return $this;
}
/**
* @return string|null
*/
public function getLocationId(): ?string
{
return $this->locationId;
}
/**
* @param string|null $locationId
* @return ItemParameter
*/
public function setLocationId(?string $locationId): ItemParameter
{
$this->locationId = $locationId;
return $this;
}
/**
* @return float|null
*/
public function getPrice(): ?float
{
return $this->price;
}
/**
* @param float|null $price
* @return ItemParameter
*/
public function setPrice(?float $price): ItemParameter
{
$this->price = $price;
return $this;
}
/**
* @return float|null
*/
public function getQuantity(): ?float
{
return $this->quantity;
}
/**
* @param float|null $quantity
* @return ItemParameter
*/
public function setQuantity(?float $quantity): ItemParameter
{
$this->quantity = $quantity;
return $this;
}
}

View File

@@ -0,0 +1,16 @@
<?php
/**
* User: Damian Zamojski (br33f)
* Date: 22.06.2021
* Time: 11:10
*/
namespace Br33f\Ga4\MeasurementProtocol\Dto\Request;
use Br33f\Ga4\MeasurementProtocol\Dto\ExportableInterface;
use Br33f\Ga4\MeasurementProtocol\Dto\RequestValidateInterface;
abstract class AbstractRequest implements ExportableInterface, RequestValidateInterface
{
}

View File

@@ -0,0 +1,289 @@
<?php
/**
* User: Damian Zamojski (br33f)
* Date: 22.06.2021
* Time: 11:22
*/
namespace Br33f\Ga4\MeasurementProtocol\Dto\Request;
use Br33f\Ga4\MeasurementProtocol\Dto\Common\EventCollection;
use Br33f\Ga4\MeasurementProtocol\Dto\Common\UserProperties;
use Br33f\Ga4\MeasurementProtocol\Dto\Common\UserProperty;
use Br33f\Ga4\MeasurementProtocol\Dto\Event\AbstractEvent;
use Br33f\Ga4\MeasurementProtocol\Enum\ErrorCode;
use Br33f\Ga4\MeasurementProtocol\Exception\ValidationException;
class BaseRequest extends AbstractRequest
{
/**
* Unique identifier of user instance.
* Required
* @var string
*/
protected $clientId = null;
/**
* App Instance ID.
* @var string
*/
protected $appInstanceId = null;
/**
* Unique identifier for a user.
* Not required
* @var string
*/
protected $userId;
/**
* An unix timestamp (microseconds) for the time to associate with the event.
* Not requied
* @var int
*/
protected $timestampMicros = null;
/**
* The user properties for the measurement.
* Not required
* @var UserProperties
*/
protected $userProperties = null;
/**
* If set true - indicates that events should not be use for personalized ads.
* Default false
* @var bool
*/
protected $nonPersonalizedAds = false;
/**
* Collection of event items. Maximum 25 events.
* Required
* @var EventCollection
*/
protected $events;
/**
* BaseRequest constructor.
* @param string|null $clientId
* @param AbstractEvent|EventCollection|null $events - Single Event or EventsCollection
*/
public function __construct(?string $clientId = null, $events = null)
{
if ($clientId !== null) {
@trigger_error('Creating a request by passing a web client ID to the constructor is deprecated in v0.1.3 and removed in v0.2.0. Use ::setClientId() or ::setAppInstanceId() directly, instead.', E_USER_DEPRECATED);
$this->clientId = $clientId;
}
if ($events !== null) {
if ($events instanceof EventCollection) {
$this->events = $events;
} else if ($events instanceof AbstractEvent) {
$this->events = new EventCollection();
$this->events->addEvent($events);
}
} else {
$this->events = new EventCollection();
}
}
/**
* @param UserProperty $userProperty
* @return BaseRequest
*/
public function addUserProperty(UserProperty $userProperty)
{
if ($this->getUserProperties() === null) {
$this->setUserProperties(new UserProperties());
}
$this->getUserProperties()->addUserProperty($userProperty);
return $this;
}
/**
* @return UserProperties|null
*/
public function getUserProperties(): ?UserProperties
{
return $this->userProperties;
}
/**
* @param UserProperties|null $userProperties
* @return BaseRequest
*/
public function setUserProperties(?UserProperties $userProperties)
{
$this->userProperties = $userProperties;
return $this;
}
/**
* @param AbstractEvent $event
* @return BaseRequest
*/
public function addEvent(AbstractEvent $event)
{
$this->getEvents()->addEvent($event);
return $this;
}
/**
* @return EventCollection
*/
public function getEvents(): EventCollection
{
return $this->events;
}
/**
* @param EventCollection $events
* @return BaseRequest
*/
public function setEvents(EventCollection $events)
{
$this->events = $events;
return $this;
}
/**
* @return array
*/
public function export(): array
{
$exportBaseRequest = array_filter([
'client_id' => $this->getClientId(),
'app_instance_id' => $this->getAppInstanceId(),
'non_personalized_ads' => $this->isNonPersonalizedAds(),
'events' => $this->getEvents()->export(),
]);
if ($this->getUserId() !== null) {
$exportBaseRequest['user_id'] = $this->getUserId();
}
if ($this->getTimestampMicros() !== null) {
$exportBaseRequest['timestamp_micros'] = $this->getTimestampMicros();
}
if ($this->getUserProperties() !== null) {
$exportBaseRequest['user_properties'] = $this->getUserProperties()->export();
}
return $exportBaseRequest;
}
/**
* @return string
*/
public function getClientId(): ?string
{
return $this->clientId;
}
/**
* @param string $clientId
* @return BaseRequest
*/
public function setClientId(string $clientId): self
{
$this->clientId = $clientId;
return $this;
}
/**
* @return string|null
*/
public function getAppInstanceId(): ?string
{
return $this->appInstanceId;
}
/**
* @param string $appInstanceId
* @return BaseRequest
*/
public function setAppInstanceId(string $appInstanceId): self
{
$this->appInstanceId = $appInstanceId;
return $this;
}
/**
* @return bool
*/
public function isNonPersonalizedAds(): bool
{
return $this->nonPersonalizedAds;
}
/**
* @param bool $nonPersonalizedAds
* @return BaseRequest
*/
public function setNonPersonalizedAds(bool $nonPersonalizedAds)
{
$this->nonPersonalizedAds = $nonPersonalizedAds;
return $this;
}
/**
* @return string|null
*/
public function getUserId(): ?string
{
return $this->userId;
}
/**
* @param string|null $userId
* @return BaseRequest
*/
public function setUserId(?string $userId)
{
$this->userId = $userId;
return $this;
}
/**
* @return ?int
*/
public function getTimestampMicros(): ?int
{
return $this->timestampMicros;
}
/**
* @param ?int $timestampMicros
* @return BaseRequest
*/
public function setTimestampMicros(?int $timestampMicros)
{
$this->timestampMicros = $timestampMicros;
return $this;
}
/**
* @param string|null $context Context for request, either 'web' or 'firebase'.
* @return bool
* @throws ValidationException
*/
public function validate(?string $context = 'web')
{
if ($context === 'web' && empty($this->getClientId())) {
throw new ValidationException('Parameter "client_id" is required.', ErrorCode::VALIDATION_CLIENT_ID_REQUIRED, 'client_id');
}
if ($context === 'firebase' && empty($this->getAppInstanceId())) {
throw new ValidationException('Parameter "app_instance_id" is required.', ErrorCode::VALIDATION_APP_INSTANCE_ID_REQUIRED, 'app_instance_id');
}
if ($this->getClientId() && $this->getAppInstanceId()) {
throw new ValidationException('Cannot specify both "client_id" and "app_instance_id".', ErrorCode::VALIDATION_CLIENT_IDENTIFIER_MISCONFIGURED);
}
$this->getEvents()->validate();
return true;
}
}

View File

@@ -0,0 +1,21 @@
<?php
/**
* User: Damian Zamojski (br33f)
* Date: 22.06.2021
* Time: 11:10
*/
namespace Br33f\Ga4\MeasurementProtocol\Dto;
use Br33f\Ga4\MeasurementProtocol\Exception\ValidationException;
interface RequestValidateInterface
{
/**
* Method validates object. Throws exception if error, returns true if valid.
* @param string|null $context
* @return boolean
* @throws ValidationException
*/
public function validate(?string $context);
}

View File

@@ -0,0 +1,28 @@
<?php
/**
* User: Damian Zamojski (br33f)
* Date: 22.06.2021
* Time: 11:10
*/
namespace Br33f\Ga4\MeasurementProtocol\Dto\Response;
use Br33f\Ga4\MeasurementProtocol\Dto\HydratableInterface;
use Br33f\Ga4\MeasurementProtocol\Exception\HydrationException;
use Psr\Http7\Message\ResponseInterface;
abstract class AbstractResponse implements HydratableInterface
{
/**
* AbstractResponse constructor.
* @param ResponseInterface|null $blueprint
* @throws HydrationException
*/
public function __construct(ResponseInterface $blueprint = null)
{
if ($blueprint !== null) {
$this->hydrate($blueprint);
}
}
}

View File

@@ -0,0 +1,77 @@
<?php
/**
* User: Damian Zamojski (br33f)
* Date: 22.06.2021
* Time: 11:10
*/
namespace Br33f\Ga4\MeasurementProtocol\Dto\Response;
use Psr\Http7\Message\ResponseInterface;
class BaseResponse extends AbstractResponse
{
/**
* @var int|null
*/
protected $statusCode;
/**
* @var string
*/
protected $body;
/**
* @return int|null
*/
public function getStatusCode(): ?int
{
return $this->statusCode;
}
/**
* @param int|null $statusCode
* @return BaseResponse
*/
public function setStatusCode(?int $statusCode)
{
$this->statusCode = $statusCode;
return $this;
}
/**
* Get parsed body
* @return array
*/
public function getData()
{
return json_decode($this->getBody(), true);
}
/**
* @return string
*/
public function getBody(): string
{
return $this->body;
}
/**
* @param string $body
* @return BaseResponse
*/
public function setBody(string $body)
{
$this->body = $body;
return $this;
}
/**
* @param array|ResponseInterface $blueprint
*/
public function hydrate($blueprint)
{
$this->setStatusCode($blueprint->getStatusCode());
$this->setBody($blueprint->getBody()->getContents());
}
}

View File

@@ -0,0 +1,51 @@
<?php
/**
* User: Damian Zamojski (br33f)
* Date: 22.06.2021
* Time: 11:10
*/
namespace Br33f\Ga4\MeasurementProtocol\Dto\Response;
use Br33f\Ga4\MeasurementProtocol\Dto\Common\ValidationMessage;
use Psr\Http7\Message\ResponseInterface;
class DebugResponse extends BaseResponse
{
/**
* @var ValidationMessage[]
*/
protected $validationMessages = [];
/**
* @return ValidationMessage[]
*/
public function getValidationMessages(): array
{
return $this->validationMessages;
}
/**
* @param ValidationMessage[] $validationMessages
* @return DebugResponse
*/
public function setValidationMessages(array $validationMessages)
{
$this->validationMessages = $validationMessages;
return $this;
}
/**
* @param array|ResponseInterface $blueprint
*/
public function hydrate($blueprint)
{
parent::hydrate($blueprint);
$validationMessages = [];
foreach ($this->getData()['validationMessages'] as $validationMessage) {
$validationMessages[] = new ValidationMessage($validationMessage);
}
$this->setValidationMessages($validationMessages);
}
}

View File

@@ -0,0 +1,20 @@
<?php
/**
* User: Damian Zamojski (br33f)
* Date: 22.06.2021
* Time: 11:10
*/
namespace Br33f\Ga4\MeasurementProtocol\Dto;
use Br33f\Ga4\MeasurementProtocol\Exception\ValidationException;
interface ValidateInterface
{
/**
* Method validates object. Throws exception if error, returns true if valid.
* @return boolean
* @throws ValidationException
*/
public function validate();
}

View File

@@ -0,0 +1,20 @@
<?php
/**
* User: Damian Zamojski (br33f)
* Date: 22.06.2021
* Time: 15:29
*/
namespace Br33f\Ga4\MeasurementProtocol\Enum;
class ErrorCode
{
const MAX_EVENT_COUNT_EXCEED = 100001;
const VALIDATION_CLIENT_ID_REQUIRED = 1000002;
const VALIDATION_EVENTS_MUST_NOT_BE_EMPTY = 1000003;
const VALIDATION_ITEM_AT_LEAST_ITEM_ID_OR_ITEM_NAME_REQUIRED = 1000004;
const VALIDATION_FIELD_REQUIRED = 1000005;
const VALIDATION_APP_INSTANCE_ID_REQUIRED = 1000006;
const VALIDATION_CLIENT_IDENTIFIER_MISCONFIGURED = 1000007;
}

View File

@@ -0,0 +1,19 @@
<?php
/**
* User: Damian Zamojski (br33f)
* Date: 24.06.2021
* Time: 13:49
*/
namespace Br33f\Ga4\MeasurementProtocol\Enum;
class ValidationCode
{
const VALUE_INVALID = 'VALUE_INVALID';
const VALUE_REQUIRED = 'VALUE_REQUIRED';
const NAME_INVALID = 'NAME_INVALID';
const NAME_RESERVED = 'NAME_RESERVED';
const VALUE_OUT_OF_BOUNDS = 'VALUE_OUT_OF_BOUNDS';
const EXCEEDED_MAX_ENTITIES = 'EXCEEDED_MAX_ENTITIES';
const NAME_DUPLICATED = 'NAME_DUPLICATED';
}

View File

@@ -0,0 +1,5 @@
<?php
namespace Br33f\Ga4\MeasurementProtocol\Exception;
abstract class AnalyticsException extends \Exception {};

View File

@@ -0,0 +1,16 @@
<?php
/**
* User: Damian Zamojski (br33f)
* Date: 22.06.2021
* Time: 11:20
*/
namespace Br33f\Ga4\MeasurementProtocol\Exception;
use Exception;
class HydrationException extends AnalyticsException
{
}

View File

@@ -0,0 +1,10 @@
<?php
namespace Br33f\Ga4\MeasurementProtocol\Exception;
use Exception;
class MisconfigurationException extends AnalyticsException
{
}

View File

@@ -0,0 +1,42 @@
<?php
/**
* User: Damian Zamojski (br33f)
* Date: 22.06.2021
* Time: 11:20
*/
namespace Br33f\Ga4\MeasurementProtocol\Exception;
use Exception;
use Throwable;
class ValidationException extends AnalyticsException
{
/**
* @var string|null
*/
protected $fieldName = null;
public function __construct($message = "", $code = 0, $fieldName = null, Throwable $previous = null)
{
parent::__construct($message, $code, $previous);
$this->fieldName = $fieldName;
}
/**
* @return string|null
*/
public function getFieldName(): ?string
{
return $this->fieldName;
}
/**
* @param string|null $fieldName
*/
public function setFieldName(?string $fieldName): void
{
$this->fieldName = $fieldName;
}
}

View File

@@ -0,0 +1,83 @@
<?php
/**
* User: Damian Zamojski (br33f)
* Date: 22.06.2021
* Time: 10:35
*/
namespace Br33f\Ga4\MeasurementProtocol;
use GuzzleHttp7\Client;
use GuzzleHttp7\Exception\BadResponseException;
use GuzzleHttp7\RequestOptions;
use Psr\Http7\Message\ResponseInterface;
class HttpClient
{
const DEFAULT_REQUEST_TIMEOUT = 30;
/**
* Guzzle Client
* @var Client
*/
protected $client;
/**
* Sends request to Google Analytics.
*
* @param string $url
* @param array $data
* @param array|null $options
* @return ResponseInterface
*/
public function post(string $url, array $data, ?array $options = [])
{
try {
return $this->getClient()->post($url, $this->getPreparedOptions($options, $data));
} catch (BadResponseException $e) {
return $e->getResponse();
}
}
/**
* Returns guzzle client if set or creates a new instance and returns it
* @return Client
*/
public function getClient(): Client
{
if ($this->client === null) {
$this->client = new Client();
}
return $this->client;
}
/**
* @param Client $client
*/
public function setClient(Client $client)
{
$this->client = $client;
}
/**
* @param $options
* @param $data
* @return array
*/
protected function getPreparedOptions($options, $data)
{
$options[RequestOptions::JSON] = $data;
if (!isset($options['timeout'])) {
$options['timeout'] = self::DEFAULT_REQUEST_TIMEOUT;
}
if (!isset($options['connect_timeout'])) {
$options['connect_timeout'] = self::DEFAULT_REQUEST_TIMEOUT;
}
return $options;
}
}

View File

@@ -0,0 +1,331 @@
<?php
/**
* User: Damian Zamojski (br33f)
* Date: 21.06.2021
* Time: 16:15
*/
namespace Br33f\Ga4\MeasurementProtocol;
use Br33f\Ga4\MeasurementProtocol\Dto\Request\AbstractRequest;
use Br33f\Ga4\MeasurementProtocol\Dto\Response\BaseResponse;
use Br33f\Ga4\MeasurementProtocol\Dto\Response\DebugResponse;
use Br33f\Ga4\MeasurementProtocol\Exception\MisconfigurationException;
class Service
{
const SSL_SCHEME = 'https://';
const NOT_SSL_SCHEME = 'http://';
const PREPENDED_WWW = 'www';
/**
* Indicates if connection to endpoint should be made with HTTPS (true) or HTTP (false)
* @var bool
*/
protected $useSsl = true;
/**
* Indicates if connection to endpoint should be made with prepended WWW
* @var bool
*/
protected $useWww = false;
/**
* Collect Endpoint
* @var string
*/
protected $collectEndpoint = 'google-analytics.com/mp/collect';
/**
* Collect Debug Endpoint. Used for validating events.
* @var string
*/
protected $collectDebugEndpoint = 'google-analytics.com/debug/mp/collect';
/**
* Http Client
* @var HttpClient
*/
protected $httpClient;
/**
* An API SECRET generated in the Google Analytics UI
* @var string
*/
protected $apiSecret = null;
/**
* The measurement ID associated with a data stream
* @var string
*/
protected $measurementId = null;
/**
* The Firebase App ID associated with a data stream
* @var string
*/
protected $firebaseId = null;
/**
* The custom ip address of the visitor
* @var string
*/
protected $ipOverride;
/**
* Http Options
* @var array
*/
protected $options;
/**
* Client constructor.
* @param string $apiSecret
* @param string|null $measurementId
*/
public function __construct(string $apiSecret, ?string $measurementId = null)
{
$this->setApiSecret($apiSecret);
if ($measurementId) {
@trigger_error('Creating a measurement service instance with a measurement ID passed to the constructor is deprecated in v0.1.3 and removed in v0.2.0. Use ::setMeasurementId() or ::setFirebaseId() directly, instead.', E_USER_DEPRECATED);
$this->setMeasurementId($measurementId);
}
}
/**
* @param AbstractRequest $request
* @param bool|null $debug
* @return BaseResponse
* @throws Exception\ValidationException
* @throws Exception\HydrationException
*/
public function send(AbstractRequest $request, ?bool $debug = false)
{
$request->validate($this->measurementId ? 'web' : 'firebase');
$response = $this->getHttpClient()->post($this->getEndpoint($debug), $request->export(), $this->getOptions());
return !$debug
? new BaseResponse($response)
: new DebugResponse($response);
}
/**
* @param AbstractRequest $request
* @return BaseResponse
* @throws Exception\ValidationException
* @throws Exception\HydrationException
*/
public function sendDebug(AbstractRequest $request)
{
return $this->send($request, true);
}
/**
* Returns Http Client if set or creates a new instance and returns it
* @return HttpClient
*/
public function getHttpClient(): HttpClient
{
if ($this->httpClient === null) {
$this->httpClient = new HttpClient();
}
return $this->httpClient;
}
/**
* @param HttpClient $httpClient
*/
public function setHttpClient(HttpClient $httpClient)
{
$this->httpClient = $httpClient;
}
/**
* Returns prepared endpoint url
* @return string
*/
public function getEndpoint(?bool $isDebug = false): string
{
$protocolScheme = $this->isUseSsl() ? self::SSL_SCHEME : self::NOT_SSL_SCHEME;
$collectEndpoint = $isDebug ? $this->getCollectDebugEndpoint() : $this->getCollectEndpoint();
$prependedWww = $this->isUseWww() ? (self::PREPENDED_WWW . '.') : '';
return $protocolScheme . $prependedWww . $collectEndpoint . "?" . http_build_query($this->getQueryParameters());
}
/**
* @return bool
*/
public function isUseSsl(): bool
{
return $this->useSsl;
}
/**
* @param bool $useSsl
*/
public function setUseSsl(bool $useSsl)
{
$this->useSsl = $useSsl;
}
/**
* @return bool
*/
public function isUseWww(): bool
{
return $this->useWww;
}
/**
* @param bool $useWww
*/
public function setUseWww(bool $useWww)
{
$this->useWww = $useWww;
}
/**
* @return string
*/
public function getCollectDebugEndpoint(): string
{
return $this->collectDebugEndpoint;
}
/**
* @param string $collectDebugEndpoint
*/
public function setCollectDebugEndpoint(string $collectDebugEndpoint)
{
$this->collectDebugEndpoint = $collectDebugEndpoint;
}
/**
* @return string
*/
public function getCollectEndpoint(): string
{
return $this->collectEndpoint;
}
/**
* @param string $collectEndpoint
*/
public function setCollectEndpoint(string $collectEndpoint)
{
$this->collectEndpoint = $collectEndpoint;
}
/**
* Returns query parameters
* @return array
* @throws MisconfigurationException
*/
public function getQueryParameters(): array
{
$parameters = [
'api_secret' => $this->getApiSecret(),
'measurement_id' => $this->getMeasurementId(),
'firebase_app_id' => $this->getFirebaseId(),
];
if ($parameters['firebase_app_id'] && $parameters['measurement_id']) {
throw new MisconfigurationException("Cannot specify both 'measurement_id' and 'firebase_app_id'.");
}
$ip = $this->getIpOverride();
if (!empty($ip)) {
$parameters['uip'] = $ip;
// TODO Remove the following line when the GA4 API will support the IP override
// https://github.com/dataunlocker/save-analytics-from-content-blockers/issues/25#issuecomment-864392422
$parameters['_uip'] = $ip;
}
return array_filter($parameters);
}
/**
* @return string
*/
public function getMeasurementId(): ?string
{
return $this->measurementId;
}
/**
* @param string $measurementId
* @return self
*/
public function setMeasurementId(string $measurementId): self
{
$this->measurementId = $measurementId;
return $this;
}
/**
* @return string|null
*/
public function getFirebaseId(): ?string
{
return $this->firebaseId;
}
/**
* @param string $firebaseId
* @return self
*/
public function setFirebaseId(string $firebaseId): self
{
$this->firebaseId = $firebaseId;
return $this;
}
/**
* @return string
*/
public function getApiSecret(): string
{
return $this->apiSecret;
}
/**
* @param string $apiSecret
*/
public function setApiSecret(string $apiSecret)
{
$this->apiSecret = $apiSecret;
}
/**
* @return string
*/
public function getIpOverride(): ?string
{
return $this->ipOverride;
}
/**
* @param string $ipOverride
*/
public function setIpOverride(string $ipOverride)
{
$this->ipOverride = $ipOverride;
}
/**
* @return array|null
*/
public function getOptions(): ?array
{
return $this->options;
}
/**
* @param array|null $options
*/
public function setOptions(?array $options)
{
$this->options = $options;
}
}

View File

@@ -0,0 +1,25 @@
<?php
/**
* User: Damian Zamojski (br33f)
* Date: 22.06.2021
* Time: 14:49
*/
namespace Tests\Common;
use Faker\Factory;
use PHPUnit\Framework\TestCase;
class BaseTestCase extends TestCase
{
protected $faker;
/**
* BaseTestCase constructor.
*/
public function __construct()
{
parent::__construct();
$this->faker = Factory::create();
}
}

View File

@@ -0,0 +1,109 @@
<?php
/**
* User: Damian Zamojski (br33f)
* Date: 22.06.2021
* Time: 15:23
*/
namespace Tests\Ga4\MeasurementProtocol\Dto\Common;
use Br33f\Ga4\MeasurementProtocol\Dto\Common\EventCollection;
use Br33f\Ga4\MeasurementProtocol\Dto\Event\BaseEvent;
use Br33f\Ga4\MeasurementProtocol\Dto\Parameter\BaseParameter;
use Br33f\Ga4\MeasurementProtocol\Enum\ErrorCode;
use Tests\Common\BaseTestCase;
class EventCollectionTest extends BaseTestCase
{
/**
* @var EventCollection
*/
protected $eventCollection;
public function testEventList()
{
$setEventList = [];
for ($i = 0; $i < rand(1, 25); $i++) {
$setEventList[] = new BaseEvent();
}
$this->eventCollection->setEventList($setEventList);
$this->assertEquals($setEventList, $this->eventCollection->getEventList());
}
public function testSetEventListExceed25()
{
$setEventList = [];
for ($i = 0; $i < rand(26, 50); $i++) {
$setEventList[] = new BaseEvent();
}
$this->expectExceptionCode(ErrorCode::MAX_EVENT_COUNT_EXCEED);
$this->eventCollection->setEventList($setEventList);
}
public function testAddEvent()
{
$this->eventCollection->setEventList([]);
$newEvent = new BaseEvent();
$this->eventCollection->addEvent($newEvent);
$this->assertEquals(1, count($this->eventCollection->getEventList()));
$this->assertEquals($newEvent, $this->eventCollection->getEventList()[0]);
}
public function testAddEventExceed25()
{
$setEventList = [];
for ($i = 0; $i < 25; $i++) {
$setEventList[] = new BaseEvent();
}
$this->eventCollection->setEventList($setEventList);
$newEvent = new BaseEvent();
$this->expectExceptionCode(ErrorCode::MAX_EVENT_COUNT_EXCEED);
$this->eventCollection->addEvent($newEvent);
}
public function testExport()
{
$setEventListExport = [];
$setEventList = [];
for ($i = 0; $i < rand(1, 25); $i++) {
$event = new BaseEvent($this->faker->word);
$event->addParam($this->faker->word, new BaseParameter($this->faker->word));
$setEventList[] = $event;
$setEventListExport[] = $event->export();
}
$this->eventCollection->setEventList($setEventList);
$this->assertEquals($setEventListExport, $this->eventCollection->export());
}
public function testValidateFailed()
{
$newEventCollection = new EventCollection();
$this->expectExceptionCode(ErrorCode::VALIDATION_EVENTS_MUST_NOT_BE_EMPTY);
$newEventCollection->validate();
}
public function testValidateSuccess()
{
$newEventCollection = new EventCollection();
$event = new BaseEvent($this->faker->word);
$event->addParam($this->faker->word, new BaseParameter($this->faker->word));
$newEventCollection->addEvent($event);
$this->assertTrue($newEventCollection->validate());
}
protected function setUp(): void
{
parent::setUp();
$this->eventCollection = new EventCollection();
}
}

View File

@@ -0,0 +1,90 @@
<?php
/**
* User: Damian Zamojski (br33f)
* Date: 22.06.2021
* Time: 15:56
*/
namespace Tests\Ga4\MeasurementProtocol\Dto\Common;
use Br33f\Ga4\MeasurementProtocol\Dto\Common\UserProperties;
use Br33f\Ga4\MeasurementProtocol\Dto\Common\UserProperty;
use Tests\Common\BaseTestCase;
class UserPropertiesTest extends BaseTestCase
{
/**
* @var UserProperties
*/
protected $userProperties;
public function testDefaultConstructor()
{
$constructedUserProperties = new UserProperties();
$this->assertEquals([], $constructedUserProperties->getUserPropertiesList());
}
public function testConstructor()
{
$setUserProperties = [
new UserProperty(),
new UserProperty()
];
$constructedUserProperties = new UserProperties($setUserProperties);
$this->assertEquals($setUserProperties, $constructedUserProperties->getUserPropertiesList());
}
public function testUserPropertiesList()
{
$setUserProperties = [
new UserProperty($this->faker->word, $this->faker->word),
new UserProperty($this->faker->word, $this->faker->word),
new UserProperty($this->faker->word, $this->faker->word)
];
$this->userProperties->setUserPropertiesList($setUserProperties);
$this->assertEquals($setUserProperties, $this->userProperties->getUserPropertiesList());
}
public function testUserPropertyAdd()
{
$this->userProperties->setUserPropertiesList([]);
$addUserProperty = new UserProperty($this->faker->word, $this->faker->word);
$this->userProperties->addUserProperty($addUserProperty);
$this->assertEquals(1, count($this->userProperties->getUserPropertiesList()));
$this->assertEquals($addUserProperty, $this->userProperties->getUserPropertiesList()[0]);
}
public function testExport()
{
$setUserProperties = [
new UserProperty($this->faker->word, $this->faker->word),
new UserProperty($this->faker->word, $this->faker->word),
new UserProperty($this->faker->word, $this->faker->word)
];
$this->userProperties->setUserPropertiesList($setUserProperties);
$this->assertEquals([
$setUserProperties[0]->getName() => [
'value' => $setUserProperties[0]->getValue()
],
$setUserProperties[1]->getName() => [
'value' => $setUserProperties[1]->getValue()
],
$setUserProperties[2]->getName() => [
'value' => $setUserProperties[2]->getValue()
],
], $this->userProperties->export());
}
protected function setUp(): void
{
$this->userProperties = new UserProperties();
}
}

View File

@@ -0,0 +1,75 @@
<?php
/**
* User: Damian Zamojski (br33f)
* Date: 22.06.2021
* Time: 15:47
*/
namespace Tests\Ga4\MeasurementProtocol\Dto\Common;
use Br33f\Ga4\MeasurementProtocol\Dto\Common\UserProperty;
use Tests\Common\BaseTestCase;
class UserPropertyTest extends BaseTestCase
{
/**
* @var UserProperty
*/
protected $userProperty;
public function testDefaultConstructor()
{
$constructedUserProperty = new UserProperty();
$this->assertEquals(null, $constructedUserProperty->getName());
$this->assertEquals(null, $constructedUserProperty->getValue());
}
public function testParametrizedConstructor()
{
$setName = $this->faker->word;
$setValue = $this->faker->word;
$constructedUserProperty = new UserProperty($setName, $setValue);
$this->assertEquals($setName, $constructedUserProperty->getName());
$this->assertEquals($setValue, $constructedUserProperty->getValue());
}
public function testName()
{
$setName = $this->faker->word;
$this->userProperty->setName($setName);
$this->assertEquals($setName, $this->userProperty->getName());
}
public function testValue()
{
$setValue = $this->faker->word;
$this->userProperty->setValue($setValue);
$this->assertEquals($setValue, $this->userProperty->getValue());
}
public function testExportEmpty()
{
$emptyUserProperty = new UserProperty();
$this->assertEquals([null => ['value' => null]], $emptyUserProperty->export());
}
public function testExport()
{
$setName = $this->faker->word;
$setValue = $this->faker->word;
$emptyUserProperty = new UserProperty($setName, $setValue);
$this->assertEquals([$setName => ['value' => $setValue]], $emptyUserProperty->export());
}
protected function setUp(): void
{
parent::setUp();
$this->userProperty = new UserProperty();
}
}

View File

@@ -0,0 +1,83 @@
<?php
/**
* User: Damian Zamojski (br33f)
* Date: 24.06.2021
* Time: 13:54
*/
namespace Tests\Ga4\MeasurementProtocol\Dto\Common;
use Br33f\Ga4\MeasurementProtocol\Dto\Common\ValidationMessage;
use Br33f\Ga4\MeasurementProtocol\Enum\ValidationCode;
use Tests\Common\BaseTestCase;
class ValidationMessageTest extends BaseTestCase
{
/**
* @var ValidationMessage
*/
protected $validationMessage;
public function testDefaultConstructor()
{
$constructedValidationMessage = new ValidationMessage();
$this->assertNotNull($constructedValidationMessage);
}
public function testArrayBlueprintConstructor()
{
$blueprint = ['fieldPath' => $this->faker->word];
$constructedValidationMessage = new ValidationMessage($blueprint);
$this->assertNotNull($constructedValidationMessage);
$this->assertEquals($blueprint['fieldPath'], $constructedValidationMessage->getFieldPath());
$this->assertEquals(null, $constructedValidationMessage->getDescription());
$this->assertEquals(null, $constructedValidationMessage->getValidationCode());
}
public function testHydrate()
{
$blueprint = [
'fieldPath' => $this->faker->word,
'description' => $this->faker->text,
'validationCode' => $this->faker->randomElement([ValidationCode::EXCEEDED_MAX_ENTITIES, ValidationCode::NAME_DUPLICATED, ValidationCode::NAME_RESERVED])
];
$constructedValidationMessage = new ValidationMessage($blueprint);
$this->assertNotNull($constructedValidationMessage);
$this->assertEquals($blueprint['fieldPath'], $constructedValidationMessage->getFieldPath());
$this->assertEquals($blueprint['description'], $constructedValidationMessage->getDescription());
$this->assertEquals($blueprint['validationCode'], $constructedValidationMessage->getValidationCode());
}
public function testFieldPath()
{
$setFieldPath = $this->faker->word;
$this->validationMessage->setFieldPath($setFieldPath);
$this->assertEquals($setFieldPath, $this->validationMessage->getFieldPath());
}
public function testValidationCode()
{
$setValidationCode = $this->faker->randomElement([ValidationCode::EXCEEDED_MAX_ENTITIES, ValidationCode::NAME_DUPLICATED, ValidationCode::NAME_RESERVED]);
$this->validationMessage->setValidationCode($setValidationCode);
$this->assertEquals($setValidationCode, $this->validationMessage->getValidationCode());
}
public function testDescription()
{
$setDescription = $this->faker->text;
$this->validationMessage->setDescription($setDescription);
$this->assertEquals($setDescription, $this->validationMessage->getDescription());
}
protected function setUp(): void
{
parent::setUp();
$this->validationMessage = new ValidationMessage();
}
}

View File

@@ -0,0 +1,50 @@
<?php
/**
* User: Damian Zamojski (br33f)
* Date: 25.06.2021
* Time: 13:40
*/
namespace Tests\Ga4\MeasurementProtocol\Dto\Event;
use Br33f\Ga4\MeasurementProtocol\Dto\Event\AddPaymentInfoEvent;
use Br33f\Ga4\MeasurementProtocol\Exception\ValidationException;
use Tests\Common\BaseTestCase;
class AddPaymentInfoEventTest extends BaseTestCase
{
/**
* @var AddPaymentInfoEvent
*/
protected $event;
public function testDefaultConstructor()
{
$constructedEvent = new AddPaymentInfoEvent();
$this->assertNotNull($constructedEvent);
$this->assertEquals('add_payment_info', $constructedEvent->getName());
}
public function testValidateSuccess()
{
$this->event->setValue($this->faker->randomFloat(2, 10, 3000));
$this->event->setCurrency($this->faker->currencyCode);
$this->assertTrue($this->event->validate());
}
public function testValidateFail()
{
$this->event->setValue($this->faker->randomFloat(2, 10, 3000));
$this->expectException(ValidationException::class);
$this->event->validate();
}
protected function setUp(): void
{
parent::setUp();
$this->event = new AddPaymentInfoEvent();
}
}

View File

@@ -0,0 +1,50 @@
<?php
/**
* User: Damian Zamojski (br33f)
* Date: 25.06.2021
* Time: 13:40
*/
namespace Tests\Ga4\MeasurementProtocol\Dto\Event;
use Br33f\Ga4\MeasurementProtocol\Dto\Event\AddShippingInfoEvent;
use Br33f\Ga4\MeasurementProtocol\Exception\ValidationException;
use Tests\Common\BaseTestCase;
class AddShippingInfoEventTest extends BaseTestCase
{
/**
* @var AddShippingInfoEvent
*/
protected $event;
public function testDefaultConstructor()
{
$constructedEvent = new AddShippingInfoEvent();
$this->assertNotNull($constructedEvent);
$this->assertEquals('add_shipping_info', $constructedEvent->getName());
}
public function testValidateSuccess()
{
$this->event->setValue($this->faker->randomFloat(2, 10, 3000));
$this->event->setCurrency($this->faker->currencyCode);
$this->assertTrue($this->event->validate());
}
public function testValidateFail()
{
$this->event->setValue($this->faker->randomFloat(2, 10, 3000));
$this->expectException(ValidationException::class);
$this->event->validate();
}
protected function setUp(): void
{
parent::setUp();
$this->event = new AddShippingInfoEvent();
}
}

View File

@@ -0,0 +1,50 @@
<?php
/**
* User: Damian Zamojski (br33f)
* Date: 25.06.2021
* Time: 13:40
*/
namespace Tests\Ga4\MeasurementProtocol\Dto\Event;
use Br33f\Ga4\MeasurementProtocol\Dto\Event\AddToCartEvent;
use Br33f\Ga4\MeasurementProtocol\Exception\ValidationException;
use Tests\Common\BaseTestCase;
class AddToCartEventTest extends BaseTestCase
{
/**
* @var AddToCartEvent
*/
protected $event;
public function testDefaultConstructor()
{
$constructedEvent = new AddToCartEvent();
$this->assertNotNull($constructedEvent);
$this->assertEquals('add_to_cart', $constructedEvent->getName());
}
public function testValidateSuccess()
{
$this->event->setValue($this->faker->randomFloat(2, 10, 3000));
$this->event->setCurrency($this->faker->currencyCode);
$this->assertTrue($this->event->validate());
}
public function testValidateFail()
{
$this->event->setValue($this->faker->randomFloat(2, 10, 3000));
$this->expectException(ValidationException::class);
$this->event->validate();
}
protected function setUp(): void
{
parent::setUp();
$this->event = new AddToCartEvent();
}
}

View File

@@ -0,0 +1,171 @@
<?php
/**
* User: Damian Zamojski (br33f)
* Date: 22.06.2021
* Time: 15:05
*/
namespace Tests\Ga4\MeasurementProtocol\Dto\Event;
use BadMethodCallException;
use Br33f\Ga4\MeasurementProtocol\Dto\Event\BaseEvent;
use Br33f\Ga4\MeasurementProtocol\Dto\Parameter\BaseParameter;
use InvalidArgumentException;
use Tests\Common\BaseTestCase;
class BaseEventTest extends BaseTestCase
{
/**
* @var BaseEvent
*/
protected $event;
public function testDefaultConstructor()
{
$constructedEvent = new BaseEvent();
$this->assertEquals(null, $constructedEvent->getName());
$this->assertEquals([], $constructedEvent->getParamList());
}
public function testNameOnlyConstructor()
{
$setupName = $this->faker->word;
$constructedEvent = new BaseEvent($setupName);
$this->assertEquals($setupName, $constructedEvent->getName());
$this->assertEquals([], $constructedEvent->getParamList());
}
public function testParamListOnlyConstructor()
{
$baseParams = [];
for ($i = 0; $i < rand(4, 10); $i++) {
$baseParams[$this->faker->word] = new BaseParameter($this->faker->word);
}
$constructedEvent = new BaseEvent(null, $baseParams);
$this->assertEquals(null, $constructedEvent->getName());
$this->assertEquals($baseParams, $constructedEvent->getParamList());
}
public function testFullConstructor()
{
$setupName = $this->faker->word;
$baseParams = [];
for ($i = 0; $i < rand(4, 10); $i++) {
$baseParams[$this->faker->word] = new BaseParameter($this->faker->word);
}
$constructedEvent = new BaseEvent($setupName, $baseParams);
$this->assertEquals($setupName, $constructedEvent->getName());
$this->assertEquals($baseParams, $constructedEvent->getParamList());
}
public function testName()
{
$setupName = $this->faker->word;
$this->event->setName($setupName);
$this->assertEquals($setupName, $this->event->getName());
}
public function testParamList()
{
$baseParams = [];
for ($i = 0; $i < rand(4, 10); $i++) {
$baseParams[$this->faker->word] = new BaseParameter($this->faker->word);
}
$this->event->setParamList($baseParams);
$this->assertEquals($baseParams, $this->event->getParamList());
}
public function testAddParam()
{
$this->event->setParamList([]);
$paramName = $this->faker->word;
$paramToAdd = new BaseParameter($this->faker->word);
$this->event->addParam($paramName, $paramToAdd);
$this->assertEquals(1, count($this->event->getParamList()));
$this->assertEquals($paramToAdd, $this->event->getParamList()[$paramName]);
}
public function testDeleteParam()
{
$this->event->setParamList([]);
$paramName = $this->faker->word;
$paramToAdd = new BaseParameter($this->faker->word);
$this->event->addParam($paramName, $paramToAdd);
$this->assertEquals(1, count($this->event->getParamList()));
$this->event->deleteParameter($paramName);
$this->assertEquals(0, count($this->event->getParamList()));
}
public function testExportEmpty()
{
$emptyEvent = new BaseEvent();
$this->assertEquals(['name' => null, 'params' => new \ArrayObject()], $emptyEvent->export());
}
public function testExport()
{
$setupName = $this->faker->word;
$baseParamsExport = [];
$baseParams = [];
for ($i = 0; $i < rand(4, 10); $i++) {
$baseParamName = $this->faker->word;
$baseParam = new BaseParameter($this->faker->word);
$baseParams[$baseParamName] = $baseParam;
$baseParamsExport[$baseParamName] = $baseParam->export();
}
$constructedEvent = new BaseEvent($setupName, $baseParams);
$this->assertEquals(['name' => $setupName, 'params' => new \ArrayObject($baseParamsExport)], $constructedEvent->export());
}
public function testSetGetCall()
{
$this->event->setTest1('test1value');
$this->assertEquals('test1value', $this->event->getTest1());
}
public function testSetCallNoParams()
{
$this->expectException(InvalidArgumentException::class);
$this->event->setTest2();
}
public function testUnknownCallMethod()
{
$this->expectException(BadMethodCallException::class);
$this->event->invalidMethodName();
}
public function testValidate()
{
$setupName = $this->faker->word;
$baseParams = [];
for ($i = 0; $i < rand(4, 10); $i++) {
$baseParam = new BaseParameter($this->faker->word);
$baseParams[$this->faker->word] = $baseParam;
}
$constructedEvent = new BaseEvent($setupName, $baseParams);
$this->assertTrue($constructedEvent->validate());
}
protected function setUp(): void
{
$this->event = new BaseEvent();
}
}

View File

@@ -0,0 +1,50 @@
<?php
/**
* User: Damian Zamojski (br33f)
* Date: 25.06.2021
* Time: 13:40
*/
namespace Tests\Ga4\MeasurementProtocol\Dto\Event;
use Br33f\Ga4\MeasurementProtocol\Dto\Event\BeginCheckoutEvent;
use Br33f\Ga4\MeasurementProtocol\Exception\ValidationException;
use Tests\Common\BaseTestCase;
class BeginCheckoutEventTest extends BaseTestCase
{
/**
* @var BeginCheckoutEvent
*/
protected $event;
public function testDefaultConstructor()
{
$constructedEvent = new BeginCheckoutEvent();
$this->assertNotNull($constructedEvent);
$this->assertEquals('begin_checkout', $constructedEvent->getName());
}
public function testValidateSuccess()
{
$this->event->setValue($this->faker->randomFloat(2, 10, 3000));
$this->event->setCurrency($this->faker->currencyCode);
$this->assertTrue($this->event->validate());
}
public function testValidateFail()
{
$this->event->setValue($this->faker->randomFloat(2, 10, 3000));
$this->expectException(ValidationException::class);
$this->event->validate();
}
protected function setUp(): void
{
parent::setUp();
$this->event = new BeginCheckoutEvent();
}
}

View File

@@ -0,0 +1,40 @@
<?php
/**
* User: Damian Zamojski (br33f)
* Date: 25.06.2021
* Time: 13:40
*/
namespace Tests\Ga4\MeasurementProtocol\Dto\Event;
use Br33f\Ga4\MeasurementProtocol\Dto\Event\LoginEvent;
use Tests\Common\BaseTestCase;
class LoginEventTest extends BaseTestCase
{
/**
* @var LoginEvent
*/
protected $event;
public function testDefaultConstructor()
{
$constructedEvent = new LoginEvent();
$this->assertNotNull($constructedEvent);
$this->assertEquals('login', $constructedEvent->getName());
}
public function testValidateSuccess()
{
$this->event->setMethod($this->faker->word);
$this->assertTrue($this->event->validate());
}
protected function setUp(): void
{
parent::setUp();
$this->event = new LoginEvent();
}
}

View File

@@ -0,0 +1,61 @@
<?php
/**
* User: Damian Zamojski (br33f)
* Date: 25.06.2021
* Time: 13:40
*/
namespace Tests\Ga4\MeasurementProtocol\Dto\Event;
use Br33f\Ga4\MeasurementProtocol\Dto\Event\PurchaseEvent;
use Br33f\Ga4\MeasurementProtocol\Exception\ValidationException;
use Tests\Common\BaseTestCase;
class PurchaseEventTest extends BaseTestCase
{
/**
* @var PurchaseEvent
*/
protected $event;
public function testDefaultConstructor()
{
$constructedEvent = new PurchaseEvent();
$this->assertNotNull($constructedEvent);
$this->assertEquals('purchase', $constructedEvent->getName());
}
public function testValidateSuccess()
{
$this->event->setValue($this->faker->randomFloat(2, 10, 3000));
$this->event->setCurrency($this->faker->currencyCode);
$this->event->setTransactionId($this->faker->bothify('*#*#*#_transaction'));
$this->assertTrue($this->event->validate());
}
public function testValidateFailNoCurrency()
{
$this->event->setValue($this->faker->randomFloat(2, 10, 3000));
$this->event->setTransactionId($this->faker->bothify('*#*#*#_transaction'));
$this->expectException(ValidationException::class);
$this->event->validate();
}
public function testValidateFailNoTransaction()
{
$this->event->setValue($this->faker->randomFloat(2, 10, 3000));
$this->event->setCurrency($this->faker->currencyCode);
$this->expectException(ValidationException::class);
$this->event->validate();
}
protected function setUp(): void
{
parent::setUp();
$this->event = new PurchaseEvent();
}
}

View File

@@ -0,0 +1,61 @@
<?php
/**
* User: Damian Zamojski (br33f)
* Date: 25.06.2021
* Time: 13:40
*/
namespace Tests\Ga4\MeasurementProtocol\Dto\Event;
use Br33f\Ga4\MeasurementProtocol\Dto\Event\RefundEvent;
use Br33f\Ga4\MeasurementProtocol\Exception\ValidationException;
use Tests\Common\BaseTestCase;
class RefundEventTest extends BaseTestCase
{
/**
* @var RefundEvent
*/
protected $event;
public function testDefaultConstructor()
{
$constructedEvent = new RefundEvent();
$this->assertNotNull($constructedEvent);
$this->assertEquals('refund', $constructedEvent->getName());
}
public function testValidateSuccess()
{
$this->event->setValue($this->faker->randomFloat(2, 10, 3000));
$this->event->setCurrency($this->faker->currencyCode);
$this->event->setTransactionId($this->faker->bothify('*#*#*#_transaction'));
$this->assertTrue($this->event->validate());
}
public function testValidateFailNoCurrency()
{
$this->event->setValue($this->faker->randomFloat(2, 10, 3000));
$this->event->setTransactionId($this->faker->bothify('*#*#*#_transaction'));
$this->expectException(ValidationException::class);
$this->event->validate();
}
public function testValidateFailNoTransaction()
{
$this->event->setValue($this->faker->randomFloat(2, 10, 3000));
$this->event->setCurrency($this->faker->currencyCode);
$this->expectException(ValidationException::class);
$this->event->validate();
}
protected function setUp(): void
{
parent::setUp();
$this->event = new RefundEvent();
}
}

View File

@@ -0,0 +1,50 @@
<?php
/**
* User: Damian Zamojski (br33f)
* Date: 25.06.2021
* Time: 13:40
*/
namespace Tests\Ga4\MeasurementProtocol\Dto\Event;
use Br33f\Ga4\MeasurementProtocol\Dto\Event\RemoveFromCartEvent;
use Br33f\Ga4\MeasurementProtocol\Exception\ValidationException;
use Tests\Common\BaseTestCase;
class RemoveFromCartEventTest extends BaseTestCase
{
/**
* @var RemoveFromCartEvent
*/
protected $event;
public function testDefaultConstructor()
{
$constructedEvent = new RemoveFromCartEvent();
$this->assertNotNull($constructedEvent);
$this->assertEquals('remove_from_cart', $constructedEvent->getName());
}
public function testValidateSuccess()
{
$this->event->setValue($this->faker->randomFloat(2, 10, 3000));
$this->event->setCurrency($this->faker->currencyCode);
$this->assertTrue($this->event->validate());
}
public function testValidateFail()
{
$this->event->setValue($this->faker->randomFloat(2, 10, 3000));
$this->expectException(ValidationException::class);
$this->event->validate();
}
protected function setUp(): void
{
parent::setUp();
$this->event = new RemoveFromCartEvent();
}
}

View File

@@ -0,0 +1,49 @@
<?php
/**
* User: Damian Zamojski (br33f)
* Date: 25.06.2021
* Time: 13:40
*/
namespace Tests\Ga4\MeasurementProtocol\Dto\Event;
use Br33f\Ga4\MeasurementProtocol\Dto\Event\SearchEvent;
use Br33f\Ga4\MeasurementProtocol\Exception\ValidationException;
use Tests\Common\BaseTestCase;
class SearchEventTest extends BaseTestCase
{
/**
* @var SearchEvent
*/
protected $event;
public function testDefaultConstructor()
{
$constructedEvent = new SearchEvent();
$this->assertNotNull($constructedEvent);
$this->assertEquals('search', $constructedEvent->getName());
}
public function testValidateSuccess()
{
$this->event->setSearchTerm($this->faker->paragraph);
$this->assertTrue($this->event->validate());
}
public function testValidateFail()
{
$this->event = new SearchEvent();
$this->expectException(ValidationException::class);
$this->event->validate();
}
protected function setUp(): void
{
parent::setUp();
$this->event = new SearchEvent();
}
}

View File

@@ -0,0 +1,55 @@
<?php
/**
* User: Damian Zamojski (br33f)
* Date: 25.06.2021
* Time: 10:44
*/
namespace Tests\Ga4\MeasurementProtocol\Dto\Event;
use Br33f\Ga4\MeasurementProtocol\Dto\Event\SelectItemEvent;
use Br33f\Ga4\MeasurementProtocol\Dto\Event\ViewItemEvent;
use Br33f\Ga4\MeasurementProtocol\Dto\Parameter\BaseParameter;
use Tests\Common\BaseTestCase;
class SelectItemEventTest extends BaseTestCase
{
/**
* @var SelectItemEvent
*/
protected $event;
public function testDefaultConstructor()
{
$constructedEvent = new SelectItemEvent();
$this->assertNotNull($constructedEvent);
$this->assertEquals('select_item', $constructedEvent->getName());
}
public function testParameterConstructor()
{
$setParameters = [
$this->faker->word => new BaseParameter($this->faker->word),
$this->faker->word => new BaseParameter($this->faker->word)
];
$constructedEvent = new ViewItemEvent($setParameters);
$this->assertNotNull($constructedEvent);
$this->assertEquals($setParameters, $constructedEvent->getParamList());
}
public function testValidateSuccess()
{
$newViewItemEvent = new ViewItemEvent();
$this->assertTrue($newViewItemEvent->validate());
}
protected function setUp(): void
{
parent::setUp();
$this->event = new SelectItemEvent();
}
}

View File

@@ -0,0 +1,40 @@
<?php
/**
* User: Damian Zamojski (br33f)
* Date: 25.06.2021
* Time: 13:40
*/
namespace Tests\Ga4\MeasurementProtocol\Dto\Event;
use Br33f\Ga4\MeasurementProtocol\Dto\Event\SignUpEvent;
use Tests\Common\BaseTestCase;
class SignUpEventTest extends BaseTestCase
{
/**
* @var SignUpEvent
*/
protected $event;
public function testDefaultConstructor()
{
$constructedEvent = new SignUpEvent();
$this->assertNotNull($constructedEvent);
$this->assertEquals('sign_up', $constructedEvent->getName());
}
public function testValidateSuccess()
{
$this->event->setMethod($this->faker->word);
$this->assertTrue($this->event->validate());
}
protected function setUp(): void
{
parent::setUp();
$this->event = new SignUpEvent();
}
}

View File

@@ -0,0 +1,50 @@
<?php
/**
* User: Damian Zamojski (br33f)
* Date: 25.06.2021
* Time: 13:40
*/
namespace Tests\Ga4\MeasurementProtocol\Dto\Event;
use Br33f\Ga4\MeasurementProtocol\Dto\Event\ViewCartEvent;
use Br33f\Ga4\MeasurementProtocol\Exception\ValidationException;
use Tests\Common\BaseTestCase;
class ViewCartEventTest extends BaseTestCase
{
/**
* @var ViewCartEvent
*/
protected $event;
public function testDefaultConstructor()
{
$constructedEvent = new ViewCartEvent();
$this->assertNotNull($constructedEvent);
$this->assertEquals('view_cart', $constructedEvent->getName());
}
public function testValidateSuccess()
{
$this->event->setValue($this->faker->randomFloat(2, 10, 3000));
$this->event->setCurrency($this->faker->currencyCode);
$this->assertTrue($this->event->validate());
}
public function testValidateFail()
{
$this->event->setValue($this->faker->randomFloat(2, 10, 3000));
$this->expectException(ValidationException::class);
$this->event->validate();
}
protected function setUp(): void
{
parent::setUp();
$this->event = new ViewCartEvent();
}
}

View File

@@ -0,0 +1,109 @@
<?php
/**
* User: Damian Zamojski (br33f)
* Date: 25.06.2021
* Time: 10:44
*/
namespace Tests\Ga4\MeasurementProtocol\Dto\Event;
use Br33f\Ga4\MeasurementProtocol\Dto\Event\ViewItemEvent;
use Br33f\Ga4\MeasurementProtocol\Dto\Parameter\BaseParameter;
use Br33f\Ga4\MeasurementProtocol\Dto\Parameter\ItemCollectionParameter;
use Br33f\Ga4\MeasurementProtocol\Dto\Parameter\ItemParameter;
use Tests\Common\BaseTestCase;
class ViewItemEventTest extends BaseTestCase
{
protected $viewItemEvent;
public function testDefaultConstructor()
{
$constructedEvent = new ViewItemEvent();
$this->assertNotNull($constructedEvent);
}
public function testParameterConstructor()
{
$setParameters = [
$this->faker->word => new BaseParameter($this->faker->word),
$this->faker->word => new BaseParameter($this->faker->word)
];
$constructedEvent = new ViewItemEvent($setParameters);
$this->assertNotNull($constructedEvent);
$this->assertEquals($setParameters, $constructedEvent->getParamList());
}
public function testSetGetItems()
{
$setItems = new ItemCollectionParameter([
new ItemParameter(['item_name' => $this->faker->word]),
new ItemParameter(['item_name' => $this->faker->word, 'price' => $this->faker->randomFloat(2, 10, 100)])
]);
$this->viewItemEvent->setItems($setItems);
$this->assertEquals($setItems, $this->viewItemEvent->getItems());
}
public function testGetItemsEmpty()
{
$newViewItemEvent = new ViewItemEvent();
$this->assertNotNull($newViewItemEvent->getItems());
}
public function testAddItem()
{
$this->viewItemEvent->setItems(new ItemCollectionParameter());
$itemToAdd = new ItemParameter(['item_name' => $this->faker->word]);
$this->viewItemEvent->addItem($itemToAdd);
$this->assertEquals(1, count($this->viewItemEvent->getItems()->getItemList()));
$this->assertEquals($itemToAdd, $this->viewItemEvent->getItems()->getItemList()[0]);
}
public function testCurrency()
{
$setCurrency = $this->faker->currencyCode;
$this->viewItemEvent->setCurrency($setCurrency);
$this->assertEquals($setCurrency, $this->viewItemEvent->getCurrency());
}
public function testValue()
{
$setValue = $this->faker->randomFloat(2, 10, 2000);
$this->viewItemEvent->setValue($setValue);
$this->assertEquals($setValue, $this->viewItemEvent->getValue());
}
public function testValidateEmpty()
{
$newViewItemEvent = new ViewItemEvent();
$this->assertTrue($newViewItemEvent->validate());
}
public function testValidate()
{
$setItems = new ItemCollectionParameter([
new ItemParameter(['item_name' => $this->faker->word]),
new ItemParameter(['item_name' => $this->faker->word, 'price' => $this->faker->randomFloat(2, 10, 100)])
]);
$newViewItemEvent = new ViewItemEvent([$setItems]);
$this->assertTrue($newViewItemEvent->validate());
}
protected function setUp(): void
{
parent::setUp();
$this->viewItemEvent = new ViewItemEvent();
}
}

View File

@@ -0,0 +1,103 @@
<?php
/**
* User: Damian Zamojski (br33f)
* Date: 25.06.2021
* Time: 10:44
*/
namespace Tests\Ga4\MeasurementProtocol\Dto\Event;
use Br33f\Ga4\MeasurementProtocol\Dto\Event\ViewItemEvent;
use Br33f\Ga4\MeasurementProtocol\Dto\Event\ViewItemListEvent;
use Br33f\Ga4\MeasurementProtocol\Dto\Parameter\BaseParameter;
use Br33f\Ga4\MeasurementProtocol\Dto\Parameter\ItemCollectionParameter;
use Br33f\Ga4\MeasurementProtocol\Dto\Parameter\ItemParameter;
use Tests\Common\BaseTestCase;
class ViewItemListEventTest extends BaseTestCase
{
/**
* @var ViewItemEvent
*/
protected $event;
public function testDefaultConstructor()
{
$constructedEvent = new ViewItemListEvent();
$this->assertNotNull($constructedEvent);
$this->assertEquals('view_item_list', $constructedEvent->getName());
}
public function testParameterConstructor()
{
$setParameters = [
$this->faker->word => new BaseParameter($this->faker->word),
$this->faker->word => new BaseParameter($this->faker->word)
];
$constructedEvent = new ViewItemEvent($setParameters);
$this->assertNotNull($constructedEvent);
$this->assertEquals($setParameters, $constructedEvent->getParamList());
}
public function testSetGetItems()
{
$setItems = new ItemCollectionParameter([
new ItemParameter(['item_name' => $this->faker->word]),
new ItemParameter(['item_name' => $this->faker->word, 'price' => $this->faker->randomFloat(2, 10, 100)])
]);
$this->event->setItems($setItems);
$this->assertEquals($setItems, $this->event->getItems());
}
public function testGetItemsEmpty()
{
$newViewItemEvent = new ViewItemEvent();
$this->assertNotNull($newViewItemEvent->getItems());
}
public function testAddItem()
{
$this->event->setItems(new ItemCollectionParameter());
$itemToAdd = new ItemParameter(['item_name' => $this->faker->word]);
$this->event->addItem($itemToAdd);
$this->assertEquals(1, count($this->event->getItems()->getItemList()));
$this->assertEquals($itemToAdd, $this->event->getItems()->getItemList()[0]);
}
public function testCurrency()
{
$setCurrency = $this->faker->currencyCode;
$this->event->setCurrency($setCurrency);
$this->assertEquals($setCurrency, $this->event->getCurrency());
}
public function testValue()
{
$setValue = $this->faker->randomFloat(2, 10, 2000);
$this->event->setValue($setValue);
$this->assertEquals($setValue, $this->event->getValue());
}
public function testValidate()
{
$newViewItemEvent = new ViewItemEvent();
$this->assertTrue($newViewItemEvent->validate());
}
protected function setUp(): void
{
parent::setUp();
$this->event = new ViewItemListEvent();
}
}

View File

@@ -0,0 +1,40 @@
<?php
/**
* User: Damian Zamojski (br33f)
* Date: 25.06.2021
* Time: 13:40
*/
namespace Tests\Ga4\MeasurementProtocol\Dto\Event;
use Br33f\Ga4\MeasurementProtocol\Dto\Event\ViewSearchResultsEvent;
use Tests\Common\BaseTestCase;
class ViewSearchResultsEventTest extends BaseTestCase
{
/**
* @var ViewSearchResultsEvent
*/
protected $event;
public function testDefaultConstructor()
{
$constructedEvent = new ViewSearchResultsEvent();
$this->assertNotNull($constructedEvent);
$this->assertEquals('view_search_results', $constructedEvent->getName());
}
public function testValidateSuccess()
{
$this->event->setSearchTerm($this->faker->paragraph);
$this->assertTrue($this->event->validate());
}
protected function setUp(): void
{
parent::setUp();
$this->event = new ViewSearchResultsEvent();
}
}

View File

@@ -0,0 +1,69 @@
<?php
/**
* User: Damian Zamojski (br33f)
* Date: 22.06.2021
* Time: 14:22
*/
namespace Tests\Ga4\MeasurementProtocol\Dto\Parameter;
use Br33f\Ga4\MeasurementProtocol\Dto\Parameter\BaseParameter;
use Tests\Common\BaseTestCase;
class BaseParameterTest extends BaseTestCase
{
/**
* @var BaseParameter
*/
protected $baseParameter;
public function setUp(): void
{
$this->baseParameter = new BaseParameter();
}
public function testValueString()
{
$valueToSet = $this->faker->word;
$this->baseParameter->setValue($valueToSet);
$this->assertEquals($valueToSet, $this->baseParameter->getValue());
}
public function testValueArray()
{
$valueToSet = [];
for ($i = 0; $i < $this->faker->randomDigit; $i++) {
$valueToSet[] = $this->faker->word;
}
$this->baseParameter->setValue($valueToSet);
$this->assertEquals($valueToSet, $this->baseParameter->getValue());
}
public function testValueBaseParam()
{
$valueToSet = new BaseParameter();
$valueToSet->setValue($this->faker->word);
$this->baseParameter->setValue($valueToSet);
$this->assertEquals($valueToSet, $this->baseParameter->getValue());
}
public function testExportSimple()
{
$valueToSet = $this->faker->word;
$this->baseParameter->setValue($valueToSet);
$this->assertEquals($valueToSet, $this->baseParameter->export());
}
public function testExportBaseParameter()
{
$valueToSet = new BaseParameter();
$valueToSet->setValue($this->faker->word);
$this->baseParameter->setValue($valueToSet);
$this->assertEquals($valueToSet->getValue(), $this->baseParameter->export());
}
}

View File

@@ -0,0 +1,92 @@
<?php
/**
* User: Damian Zamojski (br33f)
* Date: 22.06.2021
* Time: 14:22
*/
namespace Tests\Ga4\MeasurementProtocol\Dto\Parameter;
use Br33f\Ga4\MeasurementProtocol\Dto\Parameter\ItemCollectionParameter;
use Br33f\Ga4\MeasurementProtocol\Dto\Parameter\ItemParameter;
use Br33f\Ga4\MeasurementProtocol\Exception\ValidationException;
use Tests\Common\BaseTestCase;
class ItemCollectionParameterTest extends BaseTestCase
{
/**
* @var ItemCollectionParameter
*/
protected $itemCollectionParameter;
public function setUp(): void
{
$this->itemCollectionParameter = new ItemCollectionParameter();
}
public function testDefaultContructor()
{
$constructedItemCollectionParameter = new ItemCollectionParameter();
$this->assertNotNull($constructedItemCollectionParameter);
}
public function testItemList()
{
$setItemList = [
new ItemParameter(['item_name' => 'test1']),
new ItemParameter(['item_id' => '123', 'price' => 15.55])
];
$this->itemCollectionParameter->setItemList($setItemList);
$this->assertEquals($setItemList, $this->itemCollectionParameter->getItemList());
}
public function testAddItem()
{
$itemToAdd = new ItemParameter(['item_name' => $this->faker->name, 'item_id' => '123', 'price' => 15.55]);
$this->itemCollectionParameter
->setItemList([])
->addItem($itemToAdd);
$this->assertEquals(1, count($this->itemCollectionParameter->getItemList()));
$this->assertEquals($itemToAdd, $this->itemCollectionParameter->getItemList()[0]);
}
public function testExportSimple()
{
$setItemList = [
new ItemParameter(['item_name' => 'test1']),
new ItemParameter(['item_id' => '123', 'price' => 15.55])
];
$this->itemCollectionParameter->setItemList($setItemList);
$this->assertEquals([
['item_name' => 'test1'],
['item_id' => '123', 'price' => 15.55]
], $this->itemCollectionParameter->export());
}
public function testValidateSuccess()
{
$setItemList = [
new ItemParameter(['item_name' => 'test1']),
new ItemParameter(['item_id' => '123', 'price' => 15.55])
];
$this->itemCollectionParameter->setItemList($setItemList);
$this->assertTrue($this->itemCollectionParameter->validate());
}
public function testValidateError()
{
$setItemList = [
new ItemParameter(['item_name' => 'test1']),
new ItemParameter(['price' => 15.55])
];
$this->itemCollectionParameter->setItemList($setItemList);
$this->expectException(ValidationException::class);
$this->itemCollectionParameter->validate();
}
}

View File

@@ -0,0 +1,265 @@
<?php
/**
* User: Damian Zamojski (br33f)
* Date: 25.06.2021
* Time: 13:03
*/
namespace Tests\Ga4\MeasurementProtocol\Dto\Parameter;
use Br33f\Ga4\MeasurementProtocol\Dto\Parameter\ItemParameter;
use Br33f\Ga4\MeasurementProtocol\Exception\ValidationException;
use Tests\Common\BaseTestCase;
class ItemParameterTest extends BaseTestCase
{
protected $itemParameter;
public function testDefaultConstructor()
{
$constructedItemParameter = new ItemParameter();
$this->assertNotNull($constructedItemParameter);
}
public function testParametrizedConstructor()
{
$blueprint = [
'item_id' => $this->faker->name,
'price' => $this->faker->randomFloat(2, 10, 1000),
'quantity' => $this->faker->numberBetween(1, 3)
];
$constructedItemParameter = new ItemParameter($blueprint);
$this->assertNotNull($constructedItemParameter);
$this->assertEquals($blueprint['item_id'], $constructedItemParameter->getItemId());
$this->assertEquals($blueprint['price'], $constructedItemParameter->getPrice());
$this->assertEquals($blueprint['quantity'], $constructedItemParameter->getQuantity());
}
public function testIndex()
{
$setIndex = $this->faker->numberBetween(1, 9);
$this->itemParameter->setIndex($setIndex);
$this->assertEquals($setIndex, $this->itemParameter->getIndex());
}
public function testAffiliation()
{
$setAffiliation = $this->faker->word;
$this->itemParameter->setAffiliation($setAffiliation);
$this->assertEquals($setAffiliation, $this->itemParameter->getAffiliation());
}
public function testCurrency()
{
$setCurrency = $this->faker->currencyCode;
$this->itemParameter->setCurrency($setCurrency);
$this->assertEquals($setCurrency, $this->itemParameter->getCurrency());
}
public function testQuantity()
{
$setQuantity = $this->faker->numberBetween(1, 30);
$this->itemParameter->setQuantity($setQuantity);
$this->assertEquals($setQuantity, $this->itemParameter->getQuantity());
}
public function testItemName()
{
$setName = $this->faker->word . $this->faker->colorName;
$this->itemParameter->setItemName($setName);
$this->assertEquals($setName, $this->itemParameter->getItemName());
}
public function testLocationId()
{
$setLocationId = $this->faker->bothify('*_#####');
$this->itemParameter->setLocationId($setLocationId);
$this->assertEquals($setLocationId, $this->itemParameter->getLocationId());
}
public function testItemCategory()
{
$setCategory = $this->faker->word;
$this->itemParameter->setItemCategory($setCategory);
$this->itemParameter->setItemCategory2($setCategory);
$this->itemParameter->setItemCategory3($setCategory);
$this->itemParameter->setItemCategory4($setCategory);
$this->itemParameter->setItemCategory5($setCategory);
$this->assertEquals($setCategory, $this->itemParameter->getItemCategory());
$this->assertEquals($setCategory, $this->itemParameter->getItemCategory2());
$this->assertEquals($setCategory, $this->itemParameter->getItemCategory3());
$this->assertEquals($setCategory, $this->itemParameter->getItemCategory4());
$this->assertEquals($setCategory, $this->itemParameter->getItemCategory5());
}
public function testPrice()
{
$setPrice = $this->faker->randomFloat(2, 1, 10000);
$this->itemParameter->setPrice($setPrice);
$this->assertEquals($setPrice, $this->itemParameter->getPrice());
}
public function testSetItemId()
{
$setItemId = $this->faker->bothify('***_#####');
$this->itemParameter->setItemId($setItemId);
$this->assertEquals($setItemId, $this->itemParameter->getItemId());
}
public function testItemListId()
{
$setItemListId = $this->faker->bothify('ITEM_LIST_#####');
$this->itemParameter->setItemListId($setItemListId);
$this->assertEquals($setItemListId, $this->itemParameter->getItemListId());
}
public function testItemListName()
{
$setItemListName = $this->faker->word;
$this->itemParameter->setItemListName($setItemListName);
$this->assertEquals($setItemListName, $this->itemParameter->getItemListName());
}
public function testCoupon()
{
$setCoupon = $this->faker->bothify('***-#####-#####');
$this->itemParameter->setCoupon($setCoupon);
$this->assertEquals($setCoupon, $this->itemParameter->getCoupon());
}
public function testDiscount()
{
$setDiscount = $this->faker->randomFloat(2, 1, 20);
$this->itemParameter->setDiscount($setDiscount);
$this->assertEquals($setDiscount, $this->itemParameter->getDiscount());
}
public function testItemVariant()
{
$setItemVariant = $this->faker->colorName;
$this->itemParameter->setItemVariant($setItemVariant);
$this->assertEquals($setItemVariant, $this->itemParameter->getItemVariant());
}
public function testSetItemBrand()
{
$setItemBrand = $this->faker->company;
$this->itemParameter->setItemBrand($setItemBrand);
$this->assertEquals($setItemBrand, $this->itemParameter->getItemBrand());
}
public function testValidateSuccess()
{
$validatedParameter = new ItemParameter(['item_name' => 'test1']);
$this->assertTrue($validatedParameter->validate());
}
public function testValidateError()
{
$validatedParameter = new ItemParameter(['price' => 15.55]);
$this->expectException(ValidationException::class);
$validatedParameter->validate();
}
public function testHydrate()
{
$blueprint = [
'item_id' => $this->faker->name,
'item_name' => $this->faker->name,
'affiliation' => $this->faker->name,
'coupon' => $this->faker->name,
'currency' => $this->faker->currencyCode,
'discount' => $this->faker->randomFloat(2, 10, 30),
'index' => $this->faker->numberBetween(1, 5),
'item_brand' => $this->faker->company,
'item_category' => $this->faker->name,
'item_category2' => $this->faker->name,
'item_category3' => $this->faker->name,
'item_category4' => $this->faker->name,
'item_category5' => $this->faker->name,
'item_list_id' => $this->faker->name,
'item_list_name' => $this->faker->name,
'item_variant' => $this->faker->name,
'location_id' => $this->faker->name,
'price' => $this->faker->randomFloat(2, 10, 1000),
'quantity' => $this->faker->numberBetween(1, 3)
];
$constructedItemParameter = new ItemParameter();
$constructedItemParameter->hydrate($blueprint);
$this->assertEquals($blueprint['item_id'], $constructedItemParameter->getItemId());
$this->assertEquals($blueprint['item_name'], $constructedItemParameter->getItemName());
$this->assertEquals($blueprint['affiliation'], $constructedItemParameter->getAffiliation());
$this->assertEquals($blueprint['coupon'], $constructedItemParameter->getCoupon());
$this->assertEquals($blueprint['currency'], $constructedItemParameter->getCurrency());
$this->assertEquals($blueprint['discount'], $constructedItemParameter->getDiscount());
$this->assertEquals($blueprint['index'], $constructedItemParameter->getIndex());
$this->assertEquals($blueprint['item_brand'], $constructedItemParameter->getItemBrand());
$this->assertEquals($blueprint['item_category'], $constructedItemParameter->getItemCategory());
$this->assertEquals($blueprint['item_category2'], $constructedItemParameter->getItemCategory2());
$this->assertEquals($blueprint['item_category3'], $constructedItemParameter->getItemCategory3());
$this->assertEquals($blueprint['item_category4'], $constructedItemParameter->getItemCategory4());
$this->assertEquals($blueprint['item_category5'], $constructedItemParameter->getItemCategory5());
$this->assertEquals($blueprint['item_list_id'], $constructedItemParameter->getItemListId());
$this->assertEquals($blueprint['item_list_name'], $constructedItemParameter->getItemListName());
$this->assertEquals($blueprint['item_variant'], $constructedItemParameter->getItemVariant());
$this->assertEquals($blueprint['location_id'], $constructedItemParameter->getLocationId());
$this->assertEquals($blueprint['price'], $constructedItemParameter->getPrice());
$this->assertEquals($blueprint['quantity'], $constructedItemParameter->getQuantity());
}
public function testExport()
{
$blueprint = [
'item_id' => $this->faker->name,
'item_name' => $this->faker->name,
'affiliation' => $this->faker->name,
'coupon' => $this->faker->name,
'currency' => $this->faker->currencyCode,
'discount' => $this->faker->randomFloat(2, 10, 30),
'index' => $this->faker->numberBetween(1, 5),
'item_brand' => $this->faker->company,
'item_category' => $this->faker->name,
'item_category2' => $this->faker->name,
'item_category3' => $this->faker->name,
'item_category4' => $this->faker->name,
'item_category5' => $this->faker->name,
'item_list_id' => $this->faker->name,
'item_list_name' => $this->faker->name,
'item_variant' => $this->faker->name,
'location_id' => $this->faker->name,
'price' => $this->faker->randomFloat(2, 10, 1000),
'quantity' => $this->faker->numberBetween(1, 3)
];
$constructedItemParameter = new ItemParameter($blueprint);
$this->assertEquals($blueprint, $constructedItemParameter->export());
}
protected function setUp(): void
{
parent::setUp();
$this->itemParameter = new ItemParameter();
}
}

View File

@@ -0,0 +1,208 @@
<?php
/**
* User: Damian Zamojski (br33f)
* Date: 22.06.2021
* Time: 16:06
*/
namespace Tests\Ga4\MeasurementProtocol\Dto\Request;
use Br33f\Ga4\MeasurementProtocol\Dto\Common\EventCollection;
use Br33f\Ga4\MeasurementProtocol\Dto\Common\UserProperties;
use Br33f\Ga4\MeasurementProtocol\Dto\Common\UserProperty;
use Br33f\Ga4\MeasurementProtocol\Dto\Event\BaseEvent;
use Br33f\Ga4\MeasurementProtocol\Dto\Parameter\BaseParameter;
use Br33f\Ga4\MeasurementProtocol\Dto\Request\BaseRequest;
use Br33f\Ga4\MeasurementProtocol\Enum\ErrorCode;
use Tests\Common\BaseTestCase;
class BaseRequestTest extends BaseTestCase
{
/**
* @var BaseRequest
*/
protected $baseRequest;
public function testDefaultConstructor()
{
$constructedBaseRequest = new BaseRequest();
$this->assertNotNull($constructedBaseRequest);
}
public function testAbstractEventConstructor()
{
$event = new BaseEvent();
$constructedBaseRequest = new BaseRequest(null, $event);
$this->assertNotNull($constructedBaseRequest);
$this->assertCount(1, $constructedBaseRequest->getEvents()->getEventList());
$this->assertEquals($event, $constructedBaseRequest->getEvents()->getEventList()[0]);
}
public function testClientId()
{
$setClientId = $this->faker->asciify('********************.********************');
$this->baseRequest->setClientId($setClientId);
$this->assertEquals($setClientId, $this->baseRequest->getClientId());
}
public function testUserId()
{
$setUserId = $this->faker->asciify('*********');
$this->baseRequest->setUserId($setUserId);
$this->assertEquals($setUserId, $this->baseRequest->getUserId());
}
public function testTimestampMicros()
{
$setTimestampMicros = $this->faker->unixTime * 1000;
$this->baseRequest->setTimestampMicros($setTimestampMicros);
$this->assertEquals($setTimestampMicros, $this->baseRequest->getTimestampMicros());
}
public function testNonPersonalizedAds()
{
$this->baseRequest->setNonPersonalizedAds(true);
$this->assertEquals(true, $this->baseRequest->isNonPersonalizedAds());
$this->baseRequest->setNonPersonalizedAds(false);
$this->assertEquals(false, $this->baseRequest->isNonPersonalizedAds());
}
public function testUserProperties()
{
$setUserProperties = new UserProperties();
$this->baseRequest->setUserProperties($setUserProperties);
$this->assertEquals($setUserProperties, $this->baseRequest->getUserProperties());
}
public function testAddUserProperty()
{
$addUserProperty = new UserProperty($this->faker->word, $this->faker->word);
$this->baseRequest->addUserProperty($addUserProperty);
$this->assertEquals(1, count($this->baseRequest->getUserProperties()->getUserPropertiesList()));
$this->assertEquals($addUserProperty, $this->baseRequest->getUserProperties()->getUserPropertiesList()[0]);
}
public function testEvents()
{
$setEvents = new EventCollection();
$event = new BaseEvent($this->faker->word);
$event->addParam($this->faker->word, new BaseParameter($this->faker->word));
$setEvents->addEvent($event);
$this->baseRequest->setEvents($setEvents);
$this->assertEquals($setEvents, $this->baseRequest->getEvents());
}
public function testAddEvent()
{
$this->baseRequest->setEvents(new EventCollection());
$event = new BaseEvent($this->faker->word);
$event->addParam($this->faker->word, new BaseParameter($this->faker->word));
$this->baseRequest->addEvent($event);
$this->assertEquals(1, count($this->baseRequest->getEvents()->getEventList()));
$this->assertEquals($event, $this->baseRequest->getEvents()->getEventList()[0]);
}
public function testValidateClientIdRequiredFailed()
{
$newBaseRequest = new BaseRequest();
$this->expectExceptionCode(ErrorCode::VALIDATION_CLIENT_ID_REQUIRED);
$newBaseRequest->validate('web');
}
public function testValidateAppInstanceIdRequiredFailed()
{
$newBaseRequest = new BaseRequest();
$this->expectExceptionCode(ErrorCode::VALIDATION_APP_INSTANCE_ID_REQUIRED);
$newBaseRequest->validate('firebase');
}
public function testValidateBothClientIdAppInstanceIdRequiredFailed()
{
$newBaseRequest = new BaseRequest();
$setAppInstanceId = $this->faker->bothify('**-########');
$setFirebaseId = $this->faker->bothify('**-########');
$newBaseRequest->setAppInstanceId($setAppInstanceId);
$newBaseRequest->setClientId($setFirebaseId);
$this->expectExceptionCode(ErrorCode::VALIDATION_CLIENT_IDENTIFIER_MISCONFIGURED);
$newBaseRequest->validate();
}
public function testValidateSuccess()
{
$setClientId = $this->faker->asciify('********************.********************');
$setEventCollection = new EventCollection();
$event = new BaseEvent($this->faker->word);
$event->addParam($this->faker->word, new BaseParameter($this->faker->word));
$setEventCollection->addEvent($event);
$newBaseRequest = new BaseRequest($setClientId, $setEventCollection);
$this->assertTrue($newBaseRequest->validate());
}
public function testExportOnlyRequiredParameters()
{
$setClientId = $this->faker->asciify('********************.********************');
$setEventCollection = new EventCollection();
$event = new BaseEvent($this->faker->word);
$event->addParam($this->faker->word, new BaseParameter($this->faker->word));
$setEventCollection->addEvent($event);
$exportBaseRequest = new BaseRequest($setClientId, $setEventCollection);
$this->assertEquals([
'client_id' => $setClientId,
'events' => $setEventCollection->export(),
], $exportBaseRequest->export());
}
public function testExportAllParameters()
{
$setClientId = $this->faker->asciify('********************.********************');
$setEventCollection = new EventCollection();
$event = new BaseEvent($this->faker->word);
$event->addParam($this->faker->word, new BaseParameter($this->faker->word));
$setEventCollection->addEvent($event);
$exportBaseRequest = new BaseRequest($setClientId, $setEventCollection);
$setUserId = $this->faker->asciify('************');
$exportBaseRequest->setUserId($setUserId);
$setTimestampMicros = $this->faker->unixTime * 1000;
$exportBaseRequest->setTimestampMicros($setTimestampMicros);
$setUserProperties = new UserProperties();
$exportBaseRequest->setUserProperties($setUserProperties);
$this->assertEquals([
'client_id' => $setClientId,
'events' => $setEventCollection->export(),
'user_id' => $setUserId,
'timestamp_micros' => $setTimestampMicros,
'user_properties' => $setUserProperties->export()
], $exportBaseRequest->export());
}
protected function setUp(): void
{
parent::setUp();
$this->baseRequest = new BaseRequest();
}
}

View File

@@ -0,0 +1,68 @@
<?php
/**
* User: Damian Zamojski (br33f)
* Date: 24.06.2021
* Time: 14:25
*/
namespace Tests\Ga4\MeasurementProtocol\Dto\Response;
use Br33f\Ga4\MeasurementProtocol\Dto\Response\BaseResponse;
use GuzzleHttp7\Psr7\Response;
use Tests\Common\BaseTestCase;
class BaseResponseTest extends BaseTestCase
{
/**
* @var BaseResponse
*/
protected $baseResponse;
public function testDefaultConstructor()
{
$constructedBaseResponse = new BaseResponse();
$this->assertNotNull($constructedBaseResponse);
}
public function testBlueprintConstructor()
{
$response = new Response(200, [], '{"test_field": {"value": "123"}}');
$constructedBaseResponse = new BaseResponse($response);
$this->assertNotNull($constructedBaseResponse);
$this->assertEquals(200, $constructedBaseResponse->getStatusCode());
$this->assertEquals('{"test_field": {"value": "123"}}', $constructedBaseResponse->getBody());
}
public function testStatusCode()
{
$setStatusCode = 204;
$this->baseResponse->setStatusCode($setStatusCode);
$this->assertEquals(204, $this->baseResponse->getStatusCode());
}
public function testData()
{
$setBody = '{"test_field": {"value": "321"}}';
$this->baseResponse->setBody($setBody);
$this->assertEquals($setBody, $this->baseResponse->getBody());
$this->assertEquals(json_decode($setBody, true), $this->baseResponse->getData());
}
public function testBody()
{
$setBody = '{"test_field": {"value": "321"}}';
$this->baseResponse->setBody($setBody);
$this->assertEquals($setBody, $this->baseResponse->getBody());
}
protected function setUp(): void
{
parent::setUp();
$this->baseResponse = new BaseResponse();
}
}

View File

@@ -0,0 +1,66 @@
<?php
/**
* User: Damian Zamojski (br33f)
* Date: 24.06.2021
* Time: 14:25
*/
namespace Tests\Ga4\MeasurementProtocol\Dto\Response;
use Br33f\Ga4\MeasurementProtocol\Dto\Common\ValidationMessage;
use Br33f\Ga4\MeasurementProtocol\Dto\Response\DebugResponse;
use GuzzleHttp7\Psr7\Response;
use Tests\Common\BaseTestCase;
class DebugResponseTest extends BaseTestCase
{
/**
* @var DebugResponse
*/
protected $debugResponse;
public function testDefaultConstructor()
{
$constructedDebugResponse = new DebugResponse();
$this->assertNotNull($constructedDebugResponse);
}
public function testBlueprintConstructor()
{
$response = new Response(200, [], '{
"validationMessages": [
{
"description": "Unable to parse Measurement Protocol JSON payload. (events[0]) names: Cannot find field.",
"validationCode": "VALUE_INVALID"
}
]
}');
$parsedValidationMessage = new ValidationMessage(json_decode('{
"description": "Unable to parse Measurement Protocol JSON payload. (events[0]) names: Cannot find field.",
"validationCode": "VALUE_INVALID"
}', true));
$constructedDebugResponse = new DebugResponse($response);
$this->assertNotNull($constructedDebugResponse);
$this->assertEquals(200, $constructedDebugResponse->getStatusCode());
$this->assertEquals(1, count($constructedDebugResponse->getValidationMessages()));
$this->assertEquals($parsedValidationMessage, $constructedDebugResponse->getValidationMessages()[0]);
}
public function testBody()
{
$setValidationMessages = [new ValidationMessage(['fieldPath' => 'test123'])];
$this->debugResponse->setValidationMessages($setValidationMessages);
$this->assertEquals($setValidationMessages, $this->debugResponse->getValidationMessages());
}
protected function setUp(): void
{
parent::setUp();
$this->debugResponse = new DebugResponse();
}
}

View File

@@ -0,0 +1,24 @@
<?php
/**
* User: Damian Zamojski (br33f)
* Date: 23.06.2021
* Time: 12:17
*/
namespace Tests\Ga4\MeasurementProtocol\Exception;
use Br33f\Ga4\MeasurementProtocol\Exception\HydrationException;
use Tests\Common\BaseTestCase;
class HydrationExceptionTest extends BaseTestCase
{
public function test__construct()
{
$setMessage = $this->faker->word;
$setCode = $this->faker->numerify('#######');
$constructedValidationException = new HydrationException($setMessage, $setCode);
$this->assertEquals($setMessage, $constructedValidationException->getMessage());
$this->assertEquals($setCode, $constructedValidationException->getCode());
}
}

View File

@@ -0,0 +1,37 @@
<?php
/**
* User: Damian Zamojski (br33f)
* Date: 23.06.2021
* Time: 12:17
*/
namespace Tests\Ga4\MeasurementProtocol\Exception;
use Br33f\Ga4\MeasurementProtocol\Exception\ValidationException;
use Tests\Common\BaseTestCase;
class ValidationExceptionTest extends BaseTestCase
{
public function test__construct()
{
$setMessage = $this->faker->word;
$setCode = $this->faker->numerify('#######');
$setFieldName = $this->faker->word;
$constructedValidationException = new ValidationException($setMessage, $setCode, $setFieldName);
$this->assertEquals($setMessage, $constructedValidationException->getMessage());
$this->assertEquals($setCode, $constructedValidationException->getCode());
$this->assertEquals($setFieldName, $constructedValidationException->getFieldName());
}
public function testFieldName()
{
$setFieldName = $this->faker->word;
$constructedValidationException = new ValidationException();
$constructedValidationException->setFieldName($setFieldName);
$this->assertEquals($setFieldName, $constructedValidationException->getFieldName());
}
}

View File

@@ -0,0 +1,75 @@
<?php
/**
* User: Damian Zamojski (br33f)
* Date: 23.06.2021
* Time: 12:23
*/
namespace Tests\Ga4\MeasurementProtocol;
use Br33f\Ga4\MeasurementProtocol\HttpClient;
use GuzzleHttp7\Client;
use GuzzleHttp7\Handler\MockHandler;
use GuzzleHttp7\HandlerStack;
use GuzzleHttp7\Psr7\Response;
use Tests\Common\BaseTestCase;
class HttpClientTest extends BaseTestCase
{
/**
* @var HttpClient
*/
protected $httpClient;
public function testDefaultConstructor()
{
$constructedHttpClient = new HttpClient();
$this->assertNotNull($constructedHttpClient);
}
public function testClient()
{
$setClient = new Client();
$this->httpClient->setClient($setClient);
$this->assertEquals($setClient, $this->httpClient->getClient());
}
public function testGetClientWhenNotSet()
{
$emptyHttpClient = new HttpClient();
$this->assertNotNull($emptyHttpClient->getClient());
}
public function testPost()
{
$mock = new MockHandler([
new Response(204),
new Response(403),
new Response(500)
]);
$handlerStack = HandlerStack::create($mock);
$mockClient = new Client(['handler' => $handlerStack]);
$newHttpClient = new HttpClient();
$newHttpClient->setClient($mockClient);
$response = $newHttpClient->post($this->faker->url, []);
$this->assertEquals(204, $response->getStatusCode());
$response = $newHttpClient->post($this->faker->url, []);
$this->assertEquals(403, $response->getStatusCode());
$response = $newHttpClient->post($this->faker->url, []);
$this->assertEquals(500, $response->getStatusCode());
}
protected function setUp(): void
{
parent::setUp();
$this->httpClient = new HttpClient();
}
}

View File

@@ -0,0 +1,264 @@
<?php
/**
* User: Damian Zamojski (br33f)
* Date: 23.06.2021
* Time: 14:42
*/
namespace Tests\Ga4\MeasurementProtocol;
use Br33f\Ga4\MeasurementProtocol\Dto\Event\BaseEvent;
use Br33f\Ga4\MeasurementProtocol\Dto\Request\BaseRequest;
use Br33f\Ga4\MeasurementProtocol\Dto\Response\AbstractResponse;
use Br33f\Ga4\MeasurementProtocol\Dto\Response\DebugResponse;
use Br33f\Ga4\MeasurementProtocol\Exception\MisconfigurationException;
use Br33f\Ga4\MeasurementProtocol\HttpClient;
use Br33f\Ga4\MeasurementProtocol\Service;
use GuzzleHttp7\Client;
use GuzzleHttp7\Handler\MockHandler;
use GuzzleHttp7\HandlerStack;
use GuzzleHttp7\Psr7\Response;
use Tests\Common\BaseTestCase;
class ServiceTest extends BaseTestCase
{
/**
* @var Service
*/
protected $service;
public function testConstructor()
{
$setApiSecret = $this->faker->word;
$setMeasurementId = $this->faker->bothify('**-########');
$constructedService = new Service($setApiSecret, $setMeasurementId);
$this->assertEquals($setApiSecret, $constructedService->getApiSecret());
$this->assertEquals($setMeasurementId, $constructedService->getMeasurementId());
}
public function testUseSsl()
{
$this->service->setUseSsl(true);
$this->assertTrue($this->service->isUseSsl());
$this->service->setUseSsl(false);
$this->assertFalse($this->service->isUseSsl());
}
public function testUsewww()
{
$this->service->setUseWww(true);
$this->assertTrue($this->service->isUseWww());
$this->service->setUseWww(false);
$this->assertFalse($this->service->isUseWww());
}
public function testCollectEndpoint()
{
$setCollectEndpoint = str_replace('https://', '', $this->faker->url);
$setCollectEndpoint = str_replace('http://', '', $setCollectEndpoint);
$this->service->setCollectEndpoint($setCollectEndpoint);
$this->assertEquals($setCollectEndpoint, $this->service->getCollectEndpoint());
}
public function testCollectDebugEndpoint()
{
$setCollectDebugEndpoint = str_replace('https://', '', $this->faker->url);
$setCollectDebugEndpoint = str_replace('http://', '', $setCollectDebugEndpoint);
$this->service->setCollectDebugEndpoint($setCollectDebugEndpoint);
$this->assertEquals($setCollectDebugEndpoint, $this->service->getCollectDebugEndpoint());
}
public function testMeasurementId()
{
$setMeasurementId = $this->faker->bothify('**-########');
$this->service->setMeasurementId($setMeasurementId);
$this->assertEquals($setMeasurementId, $this->service->getMeasurementId());
}
public function testSetApiSecret()
{
$setApiSecret = $this->faker->word;
$this->service->setApiSecret($setApiSecret);
$this->assertEquals($setApiSecret, $this->service->getApiSecret());
}
public function testHttpClient()
{
$setHttpClient = new HttpClient();
$this->service->setHttpClient($setHttpClient);
$this->assertEquals($setHttpClient, $this->service->getHttpClient());
}
public function testHttpClientWhenEmpty()
{
$setApiSecret = $this->faker->word;
$setMeasurementId = $this->faker->bothify('**-########');
$constructedService = new Service($setApiSecret, $setMeasurementId);
$this->assertNotNull($constructedService->getHttpClient());
}
public function testEndpoint()
{
$setApiSecret = $this->faker->word;
$setMeasurementId = $this->faker->bothify('**-########');
$newService = new Service($setApiSecret, $setMeasurementId);
$setCollectEndpoint = str_replace('https://', '', $this->faker->url);
$setCollectEndpoint = str_replace('http://', '', $setCollectEndpoint);
$newService->setCollectEndpoint($setCollectEndpoint);
$newService->setUseSsl(true);
$getParams = '?' . http_build_query(['api_secret' => $newService->getApiSecret(), 'measurement_id' => $newService->getMeasurementId()]);
$this->assertEquals(Service::SSL_SCHEME . $setCollectEndpoint . $getParams, $newService->getEndpoint());
$newService->setUseSsl(false);
$this->assertEquals(Service::NOT_SSL_SCHEME . $setCollectEndpoint . $getParams, $newService->getEndpoint());
$setCollectDebugEndpoint = str_replace('https://', '', $this->faker->url);
$setCollectDebugEndpoint = str_replace('http://', '', $setCollectDebugEndpoint);
$newService->setCollectDebugEndpoint($setCollectDebugEndpoint);
$newService->setUseSsl(true);
$this->assertEquals(Service::SSL_SCHEME . $setCollectDebugEndpoint . $getParams, $newService->getEndpoint(true));
$newService->setUseSsl(false);
$this->assertEquals(Service::NOT_SSL_SCHEME . $setCollectDebugEndpoint . $getParams, $newService->getEndpoint(true));
}
public function testOptions()
{
$setOptions = [
'timeout' => '25',
'headers' => [
'User-Agent' => 'Test User Agent'
]
];
$this->service->setOptions($setOptions);
$this->assertEquals($setOptions, $this->service->getOptions());
}
public function testSend()
{
$mock = new MockHandler([
new Response(200)
]);
$handlerStack = HandlerStack::create($mock);
$mockClient = new Client(['handler' => $handlerStack]);
$setApiSecret = $this->faker->word;
$setMeasurementId = $this->faker->bothify('**-########');
$sendService = new Service($setApiSecret, $setMeasurementId);
$sendService->getHttpClient()->setClient($mockClient);
$setClientId = $this->faker->asciify('********************.********************');
$sentRequest = new BaseRequest($setClientId);
$event = new BaseEvent($this->faker->word);
$sentRequest->addEvent($event);
$baseResponse = $sendService->send($sentRequest);
$this->assertTrue($baseResponse instanceof AbstractResponse);
$this->assertEquals(200, $baseResponse->getStatusCode());
}
public function testSendWithIpOverride()
{
$mock = new MockHandler([
new Response(200)
]);
$handlerStack = HandlerStack::create($mock);
$mockClient = new Client(['handler' => $handlerStack]);
$setApiSecret = $this->faker->word;
$setMeasurementId = $this->faker->bothify('**-########');
$sendService = new Service($setApiSecret, $setMeasurementId);
$sendService->getHttpClient()->setClient($mockClient);
$sendService->setIpOverride($this->faker->ipv4);
$setClientId = $this->faker->asciify('********************.********************');
$sentRequest = new BaseRequest($setClientId);
$event = new BaseEvent($this->faker->word);
$sentRequest->addEvent($event);
$baseResponse = $sendService->send($sentRequest);
$this->assertTrue($baseResponse instanceof AbstractResponse);
$this->assertEquals(200, $baseResponse->getStatusCode());
}
public function testSendDebugWithError()
{
$mock = new MockHandler([
new Response(200, [], '{
"validationMessages": [
{
"description": "Unable to parse Measurement Protocol JSON payload. (events[0]) names: Cannot find field.",
"validationCode": "VALUE_INVALID"
}
]
}')
]);
$handlerStack = HandlerStack::create($mock);
$mockClient = new Client(['handler' => $handlerStack]);
$setApiSecret = $this->faker->word;
$setMeasurementId = $this->faker->bothify('**-########');
$sendService = new Service($setApiSecret, $setMeasurementId);
$sendService->getHttpClient()->setClient($mockClient);
$setClientId = $this->faker->asciify('********************.********************');
$sentRequest = new BaseRequest($setClientId);
$event = new BaseEvent($this->faker->word);
$sentRequest->addEvent($event);
$debugResponse = $sendService->sendDebug($sentRequest);
$this->assertTrue($debugResponse instanceof DebugResponse);
$this->assertEquals(200, $debugResponse->getStatusCode());
$this->assertEquals(1, count($debugResponse->getValidationMessages()));
}
public function testMisconfigBothInGetQueryParameters()
{
$setApiSecret = $this->faker->word;
$setMeasurementId = $this->faker->bothify('**-########');
$setFirebaseId = $this->faker->bothify('**-########');
$testService = new Service($setApiSecret, $setMeasurementId);
$testService->setFirebaseId($setFirebaseId);
$this->expectException(MisconfigurationException::class);
$testService->getQueryParameters();
}
public function testMisconfigApiSecretEmptyInGetQueryParameters()
{
$setApiSecret = $this->faker->word;
$setMeasurementId = $this->faker->bothify('**-########');
$setFirebaseId = $this->faker->bothify('**-########');
$testService = new Service($setApiSecret, $setMeasurementId);
$testService->setFirebaseId($setFirebaseId);
$this->expectException(MisconfigurationException::class);
$testService->getQueryParameters();
}
protected function setUp(): void
{
parent::setUp();
$this->service = new Service($this->faker->word, $this->faker->bothify('**-########'));
}
}

View File

@@ -0,0 +1,445 @@
<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer\Autoload;
/**
* ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
*
* $loader = new \Composer\Autoload\ClassLoader();
*
* // register classes with namespaces
* $loader->add('Symfony\Component', __DIR__.'/component');
* $loader->add('Symfony', __DIR__.'/framework');
*
* // activate the autoloader
* $loader->register();
*
* // to enable searching the include path (eg. for PEAR packages)
* $loader->setUseIncludePath(true);
*
* In this example, if you try to use a class in the Symfony\Component
* namespace or one of its children (Symfony\Component\Console for instance),
* the autoloader will first look for the class under the component/
* directory, and it will then fallback to the framework/ directory if not
* found before giving up.
*
* This class is loosely based on the Symfony UniversalClassLoader.
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Jordi Boggiano <j.boggiano@seld.be>
* @see http://www.php-fig.org/psr/psr-0/
* @see http://www.php-fig.org/psr/psr-4/
*/
class ClassLoader
{
// PSR-4
private $prefixLengthsPsr4 = array();
private $prefixDirsPsr4 = array();
private $fallbackDirsPsr4 = array();
// PSR-0
private $prefixesPsr0 = array();
private $fallbackDirsPsr0 = array();
private $useIncludePath = false;
private $classMap = array();
private $classMapAuthoritative = false;
private $missingClasses = array();
private $apcuPrefix;
public function getPrefixes()
{
if (!empty($this->prefixesPsr0)) {
return call_user_func_array('array_merge', $this->prefixesPsr0);
}
return array();
}
public function getPrefixesPsr4()
{
return $this->prefixDirsPsr4;
}
public function getFallbackDirs()
{
return $this->fallbackDirsPsr0;
}
public function getFallbackDirsPsr4()
{
return $this->fallbackDirsPsr4;
}
public function getClassMap()
{
return $this->classMap;
}
/**
* @param array $classMap Class to filename map
*/
public function addClassMap(array $classMap)
{
if ($this->classMap) {
$this->classMap = array_merge($this->classMap, $classMap);
} else {
$this->classMap = $classMap;
}
}
/**
* Registers a set of PSR-0 directories for a given prefix, either
* appending or prepending to the ones previously set for this prefix.
*
* @param string $prefix The prefix
* @param array|string $paths The PSR-0 root directories
* @param bool $prepend Whether to prepend the directories
*/
public function add($prefix, $paths, $prepend = false)
{
if (!$prefix) {
if ($prepend) {
$this->fallbackDirsPsr0 = array_merge(
(array) $paths,
$this->fallbackDirsPsr0
);
} else {
$this->fallbackDirsPsr0 = array_merge(
$this->fallbackDirsPsr0,
(array) $paths
);
}
return;
}
$first = $prefix[0];
if (!isset($this->prefixesPsr0[$first][$prefix])) {
$this->prefixesPsr0[$first][$prefix] = (array) $paths;
return;
}
if ($prepend) {
$this->prefixesPsr0[$first][$prefix] = array_merge(
(array) $paths,
$this->prefixesPsr0[$first][$prefix]
);
} else {
$this->prefixesPsr0[$first][$prefix] = array_merge(
$this->prefixesPsr0[$first][$prefix],
(array) $paths
);
}
}
/**
* Registers a set of PSR-4 directories for a given namespace, either
* appending or prepending to the ones previously set for this namespace.
*
* @param string $prefix The prefix/namespace, with trailing '\\'
* @param array|string $paths The PSR-4 base directories
* @param bool $prepend Whether to prepend the directories
*
* @throws \InvalidArgumentException
*/
public function addPsr4($prefix, $paths, $prepend = false)
{
if (!$prefix) {
// Register directories for the root namespace.
if ($prepend) {
$this->fallbackDirsPsr4 = array_merge(
(array) $paths,
$this->fallbackDirsPsr4
);
} else {
$this->fallbackDirsPsr4 = array_merge(
$this->fallbackDirsPsr4,
(array) $paths
);
}
} elseif (!isset($this->prefixDirsPsr4[$prefix])) {
// Register directories for a new namespace.
$length = strlen($prefix);
if ('\\' !== $prefix[$length - 1]) {
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
}
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
$this->prefixDirsPsr4[$prefix] = (array) $paths;
} elseif ($prepend) {
// Prepend directories for an already registered namespace.
$this->prefixDirsPsr4[$prefix] = array_merge(
(array) $paths,
$this->prefixDirsPsr4[$prefix]
);
} else {
// Append directories for an already registered namespace.
$this->prefixDirsPsr4[$prefix] = array_merge(
$this->prefixDirsPsr4[$prefix],
(array) $paths
);
}
}
/**
* Registers a set of PSR-0 directories for a given prefix,
* replacing any others previously set for this prefix.
*
* @param string $prefix The prefix
* @param array|string $paths The PSR-0 base directories
*/
public function set($prefix, $paths)
{
if (!$prefix) {
$this->fallbackDirsPsr0 = (array) $paths;
} else {
$this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
}
}
/**
* Registers a set of PSR-4 directories for a given namespace,
* replacing any others previously set for this namespace.
*
* @param string $prefix The prefix/namespace, with trailing '\\'
* @param array|string $paths The PSR-4 base directories
*
* @throws \InvalidArgumentException
*/
public function setPsr4($prefix, $paths)
{
if (!$prefix) {
$this->fallbackDirsPsr4 = (array) $paths;
} else {
$length = strlen($prefix);
if ('\\' !== $prefix[$length - 1]) {
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
}
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
$this->prefixDirsPsr4[$prefix] = (array) $paths;
}
}
/**
* Turns on searching the include path for class files.
*
* @param bool $useIncludePath
*/
public function setUseIncludePath($useIncludePath)
{
$this->useIncludePath = $useIncludePath;
}
/**
* Can be used to check if the autoloader uses the include path to check
* for classes.
*
* @return bool
*/
public function getUseIncludePath()
{
return $this->useIncludePath;
}
/**
* Turns off searching the prefix and fallback directories for classes
* that have not been registered with the class map.
*
* @param bool $classMapAuthoritative
*/
public function setClassMapAuthoritative($classMapAuthoritative)
{
$this->classMapAuthoritative = $classMapAuthoritative;
}
/**
* Should class lookup fail if not found in the current class map?
*
* @return bool
*/
public function isClassMapAuthoritative()
{
return $this->classMapAuthoritative;
}
/**
* APCu prefix to use to cache found/not-found classes, if the extension is enabled.
*
* @param string|null $apcuPrefix
*/
public function setApcuPrefix($apcuPrefix)
{
$this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null;
}
/**
* The APCu prefix in use, or null if APCu caching is not enabled.
*
* @return string|null
*/
public function getApcuPrefix()
{
return $this->apcuPrefix;
}
/**
* Registers this instance as an autoloader.
*
* @param bool $prepend Whether to prepend the autoloader or not
*/
public function register($prepend = false)
{
spl_autoload_register(array($this, 'loadClass'), true, $prepend);
}
/**
* Unregisters this instance as an autoloader.
*/
public function unregister()
{
spl_autoload_unregister(array($this, 'loadClass'));
}
/**
* Loads the given class or interface.
*
* @param string $class The name of the class
* @return bool|null True if loaded, null otherwise
*/
public function loadClass($class)
{
if ($file = $this->findFile($class)) {
includeFile($file);
return true;
}
}
/**
* Finds the path to the file where the class is defined.
*
* @param string $class The name of the class
*
* @return string|false The path if found, false otherwise
*/
public function findFile($class)
{
// class map lookup
if (isset($this->classMap[$class])) {
return $this->classMap[$class];
}
if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
return false;
}
if (null !== $this->apcuPrefix) {
$file = apcu_fetch($this->apcuPrefix.$class, $hit);
if ($hit) {
return $file;
}
}
$file = $this->findFileWithExtension($class, '.php');
// Search for Hack files if we are running on HHVM
if (false === $file && defined('HHVM_VERSION')) {
$file = $this->findFileWithExtension($class, '.hh');
}
if (null !== $this->apcuPrefix) {
apcu_add($this->apcuPrefix.$class, $file);
}
if (false === $file) {
// Remember that this class does not exist.
$this->missingClasses[$class] = true;
}
return $file;
}
private function findFileWithExtension($class, $ext)
{
// PSR-4 lookup
$logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
$first = $class[0];
if (isset($this->prefixLengthsPsr4[$first])) {
$subPath = $class;
while (false !== $lastPos = strrpos($subPath, '\\')) {
$subPath = substr($subPath, 0, $lastPos);
$search = $subPath . '\\';
if (isset($this->prefixDirsPsr4[$search])) {
$pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1);
foreach ($this->prefixDirsPsr4[$search] as $dir) {
if (file_exists($file = $dir . $pathEnd)) {
return $file;
}
}
}
}
}
// PSR-4 fallback dirs
foreach ($this->fallbackDirsPsr4 as $dir) {
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
return $file;
}
}
// PSR-0 lookup
if (false !== $pos = strrpos($class, '\\')) {
// namespaced class name
$logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
. strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
} else {
// PEAR-like class name
$logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
}
if (isset($this->prefixesPsr0[$first])) {
foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
if (0 === strpos($class, $prefix)) {
foreach ($dirs as $dir) {
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
return $file;
}
}
}
}
}
// PSR-0 fallback dirs
foreach ($this->fallbackDirsPsr0 as $dir) {
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
return $file;
}
}
// PSR-0 include paths.
if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
return $file;
}
return false;
}
}
/**
* Scope isolated include.
*
* Prevents access to $this/self from included files.
*/
function includeFile($file)
{
include $file;
}

View File

@@ -0,0 +1,56 @@
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: Composer
Upstream-Contact: Jordi Boggiano <j.boggiano@seld.be>
Source: https://github.com/composer/composer
Files: *
Copyright: 2016, Nils Adermann <naderman@naderman.de>
2016, Jordi Boggiano <j.boggiano@seld.be>
License: Expat
Files: src/Composer/Util/TlsHelper.php
Copyright: 2016, Nils Adermann <naderman@naderman.de>
2016, Jordi Boggiano <j.boggiano@seld.be>
2013, Evan Coury <me@evancoury.com>
License: Expat and BSD-2-Clause
License: BSD-2-Clause
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
.
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
License: Expat
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is furnished
to do so, subject to the following conditions:
.
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@@ -0,0 +1,9 @@
<?php
// autoload_classmap.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
);

View File

@@ -0,0 +1,14 @@
<?php
// autoload_files.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
'7b11c4dc42b3b3023073cb14e519683c' => $vendorDir . '/ralouphie/getallheaders/src/getallheaders.php',
'6e3fae29631ef280660b3cdad06f25a8' => $vendorDir . '/symfony/deprecation-contracts/function.php',
'37a3dc5111fe8f707ab4c132ef1dbc62' => $vendorDir . '/guzzlehttp/guzzle/src/functions_include.php',
'0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => $vendorDir . '/symfony/polyfill-mbstring/bootstrap.php',
);

View File

@@ -0,0 +1,9 @@
<?php
// autoload_namespaces.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
);

View File

@@ -0,0 +1,19 @@
<?php
// autoload_psr4.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
'Psr\\Http7\\Message\\' => array($vendorDir . '/psr/http-factory/src', $vendorDir . '/psr/http-message/src'),
'Psr\\Http7\\Client\\' => array($vendorDir . '/psr/http-client/src'),
'GuzzleHttp7\\Psr7\\' => array($vendorDir . '/guzzlehttp/psr7/src'),
'GuzzleHttp7\\Promise\\' => array($vendorDir . '/guzzlehttp/promises/src'),
'GuzzleHttp7\\' => array($vendorDir . '/guzzlehttp/guzzle/src'),
'Br33f\\Ga4\\MeasurementProtocol\\' => array($vendorDir . '/br33f/php-ga4-mp/src'),
'libphonenumber\\' => array($vendorDir . '/giggsey/libphonenumber-for-php/src'),
'Symfony\\Polyfill\\Mbstring\\' => array($vendorDir . '/symfony/polyfill-mbstring'),
'Giggsey\\Locale\\' => array($vendorDir . '/giggsey/locale/src'),
);

View File

@@ -0,0 +1,70 @@
<?php
// autoload_real.php @generated by Composer
class ComposerAutoloaderInit2e04d69c8d2e7acdacaf965c4df1ec3c
{
private static $loader;
public static function loadClassLoader($class)
{
if ('Composer\Autoload\ClassLoader' === $class) {
require __DIR__ . '/ClassLoader.php';
}
}
public static function getLoader()
{
if (null !== self::$loader) {
return self::$loader;
}
spl_autoload_register(array('ComposerAutoloaderInit2e04d69c8d2e7acdacaf965c4df1ec3c', 'loadClassLoader'), true, true);
self::$loader = $loader = new \Composer\Autoload\ClassLoader();
spl_autoload_unregister(array('ComposerAutoloaderInit2e04d69c8d2e7acdacaf965c4df1ec3c', 'loadClassLoader'));
$useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
if ($useStaticLoader) {
require_once __DIR__ . '/autoload_static.php';
call_user_func(\Composer\Autoload\ComposerStaticInit2e04d69c8d2e7acdacaf965c4df1ec3c::getInitializer($loader));
} else {
$map = require __DIR__ . '/autoload_namespaces.php';
foreach ($map as $namespace => $path) {
$loader->set($namespace, $path);
}
$map = require __DIR__ . '/autoload_psr4.php';
foreach ($map as $namespace => $path) {
$loader->setPsr4($namespace, $path);
}
$classMap = require __DIR__ . '/autoload_classmap.php';
if ($classMap) {
$loader->addClassMap($classMap);
}
}
$loader->register(true);
if ($useStaticLoader) {
$includeFiles = Composer\Autoload\ComposerStaticInit2e04d69c8d2e7acdacaf965c4df1ec3c::$files;
} else {
$includeFiles = require __DIR__ . '/autoload_files.php';
}
foreach ($includeFiles as $fileIdentifier => $file) {
composerRequire2e04d69c8d2e7acdacaf965c4df1ec3c($fileIdentifier, $file);
}
return $loader;
}
}
function composerRequire2e04d69c8d2e7acdacaf965c4df1ec3c($fileIdentifier, $file)
{
if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
require $file;
$GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
}
}

View File

@@ -0,0 +1,97 @@
<?php
// autoload_static.php @generated by Composer
namespace Composer\Autoload;
class ComposerStaticInit2e04d69c8d2e7acdacaf965c4df1ec3c
{
public static $files = array (
'7b11c4dc42b3b3023073cb14e519683c' => __DIR__ . '/..' . '/ralouphie/getallheaders/src/getallheaders.php',
'6e3fae29631ef280660b3cdad06f25a8' => __DIR__ . '/..' . '/symfony/deprecation-contracts/function.php',
'37a3dc5111fe8f707ab4c132ef1dbc62' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/functions_include.php',
'0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => __DIR__ . '/..' . '/symfony/polyfill-mbstring/bootstrap.php',
);
public static $prefixLengthsPsr4 = array (
'P' =>
array (
'Psr\\Http\\Message\\' => 17,
'Psr\\Http\\Client\\' => 16,
),
'G' =>
array (
'GuzzleHttp7\\Psr7\\' => 16,
'GuzzleHttp7\\Promise\\' => 19,
'GuzzleHttp7\\' => 11,
),
'B' =>
array (
'Br33f\\Ga4\\MeasurementProtocol\\' => 30,
),
'l' =>
array (
'libphonenumber\\' => 15,
),
'S' =>
array (
'Symfony\\Polyfill\\Mbstring\\' => 26,
),
'H' =>
array (
'Giggsey\\Locale\\' => 15,
),
);
public static $prefixDirsPsr4 = array (
'Psr\\Http7\\Message\\' =>
array (
0 => __DIR__ . '/..' . '/psr/http-factory/src',
1 => __DIR__ . '/..' . '/psr/http-message/src',
),
'Psr\\Http7\\Client\\' =>
array (
0 => __DIR__ . '/..' . '/psr/http-client/src',
),
'GuzzleHttp7\\Psr7\\' =>
array (
0 => __DIR__ . '/..' . '/guzzlehttp/psr7/src',
),
'GuzzleHttp7\\Promise\\' =>
array (
0 => __DIR__ . '/..' . '/guzzlehttp/promises/src',
),
'GuzzleHttp7\\' =>
array (
0 => __DIR__ . '/..' . '/guzzlehttp/guzzle/src',
),
'Br33f\\Ga4\\MeasurementProtocol\\' =>
array (
0 => __DIR__ . '/..' . '/br33f/php-ga4-mp/src',
),
'libphonenumber\\' =>
array (
0 => __DIR__ . '/..' . '/giggsey/libphonenumber-for-php/src',
),
'Symfony\\Polyfill\\Mbstring\\' =>
array (
0 => __DIR__ . '/..' . '/symfony/polyfill-mbstring',
),
'Giggsey\\Locale\\' =>
array (
0 => __DIR__ . '/..' . '/giggsey/locale/src',
),
);
public static function getInitializer(ClassLoader $loader)
{
return \Closure::bind(function () use ($loader) {
$loader->prefixLengthsPsr4 = ComposerStaticInit2e04d69c8d2e7acdacaf965c4df1ec3c::$prefixLengthsPsr4;
$loader->prefixDirsPsr4 = ComposerStaticInit2e04d69c8d2e7acdacaf965c4df1ec3c::$prefixDirsPsr4;
}, null, ClassLoader::class);
}
}

View File

@@ -0,0 +1,575 @@
[
{
"name": "br33f/php-ga4-mp",
"version": "v0.1.3",
"version_normalized": "0.1.3.0",
"source": {
"type": "git",
"url": "https://github.com/br33f/php-GA4-Measurement-Protocol.git",
"reference": "7fd3d60213e5955886e05a628c972b3291c0be31"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/br33f/php-GA4-Measurement-Protocol/zipball/7fd3d60213e5955886e05a628c972b3291c0be31",
"reference": "7fd3d60213e5955886e05a628c972b3291c0be31",
"shasum": ""
},
"require": {
"ext-json": "*",
"guzzlehttp/guzzle": "^6.5.5 || ^7.0.0",
"php": ">=7.1"
},
"require-dev": {
"fakerphp/faker": "^1.14",
"php-coveralls/php-coveralls": "^2.4",
"phpunit/phpunit": "^9.5"
},
"time": "2023-03-20T20:12:12+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
"psr-4": {
"Br33f\\Ga4\\MeasurementProtocol\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Damian Zamojski",
"email": "damian.zamojski1@gmail.com"
}
],
"description": "PHP GoogleAnalytics4 Measurement Protocol Library"
},
{
"name": "guzzlehttp/guzzle",
"version": "7.7.0",
"version_normalized": "7.7.0.0",
"source": {
"type": "git",
"url": "https://github.com/guzzle/guzzle.git",
"reference": "fb7566caccf22d74d1ab270de3551f72a58399f5"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/guzzle/guzzle/zipball/fb7566caccf22d74d1ab270de3551f72a58399f5",
"reference": "fb7566caccf22d74d1ab270de3551f72a58399f5",
"shasum": ""
},
"require": {
"ext-json": "*",
"guzzlehttp/promises": "^1.5.3 || ^2.0",
"guzzlehttp/psr7": "^1.9.1 || ^2.4.5",
"php": "^7.2.5 || ^8.0",
"psr/http-client": "^1.0",
"symfony/deprecation-contracts": "^2.2 || ^3.0"
},
"provide": {
"psr/http-client-implementation": "1.0"
},
"require-dev": {
"bamarni/composer-bin-plugin": "^1.8.1",
"ext-curl": "*",
"php-http/client-integration-tests": "dev-master#2c025848417c1135031fdf9c728ee53d0a7ceaee as 3.0.999",
"php-http/message-factory": "^1.1",
"phpunit/phpunit": "^8.5.29 || ^9.5.23",
"psr/log": "^1.1 || ^2.0 || ^3.0"
},
"suggest": {
"ext-curl": "Required for CURL handler support",
"ext-intl": "Required for Internationalized Domain Name (IDN) support",
"psr/log": "Required for using the Log middleware"
},
"time": "2023-05-21T14:04:53+00:00",
"type": "library",
"extra": {
"bamarni-bin": {
"bin-links": true,
"forward-command": false
}
},
"installation-source": "dist",
"autoload": {
"files": [
"src/functions_include.php"
],
"psr-4": {
"GuzzleHttp\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Graham Campbell",
"email": "hello@gjcampbell.co.uk",
"homepage": "https://github.com/GrahamCampbell"
},
{
"name": "Michael Dowling",
"email": "mtdowling@gmail.com",
"homepage": "https://github.com/mtdowling"
},
{
"name": "Jeremy Lindblom",
"email": "jeremeamia@gmail.com",
"homepage": "https://github.com/jeremeamia"
},
{
"name": "George Mponos",
"email": "gmponos@gmail.com",
"homepage": "https://github.com/gmponos"
},
{
"name": "Tobias Nyholm",
"email": "tobias.nyholm@gmail.com",
"homepage": "https://github.com/Nyholm"
},
{
"name": "Márk Sági-Kazár",
"email": "mark.sagikazar@gmail.com",
"homepage": "https://github.com/sagikazarmark"
},
{
"name": "Tobias Schultze",
"email": "webmaster@tubo-world.de",
"homepage": "https://github.com/Tobion"
}
],
"description": "Guzzle is a PHP HTTP client library",
"keywords": [
"client",
"curl",
"framework",
"http",
"http client",
"psr-18",
"psr-7",
"rest",
"web service"
]
},
{
"name": "guzzlehttp/promises",
"version": "2.0.0",
"version_normalized": "2.0.0.0",
"source": {
"type": "git",
"url": "https://github.com/guzzle/promises.git",
"reference": "3a494dc7dc1d7d12e511890177ae2d0e6c107da6"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/guzzle/promises/zipball/3a494dc7dc1d7d12e511890177ae2d0e6c107da6",
"reference": "3a494dc7dc1d7d12e511890177ae2d0e6c107da6",
"shasum": ""
},
"require": {
"php": "^7.2.5 || ^8.0"
},
"require-dev": {
"bamarni/composer-bin-plugin": "^1.8.1",
"phpunit/phpunit": "^8.5.29 || ^9.5.23"
},
"time": "2023-05-21T13:50:22+00:00",
"type": "library",
"extra": {
"bamarni-bin": {
"bin-links": true,
"forward-command": false
}
},
"installation-source": "dist",
"autoload": {
"psr-4": {
"GuzzleHttp\\Promise\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Graham Campbell",
"email": "hello@gjcampbell.co.uk",
"homepage": "https://github.com/GrahamCampbell"
},
{
"name": "Michael Dowling",
"email": "mtdowling@gmail.com",
"homepage": "https://github.com/mtdowling"
},
{
"name": "Tobias Nyholm",
"email": "tobias.nyholm@gmail.com",
"homepage": "https://github.com/Nyholm"
},
{
"name": "Tobias Schultze",
"email": "webmaster@tubo-world.de",
"homepage": "https://github.com/Tobion"
}
],
"description": "Guzzle promises library",
"keywords": [
"promise"
]
},
{
"name": "guzzlehttp/psr7",
"version": "2.5.0",
"version_normalized": "2.5.0.0",
"source": {
"type": "git",
"url": "https://github.com/guzzle/psr7.git",
"reference": "b635f279edd83fc275f822a1188157ffea568ff6"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/guzzle/psr7/zipball/b635f279edd83fc275f822a1188157ffea568ff6",
"reference": "b635f279edd83fc275f822a1188157ffea568ff6",
"shasum": ""
},
"require": {
"php": "^7.2.5 || ^8.0",
"psr/http-factory": "^1.0",
"psr/http-message": "^1.1 || ^2.0",
"ralouphie/getallheaders": "^3.0"
},
"provide": {
"psr/http-factory-implementation": "1.0",
"psr/http-message-implementation": "1.0"
},
"require-dev": {
"bamarni/composer-bin-plugin": "^1.8.1",
"http-interop/http-factory-tests": "^0.9",
"phpunit/phpunit": "^8.5.29 || ^9.5.23"
},
"suggest": {
"laminas/laminas-httphandlerrunner": "Emit PSR-7 responses"
},
"time": "2023-04-17T16:11:26+00:00",
"type": "library",
"extra": {
"bamarni-bin": {
"bin-links": true,
"forward-command": false
}
},
"installation-source": "dist",
"autoload": {
"psr-4": {
"GuzzleHttp\\Psr7\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Graham Campbell",
"email": "hello@gjcampbell.co.uk",
"homepage": "https://github.com/GrahamCampbell"
},
{
"name": "Michael Dowling",
"email": "mtdowling@gmail.com",
"homepage": "https://github.com/mtdowling"
},
{
"name": "George Mponos",
"email": "gmponos@gmail.com",
"homepage": "https://github.com/gmponos"
},
{
"name": "Tobias Nyholm",
"email": "tobias.nyholm@gmail.com",
"homepage": "https://github.com/Nyholm"
},
{
"name": "Márk Sági-Kazár",
"email": "mark.sagikazar@gmail.com",
"homepage": "https://github.com/sagikazarmark"
},
{
"name": "Tobias Schultze",
"email": "webmaster@tubo-world.de",
"homepage": "https://github.com/Tobion"
},
{
"name": "Márk Sági-Kazár",
"email": "mark.sagikazar@gmail.com",
"homepage": "https://sagikazarmark.hu"
}
],
"description": "PSR-7 message implementation that also provides common utility methods",
"keywords": [
"http",
"message",
"psr-7",
"request",
"response",
"stream",
"uri",
"url"
]
},
{
"name": "psr/http-client",
"version": "1.0.2",
"version_normalized": "1.0.2.0",
"source": {
"type": "git",
"url": "https://github.com/php-fig/http-client.git",
"reference": "0955afe48220520692d2d09f7ab7e0f93ffd6a31"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-fig/http-client/zipball/0955afe48220520692d2d09f7ab7e0f93ffd6a31",
"reference": "0955afe48220520692d2d09f7ab7e0f93ffd6a31",
"shasum": ""
},
"require": {
"php": "^7.0 || ^8.0",
"psr/http-message": "^1.0 || ^2.0"
},
"time": "2023-04-10T20:12:12+00:00",
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
}
},
"installation-source": "dist",
"autoload": {
"psr-4": {
"Psr\\Http\\Client\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "PHP-FIG",
"homepage": "https://www.php-fig.org/"
}
],
"description": "Common interface for HTTP clients",
"homepage": "https://github.com/php-fig/http-client",
"keywords": [
"http",
"http-client",
"psr",
"psr-18"
]
},
{
"name": "psr/http-factory",
"version": "1.0.2",
"version_normalized": "1.0.2.0",
"source": {
"type": "git",
"url": "https://github.com/php-fig/http-factory.git",
"reference": "e616d01114759c4c489f93b099585439f795fe35"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-fig/http-factory/zipball/e616d01114759c4c489f93b099585439f795fe35",
"reference": "e616d01114759c4c489f93b099585439f795fe35",
"shasum": ""
},
"require": {
"php": ">=7.0.0",
"psr/http-message": "^1.0 || ^2.0"
},
"time": "2023-04-10T20:10:41+00:00",
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
}
},
"installation-source": "dist",
"autoload": {
"psr-4": {
"Psr\\Http\\Message\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "PHP-FIG",
"homepage": "https://www.php-fig.org/"
}
],
"description": "Common interfaces for PSR-7 HTTP message factories",
"keywords": [
"factory",
"http",
"message",
"psr",
"psr-17",
"psr-7",
"request",
"response"
]
},
{
"name": "psr/http-message",
"version": "2.0",
"version_normalized": "2.0.0.0",
"source": {
"type": "git",
"url": "https://github.com/php-fig/http-message.git",
"reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-fig/http-message/zipball/402d35bcb92c70c026d1a6a9883f06b2ead23d71",
"reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71",
"shasum": ""
},
"require": {
"php": "^7.2 || ^8.0"
},
"time": "2023-04-04T09:54:51+00:00",
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.0.x-dev"
}
},
"installation-source": "dist",
"autoload": {
"psr-4": {
"Psr\\Http\\Message\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "PHP-FIG",
"homepage": "https://www.php-fig.org/"
}
],
"description": "Common interface for HTTP messages",
"homepage": "https://github.com/php-fig/http-message",
"keywords": [
"http",
"http-message",
"psr",
"psr-7",
"request",
"response"
]
},
{
"name": "ralouphie/getallheaders",
"version": "3.0.3",
"version_normalized": "3.0.3.0",
"source": {
"type": "git",
"url": "https://github.com/ralouphie/getallheaders.git",
"reference": "120b605dfeb996808c31b6477290a714d356e822"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822",
"reference": "120b605dfeb996808c31b6477290a714d356e822",
"shasum": ""
},
"require": {
"php": ">=5.6"
},
"require-dev": {
"php-coveralls/php-coveralls": "^2.1",
"phpunit/phpunit": "^5 || ^6.5"
},
"time": "2019-03-08T08:55:37+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
"files": [
"src/getallheaders.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Ralph Khattar",
"email": "ralph.khattar@gmail.com"
}
],
"description": "A polyfill for getallheaders."
},
{
"name": "symfony/deprecation-contracts",
"version": "v2.5.2",
"version_normalized": "2.5.2.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/deprecation-contracts.git",
"reference": "e8b495ea28c1d97b5e0c121748d6f9b53d075c66"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/e8b495ea28c1d97b5e0c121748d6f9b53d075c66",
"reference": "e8b495ea28c1d97b5e0c121748d6f9b53d075c66",
"shasum": ""
},
"require": {
"php": ">=7.1"
},
"time": "2022-01-02T09:53:40+00:00",
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "2.5-dev"
},
"thanks": {
"name": "symfony/contracts",
"url": "https://github.com/symfony/contracts"
}
},
"installation-source": "dist",
"autoload": {
"files": [
"function.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Nicolas Grekas",
"email": "p@tchwork.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "A generic function and convention to trigger deprecation notices",
"homepage": "https://symfony.com"
}
]

View File

@@ -0,0 +1,177 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS

View File

@@ -0,0 +1,5 @@
# This file specifies the revision of the metadata to build from
# It can be a commit, branch or tag of the https://github.com/google/libphonenumber project
#
# For more information, look at the phing tasks in build.xml
v8.13.6

View File

@@ -0,0 +1,267 @@
# libphonenumber for PHP [![Build Status](https://github.com/giggsey/libphonenumber-for-php/workflows/Continuous%20Integration/badge.svg)](https://github.com/giggsey/libphonenumber-for-php/actions?query=workflow%3A%22Continuous+Integration%22) [![Coverage Status](https://img.shields.io/coveralls/giggsey/libphonenumber-for-php.svg?style=flat-square)](https://coveralls.io/r/giggsey/libphonenumber-for-php?branch=master)
[![Total Downloads](https://poser.pugx.org/giggsey/libphonenumber-for-php/downloads?format=flat-square)](https://packagist.org/packages/giggsey/libphonenumber-for-php)
[![Downloads per month](https://img.shields.io/packagist/dm/giggsey/libphonenumber-for-php.svg?style=flat-square)](https://packagist.org/packages/giggsey/libphonenumber-for-php)
[![Latest Stable Version](https://img.shields.io/packagist/v/giggsey/libphonenumber-for-php.svg?style=flat-square)](https://packagist.org/packages/giggsey/libphonenumber-for-php)
[![License](https://img.shields.io/badge/license-Apache%202.0-red.svg?style=flat-square)](https://packagist.org/packages/giggsey/libphonenumber-for-php)
## What is it?
A PHP library for parsing, formatting, storing and validating international phone numbers. This library is based on Google's [libphonenumber](https://github.com/google/libphonenumber).
- [Installation](#installation)
- [Documentation](#documentation)
- [Online Demo](#online-demo)
- [Highlights of functionality](#highlights-of-functionality)
- [Versioning](#versioning)
- [Quick Examples](#quick-examples)
- [Geocoder](#geocoder)
- [ShortNumberInfo](#shortnumberinfo)
- [Mapping Phone Numbers to Carrier](#mapping-phone-numbers-to-carrier)
- [Mapping Phone Numbers to TimeZones](#mapping-phone-numbers-to-timezones)
- [FAQ](#faq)
- [Problems with Invalid Numbers?](#problems-with-invalid-numbers)
- [Generating data](#generating-data)
- [Integration with frameworks](#integration-with-frameworks)
## Installation
PHP versions 5.3 to PHP 8.2 are currently supported.
The PECL [mbstring](http://php.net/mbstring) extension is required.
It is recommended to use [composer](https://getcomposer.org) to install the library.
```bash
$ composer require giggsey/libphonenumber-for-php
```
You can also use any other [PSR-4](http://www.php-fig.org/psr/psr-4/) compliant autoloader.
If you do not use composer, ensure that you also load any dependencies that this project has, such as [giggsey/locale](https://github.com/giggsey/Locale).
## giggsey/libphonenumber-for-php-lite
If you only want to make sure of the core PhoneNumber Util functionality, you can use [giggsey/libphonenumber-for-php-lite](https://github.com/giggsey/libphonenumber-for-php-lite), which offers a much smaller package size.
## Documentation
- [PhoneNumber Util](docs/PhoneNumberUtil.md)
- [ShortNumber Info](docs/ShortNumberInfo.md)
- [Phone Number Geolocation](docs/PhoneNumberOfflineGeocoder.md)
- [Phone Number to Carrier Mapping](docs/PhoneNumberToCarrierMapper.md)
- [Phone Number to Timezone Mapping](docs/PhoneNumberToTimeZonesMapper.md)
- [Phone Number Matcher](docs/PhoneNumberMatcher.md)
- [As You Type Formatter](docs/AsYouTypeFormatter.md)
## Online Demo
An [online demo](http://giggsey.com/libphonenumber/) is available, and the source can be found at [giggsey/libphonenumber-example](https://github.com/giggsey/libphonenumber-example).
# Highlights of functionality
* Parsing/formatting/validating phone numbers for all countries/regions of the world.
* `getNumberType` - gets the type of the number based on the number itself; able to distinguish Fixed-line, Mobile, Toll-free, Premium Rate, Shared Cost, VoIP and Personal Numbers (whenever feasible).
* `isNumberMatch` - gets a confidence level on whether two numbers could be the same.
* `getExampleNumber`/`getExampleNumberByType` - provides valid example numbers for all countries/regions, with the option of specifying which type of example phone number is needed.
* `isValidNumber` - full validation of a phone number for a region using length and prefix information.
* `PhoneNumberOfflineGeocoder` - provides geographical information related to a phone number.
* `PhoneNumberToTimeZonesMapper` - provides timezone information related to a phone number.
* `PhoneNumberToCarrierMapper` - provides carrier information related to a phone number.
## Versioning
This library will try to follow the same version numbers as Google. There could be additional releases where needed to fix critical issues that can not wait until the next release from Google.
This does mean that this project may not follow [Semantic Versioning](http://semver.org/), but instead Google's version policy. As a result, jumps in major versions may not actually contain any backwards
incompatible changes. Please read the release notes for such releases.
Google try to release their versions according to Semantic Versioning, as laid out of in their [Versioning Guide](https://github.com/google/libphonenumber#versioning-and-announcements).
## Quick Examples
Let's say you have a string representing a phone number from Switzerland. This is how you parse/normalize it into a PhoneNumber object:
```php
$swissNumberStr = "044 668 18 00";
$phoneUtil = \libphonenumber\PhoneNumberUtil::getInstance();
try {
$swissNumberProto = $phoneUtil->parse($swissNumberStr, "CH");
var_dump($swissNumberProto);
} catch (\libphonenumber\NumberParseException $e) {
var_dump($e);
}
```
At this point, swissNumberProto contains:
class libphonenumber\PhoneNumber#9 (7) {
private $countryCode =>
int(41)
private $nationalNumber =>
double(446681800)
private $extension =>
NULL
private $italianLeadingZero =>
NULL
private $rawInput =>
NULL
private $countryCodeSource =>
NULL
private $preferredDomesticCarrierCode =>
NULL
}
Now let us validate whether the number is valid:
```php
$isValid = $phoneUtil->isValidNumber($swissNumberProto);
var_dump($isValid); // true
```
There are a few formats supported by the formatting method, as illustrated below:
```php
// Produces "+41446681800"
echo $phoneUtil->format($swissNumberProto, \libphonenumber\PhoneNumberFormat::E164);
// Produces "044 668 18 00"
echo $phoneUtil->format($swissNumberProto, \libphonenumber\PhoneNumberFormat::NATIONAL);
// Produces "+41 44 668 18 00"
echo $phoneUtil->format($swissNumberProto, \libphonenumber\PhoneNumberFormat::INTERNATIONAL);
```
You could also choose to format the number in the way it is dialled from another country:
```php
// Produces "011 41 44 668 1800", the number when it is dialled in the United States.
echo $phoneUtil->formatOutOfCountryCallingNumber($swissNumberProto, "US");
// Produces "00 41 44 668 18 00", the number when it is dialled in Great Britain.
echo $phoneUtil->formatOutOfCountryCallingNumber($swissNumberProto, "GB");
```
### Geocoder
```php
$phoneUtil = \libphonenumber\PhoneNumberUtil::getInstance();
$swissNumberProto = $phoneUtil->parse("044 668 18 00", "CH");
$usNumberProto = $phoneUtil->parse("+1 650 253 0000", "US");
$gbNumberProto = $phoneUtil->parse("0161 496 0000", "GB");
$geocoder = \libphonenumber\geocoding\PhoneNumberOfflineGeocoder::getInstance();
// Outputs "Zurich"
echo $geocoder->getDescriptionForNumber($swissNumberProto, "en_US");
// Outputs "Zürich"
echo $geocoder->getDescriptionForNumber($swissNumberProto, "de_DE");
// Outputs "Zurigo"
echo $geocoder->getDescriptionForNumber($swissNumberProto, "it_IT");
// Outputs "Mountain View, CA"
echo $geocoder->getDescriptionForNumber($usNumberProto, "en_US");
// Outputs "Mountain View, CA"
echo $geocoder->getDescriptionForNumber($usNumberProto, "de_DE");
// Outputs "미국" (Korean for United States)
echo $geocoder->getDescriptionForNumber($usNumberProto, "ko-KR");
// Outputs "Manchester"
echo $geocoder->getDescriptionForNumber($gbNumberProto, "en_GB");
// Outputs "영국" (Korean for United Kingdom)
echo $geocoder->getDescriptionForNumber($gbNumberProto, "ko-KR");
```
### ShortNumberInfo
```php
$shortNumberInfo = \libphonenumber\ShortNumberInfo::getInstance();
// true
var_dump($shortNumberInfo->isEmergencyNumber("999", "GB"));
// true
var_dump($shortNumberInfo->connectsToEmergencyNumber("999", "GB"));
// false
var_dump($shortNumberInfo->connectsToEmergencyNumber("911", "GB"));
// true
var_dump($shortNumberInfo->isEmergencyNumber("911", "US"));
// true
var_dump($shortNumberInfo->connectsToEmergencyNumber("911", "US"));
// false
var_dump($shortNumberInfo->isEmergencyNumber("911123", "US"));
// true
var_dump($shortNumberInfo->connectsToEmergencyNumber("911123", "US"));
```
### Mapping Phone Numbers to carrier
```php
$phoneUtil = \libphonenumber\PhoneNumberUtil::getInstance();
$swissNumberProto = $phoneUtil->parse("798765432", "CH");
$carrierMapper = \libphonenumber\PhoneNumberToCarrierMapper::getInstance();
// Outputs "Swisscom"
echo $carrierMapper->getNameForNumber($swissNumberProto, "en");
```
### Mapping Phone Numbers to TimeZones
```php
$phoneUtil = \libphonenumber\PhoneNumberUtil::getInstance();
$swissNumberProto = $phoneUtil->parse("798765432", "CH");
$timeZoneMapper = \libphonenumber\PhoneNumberToTimeZonesMapper::getInstance();
// returns array("Europe/Zurich")
$timeZones = $timeZoneMapper->getTimeZonesForNumber($swissNumberProto);
```
## FAQ
#### Problems with Invalid Numbers?
This library uses phone number metadata from Google's [libphonenumber](https://github.com/google/libphonenumber). If this library is working as intended, it should provide the same result as the Java version of Google's project.
If you believe that a phone number is returning an incorrect result, first test it with [libphonenumber](https://github.com/google/libphonenumber) via their [Online Demo](https://libphonenumber.appspot.com/). If that returns the same result as this project, and you feel it is in error, raise it as an Issue with the libphonenumber project.
If Google's [Online Demo](https://libphonenumber.appspot.com/) gives a different result to the [libphonenumber-for-php demo](http://giggsey.com/libphonenumber/), then please raise an Issue here.
## Generating data
Generating the data is not normally needed, as this repository will generally always have the up to data metadata.
If you do need to generate the data, the commands are provided by [Phing](https://www.phing.info). Ensure you have all the dev composer dependencies installed, then run
```bash
$ vendor/bin/phing compile
```
This compile process clones the [libphonenumber](https://github.com/google/libphonenumber) project at the version specified in [METADATA-VERSION.txt](METADATA-VERSION.txt).
### Running tests
This project uses [PHPUnit Bridge](https://symfony.com/doc/current/components/phpunit_bridge.html) to maintain compatibility for the supported PHP versions.
To run the tests locally, run the `./phpunit` script.
## Integration with frameworks
Other packages exist that integrate libphonenumber-for-php into frameworks.
| Framework | Packages |
| --------- |:-------------:|
|Symfony|[PhoneNumberBundle](https://github.com/odolbeau/phone-number-bundle)|
|Laravel|[Laravel Phone](https://github.com/Propaganistas/Laravel-Phone)|
|Yii2|[PhoneInput](https://github.com/Borales/yii2-phone-input)|
|Kohana|[PhoneNumber](https://github.com/softmediadev/kohana-phonenumber)|
|TYPO3|[TYPO3 Phone Extension](https://github.com/simonschaufi/typo3-phone)|
These packages are supplied by third parties, and their quality can not be guaranteed.

View File

@@ -0,0 +1,84 @@
{
"name": "giggsey/libphonenumber-for-php",
"type": "library",
"description": "PHP Port of Google's libphonenumber",
"keywords": [
"phonenumber",
"libphonenumber",
"mobile",
"validation",
"geocoding",
"geolocation"
],
"homepage": "https://github.com/giggsey/libphonenumber-for-php",
"license": "Apache-2.0",
"support": {
"source": "https://github.com/giggsey/libphonenumber-for-php",
"issues": "https://github.com/giggsey/libphonenumber-for-php/issues"
},
"authors": [
{
"name": "Joshua Gigg",
"email": "giggsey@gmail.com",
"homepage": "https://giggsey.com/"
}
],
"autoload": {
"psr-4": {
"libphonenumber\\": "src/"
},
"exclude-from-classmap": [
"/src/data/",
"/src/carrier/data/",
"/src/geocoding/data/",
"/src/timezone/data/"
]
},
"autoload-dev": {
"psr-4": {
"libphonenumber\\buildtools\\": "build/",
"libphonenumber\\Tests\\": "tests/"
}
},
"archive": {
"exclude": [
"build/",
"docs/",
"tests/",
".github/",
".editorconfig",
".gitattributes",
".gitignore",
".styleci.yml",
".travis.yml",
"build.xml",
"libphonenumber-for-php.spec",
"phpunit.xml.dist",
"phpunit"
]
},
"require": {
"php": ">=5.3.2",
"giggsey/locale": "^1.7|^2.0",
"symfony/polyfill-mbstring": "^1.17"
},
"require-dev": {
"phing/phing": "^2.7",
"pear/versioncontrol_git": "^0.5",
"pear/pear-core-minimal": "^1.9",
"pear/pear_exception": "^1.0",
"symfony/phpunit-bridge": "^4.2 || ^5",
"symfony/console": "^2.8|^3.0|^v4.4|^v5.2",
"php-coveralls/php-coveralls": "^1.0|^2.0"
},
"extra": {
"branch-alias": {
"dev-master": "8.x-dev"
}
},
"scripts": {
"test": "./phpunit",
"metadata": "vendor/bin/phing compile"
}
}

View File

@@ -0,0 +1,68 @@
<?php
/**
* This file has been @generated by a phing task by {@link BuildMetadataPHPFromXml}.
* See [README.md](README.md#generating-data) for more information.
*
* Pull requests changing data in these files will not be accepted. See the
* [FAQ in the README](README.md#problems-with-invalid-numbers] on how to make
* metadata changes.
*
* Do not modify this file directly!
*/
namespace libphonenumber;
class AlternateFormatsCountryCodeSet {
// A set of all country codes for which data is available.
public static $alternateFormatsCountryCodeSet = array (
0 => 7,
1 => 27,
2 => 30,
3 => 31,
4 => 34,
5 => 36,
6 => 39,
7 => 43,
8 => 44,
9 => 49,
10 => 52,
11 => 54,
12 => 55,
13 => 58,
14 => 61,
15 => 62,
16 => 64,
17 => 66,
18 => 81,
19 => 84,
20 => 90,
21 => 91,
22 => 94,
23 => 95,
24 => 255,
25 => 350,
26 => 351,
27 => 352,
28 => 358,
29 => 359,
30 => 372,
31 => 373,
32 => 380,
33 => 381,
34 => 385,
35 => 505,
36 => 506,
37 => 595,
38 => 675,
39 => 676,
40 => 679,
41 => 855,
42 => 856,
43 => 971,
44 => 972,
45 => 995,
);
}

View File

@@ -0,0 +1,846 @@
<?php
namespace libphonenumber;
/**
* Class AsYouTypeFormatter
* A formatter which formats phone numbers as they are entered.
*
* An AsYouTypeFormatter instance can be created by invoking PhoneNumberUtil::getAsYouTypeFormatter().
* After that, digits can be added by invoking inputDigit() on the formatter instance, and the partially
* formatted phone number will be returned each time a digit is added. clear() can be invokved before
* formatting a new number.
*/
class AsYouTypeFormatter
{
/**
* @var string
*/
private $currentOutput;
/**
* @var string
*/
private $formattingTemplate;
/**
* The pattern from numberFormat that is currently used to create formattingTemplate.
* @var string
*/
private $currentFormattingPattern;
/**
* @var string
*/
private $accruedInput;
/**
* @var string
*/
private $accruedInputWithoutFormatting;
/**
* This indicated whether AsYouTypeFormatter is currently doing the formatting
* @var bool
*/
private $ableToFormat = true;
/**
* Set to true when users enter their own formatting. AsYouTypeFormatter will do no formatting at
* all when this is set to true
* @var bool
*/
private $inputHasFormatting = false;
/**
* This is set to true when we know the user is entering a full national significant number, since
* we have either detected a national prefix or an international dialing prefix. When this is
* true, we will no longer use local formatting patterns.
* @var bool
*/
private $isCompleteNumber = false;
/**
* @var bool
*/
private $isExpectingCountryCallingCode = false;
/**
* @var PhoneNumberUtil
*/
private $phoneUtil;
/**
* @var string
*/
private $defaultCountry;
/**
* @var PhoneMetadata
*/
private $defaultMetadata;
/**
* @var PhoneMetadata
*/
private $currentMetadata;
/**
* @var NumberFormat[]
*/
private $possibleFormats = array();
/**
* @var int
*/
private $lastMatchPosition = 0;
/**
* The position of a digit upon which inputDigitAndRememberPosition is most recently invoked,
* as found in the original sequence of characters the user entered.
* @var int
*/
private $originalPosition = 0;
/**
* The position of a digit upon which inputDigitAndRememberPosition is most recently invoked,
* as found in accruedInputWithoutFormatting
* @var int
*/
private $positionToRemember = 0;
/**
* This contains anything that has been entered so far preceding the national significant number,
* and it is formatted (e.g. with space inserted). For example, this can contain IDD, country code,
* and/or NDD, etc.
* @var string
*/
private $prefixBeforeNationalNumber = '';
/**
* @var bool
*/
private $shouldAddSpaceAfterNationalPrefix = false;
/**
* This contains the national prefix that has been extracted. It contains only digits without
* formatting
* @var string
*/
private $extractedNationalPrefix = '';
/**
* @var string
*/
private $nationalNumber;
/**
* @var bool
*/
private static $initialised = false;
/**
* Character used when appropriate to separate a prefix, such as a long NDD or a country
* calling code, from the national number.
* @var string
*/
private static $seperatorBeforeNationalNumber = ' ';
/**
* @var PhoneMetadata
*/
private static $emptyMetadata;
/**
* A pattern that is used to determine if a numberFormat under availableFormats is eligible
* to be used by the AYTF. It is eligible when the format element under numberFormat contains
* groups of the dollar sign followed by a single digit, separated by valid phone number punctuation.
* This prevents invalid punctuation (such as the star sign in Israeli star numbers) getting
* into the output of the AYTF. We require that the first group is present in the output pattern to ensure
* no data is lost while formatting; when we format as you type, this should always be the case.
* @var string
*/
private static $eligibleFormatPattern;
/**
* A set of characters that, if found in the national prefix formatting rules, are an indicator
* to us that we should separate the national prefix from the numbers when formatting.
* @var string
*/
private static $nationalPrefixSeparatorsPattern = '[- ]';
/**
* This is the minimum length of national number accrued that is required to trigger the
* formatter. The first element of the leadingDigitsPattern of each numberFormat contains
* a regular expression that matches up to this number of digits.
* @var int
*/
private static $minLeadingDigitsLength = 3;
/**
* The digits that have not been entered yet will be represented by a \u2008, the punctuation
* space.
* @var string
*/
private static $digitPattern = "\xE2\x80\x88";
private static function init()
{
if (self::$initialised === false) {
self::$initialised = true;
self::$emptyMetadata = new PhoneMetadata();
self::$emptyMetadata->setInternationalPrefix('NA');
self::$eligibleFormatPattern = '[' . PhoneNumberUtil::VALID_PUNCTUATION . ']*'
. "\\$1" . "[" . PhoneNumberUtil::VALID_PUNCTUATION . "]*(\\$\\d"
. "[" . PhoneNumberUtil::VALID_PUNCTUATION . "]*)*";
}
}
/**
* Constructs as as-you-type formatter. Should be obtained from PhoneNumberUtil->getAsYouTypeFormatter()
* @param string $regionCode The country/region where the phone number is being entered
*/
public function __construct($regionCode)
{
self::init();
$this->phoneUtil = PhoneNumberUtil::getInstance();
$this->defaultCountry = strtoupper($regionCode);
$this->currentMetadata = $this->getMetadataForRegion($this->defaultCountry);
$this->defaultMetadata = $this->currentMetadata;
}
/**
* The metadata needed by this class is the same for all regions sharing the same country calling
* code. Therefore, we return the metadata for the 'main' region for this country calling code.
* @param string $regionCode
* @return PhoneMetadata
*/
private function getMetadataForRegion($regionCode)
{
$countryCallingCode = $this->phoneUtil->getCountryCodeForRegion($regionCode);
$mainCountry = $this->phoneUtil->getRegionCodeForCountryCode($countryCallingCode);
$metadata = $this->phoneUtil->getMetadataForRegion($mainCountry);
if ($metadata !== null) {
return $metadata;
}
// Set to a default instance of teh metadata. This allows us to function with an incorrect
// region code, even if the formatting only works for numbers specified with "+".
return self::$emptyMetadata;
}
/**
* Returns true if a new template is created as opposed to reusing the existing template.
* @return bool
*/
private function maybeCreateNewTemplate()
{
// When there are multiple available formats, the formatter uses the first format where a
// formatting template could be created.
foreach ($this->possibleFormats as $key => $numberFormat) {
$pattern = $numberFormat->getPattern();
if ($this->currentFormattingPattern == $pattern) {
return false;
}
if ($this->createFormattingTemplate($numberFormat)) {
$this->currentFormattingPattern = $pattern;
$nationalPrefixSeparatorsMatcher = new Matcher(
self::$nationalPrefixSeparatorsPattern,
$numberFormat->getNationalPrefixFormattingRule()
);
$this->shouldAddSpaceAfterNationalPrefix = $nationalPrefixSeparatorsMatcher->find();
// With a new formatting template, the matched position using the old template
// needs to be reset.
$this->lastMatchPosition = 0;
return true;
}
// Remove the current number format from $this->possibleFormats
unset($this->possibleFormats[$key]);
}
$this->ableToFormat = false;
return false;
}
/**
* @param string $leadingDigits
*/
private function getAvailableFormats($leadingDigits)
{
// First decide whether we should use international or national number rules.
$isInternationalNumber = $this->isCompleteNumber && $this->extractedNationalPrefix === '';
$formatList = ($isInternationalNumber && $this->currentMetadata->intlNumberFormatSize() > 0)
? $this->currentMetadata->intlNumberFormats()
: $this->currentMetadata->numberFormats();
foreach ($formatList as $format) {
// Discard a few formats that we know are not relevant based on the presence of the national
// prefix.
if ($this->extractedNationalPrefix !== ''
&& PhoneNumberUtil::formattingRuleHasFirstGroupOnly(
$format->getNationalPrefixFormattingRule()
)
&& !$format->getNationalPrefixOptionalWhenFormatting()
&& !$format->hasDomesticCarrierCodeFormattingRule()) {
// If it is a national number that had a national prefix, any rules that aren't valid with a
// national prefix should be excluded. A rule that has a carrier-code formatting rule is
// kept since the national prefix might actually be an extracted carrier code - we don't
// distinguish between these when extracting it in the AYTF.
continue;
}
if ($this->extractedNationalPrefix === ''
&& !$this->isCompleteNumber
&& !PhoneNumberUtil::formattingRuleHasFirstGroupOnly(
$format->getNationalPrefixFormattingRule()
)
&& !$format->getNationalPrefixOptionalWhenFormatting()) {
// This number was entered without a national prefix, and this formatting rule requires one,
// so we discard it.
continue;
}
$eligibleFormatMatcher = new Matcher(self::$eligibleFormatPattern, $format->getFormat());
if ($eligibleFormatMatcher->matches()) {
$this->possibleFormats[] = $format;
}
}
$this->narrowDownPossibleFormats($leadingDigits);
}
/**
* @param $leadingDigits
*/
private function narrowDownPossibleFormats($leadingDigits)
{
$indexOfLeadingDigitsPattern = \mb_strlen($leadingDigits) - self::$minLeadingDigitsLength;
foreach ($this->possibleFormats as $key => $format) {
if ($format->leadingDigitsPatternSize() === 0) {
// Keep everything that isn't restricted by leading digits.
continue;
}
$lastLeadingDigitsPattern = \min($indexOfLeadingDigitsPattern, $format->leadingDigitsPatternSize() - 1);
$leadingDigitsPattern = $format->getLeadingDigitsPattern($lastLeadingDigitsPattern);
$m = new Matcher($leadingDigitsPattern, $leadingDigits);
if (!$m->lookingAt()) {
unset($this->possibleFormats[$key]);
}
}
}
/**
* @param NumberFormat $format
* @return bool
*/
private function createFormattingTemplate(NumberFormat $format)
{
$numberPattern = $format->getPattern();
$this->formattingTemplate = '';
$tempTemplate = $this->getFormattingTemplate($numberPattern, $format->getFormat());
if ($tempTemplate !== '') {
$this->formattingTemplate .= $tempTemplate;
return true;
}
return false;
}
/**
* Gets a formatting template which can be used to efficiently format a partial number where
* digits are added one by one.
* @param string $numberPattern
* @param string $numberFormat
* @return string
*/
private function getFormattingTemplate($numberPattern, $numberFormat)
{
// Creates a phone number consisting only of the digit 9 that matches the
// numberPattern by applying the pattern to the longestPhoneNumber string.
$longestPhoneNumber = '999999999999999';
$m = new Matcher($numberPattern, $longestPhoneNumber);
$m->find();
$aPhoneNumber = $m->group();
// No formatting template can be created if the number of digits entered entered so far
// is longer than the maximum the current formatting rule can accommodate.
if (\mb_strlen($aPhoneNumber) < \mb_strlen($this->nationalNumber)) {
return '';
}
// Formats the number according to $numberFormat
$template = \preg_replace('/' . $numberPattern . '/' . PhoneNumberUtil::REGEX_FLAGS, $numberFormat, $aPhoneNumber);
// Replaces each digit with character self::$digitPlattern
$template = \preg_replace('/9/', self::$digitPattern, $template);
return $template;
}
/**
* Clears the internal state of the formatter, so it can be reused.
*/
public function clear()
{
$this->currentOutput = '';
$this->accruedInput = '';
$this->accruedInputWithoutFormatting = '';
$this->formattingTemplate = '';
$this->lastMatchPosition = 0;
$this->currentFormattingPattern = '';
$this->prefixBeforeNationalNumber = '';
$this->extractedNationalPrefix = '';
$this->nationalNumber = '';
$this->ableToFormat = true;
$this->inputHasFormatting = false;
$this->positionToRemember = 0;
$this->originalPosition = 0;
$this->isCompleteNumber = false;
$this->isExpectingCountryCallingCode = false;
$this->possibleFormats = array();
$this->shouldAddSpaceAfterNationalPrefix = false;
if ($this->currentMetadata !== $this->defaultMetadata) {
$this->currentMetadata = $this->getMetadataForRegion($this->defaultCountry);
}
}
/**
* Formats a phone number on-the-fly as each digit is entered.
*
* @param string $nextChar the most recently entered digit of a phone number. Formatting characters
* are allowed, but as soon as they are encountered this method foramts the number as entered
* and not "as you type" anymore. Full width digits and Arabic-indic digits are allowed, and will
* be shown as they are.
* @return string The partially formatted phone number
*/
public function inputDigit($nextChar)
{
$this->currentOutput = $this->inputDigitWithOptionToRememberPosition($nextChar, false);
return $this->currentOutput;
}
/**
* Same as $this->inputDigit(), but remembers the position where $nextChar is inserted, so
* that is can be retrieved later by using $this->getRememberedPosition(). The remembered
* position will be automatically adjusted if additional formatting characters are later
* inserted/removed in front of $nextChar
* @param string $nextChar
* @return string
*/
public function inputDigitAndRememberPosition($nextChar)
{
$this->currentOutput = $this->inputDigitWithOptionToRememberPosition($nextChar, true);
return $this->currentOutput;
}
/**
* @param string $nextChar
* @param bool $rememberPosition
* @return string
*/
private function inputDigitWithOptionToRememberPosition($nextChar, $rememberPosition)
{
$this->accruedInput .= $nextChar;
if ($rememberPosition) {
$this->originalPosition = \mb_strlen($this->accruedInput);
}
// We do formatting on-the-fly only when each character entered is either a digit, or a plus
// sign (accepted at the start of the number only).
if (!$this->isDigitOrLeadingPlusSign($nextChar)) {
$this->ableToFormat = false;
$this->inputHasFormatting = true;
} else {
$nextChar = $this->normalizeAndAccrueDigitsAndPlusSign($nextChar, $rememberPosition);
}
if (!$this->ableToFormat) {
// When we are unable to format because of reasons other than that formatting chars have been
// entered, it can be due to really long IDDs or NDDs. If that is the case, we might be able
// to do formatting again after extracting them.
if ($this->inputHasFormatting) {
return $this->accruedInput;
}
if ($this->attemptToExtractIdd()) {
if ($this->attemptToExtractCountryCallingCode()) {
return $this->attemptToChoosePatternWithPrefixExtracted();
}
} elseif ($this->ableToExtractLongerNdd()) {
// Add an additional space to separate long NDD and national significant number for
// readability. We don't set shouldAddSpaceAfterNationalPrefix to true, since we don't want
// this to change later when we choose formatting templates.
$this->prefixBeforeNationalNumber .= self::$seperatorBeforeNationalNumber;
return $this->attemptToChoosePatternWithPrefixExtracted();
}
return $this->accruedInput;
}
// We start to attempt to format only when at least MIN_LEADING_DIGITS_LENGTH digits (the plus
// sign is counted as a digit as well for this purpose) have been entered.
switch (\mb_strlen($this->accruedInputWithoutFormatting)) {
case 0:
case 1:
case 2:
return $this->accruedInput;
/** @noinspection PhpMissingBreakStatementInspection */
case 3:
if ($this->attemptToExtractIdd()) {
$this->isExpectingCountryCallingCode = true;
} else {
// No IDD or plus sign is found, might be entering in national format.
$this->extractedNationalPrefix = $this->removeNationalPrefixFromNationalNumber();
return $this->attemptToChooseFormattingPattern();
}
// fall through
// no break
default:
if ($this->isExpectingCountryCallingCode) {
if ($this->attemptToExtractCountryCallingCode()) {
$this->isExpectingCountryCallingCode = false;
}
return $this->prefixBeforeNationalNumber . $this->nationalNumber;
}
if (\count($this->possibleFormats) > 0) {
// The formatting patterns are already chosen.
$tempNationalNumber = $this->inputDigitHelper($nextChar);
// See if the accrued digits can be formatted properly already. If not, use the results
// from inputDigitHelper, which does formatting based on the formatting pattern chosen.
$formattedNumber = $this->attemptToFormatAccruedDigits();
if ($formattedNumber !== '') {
return $formattedNumber;
}
$this->narrowDownPossibleFormats($this->nationalNumber);
if ($this->maybeCreateNewTemplate()) {
return $this->inputAccruedNationalNumber();
}
return $this->ableToFormat
? $this->appendNationalNumber($tempNationalNumber)
: $this->accruedInput;
}
return $this->attemptToChooseFormattingPattern();
}
}
/**
* @return string
*/
private function attemptToChoosePatternWithPrefixExtracted()
{
$this->ableToFormat = true;
$this->isExpectingCountryCallingCode = false;
$this->possibleFormats = array();
$this->lastMatchPosition = 0;
$this->formattingTemplate = '';
$this->currentFormattingPattern = '';
return $this->attemptToChooseFormattingPattern();
}
/**
* @return string
* @internal
*/
public function getExtractedNationalPrefix()
{
return $this->extractedNationalPrefix;
}
/**
* Some national prefixes are a substring of others. If extracting the shorter NDD doesn't result
* in a number we can format, we try to see if we can extract a longer version here.
* @return bool
*/
private function ableToExtractLongerNdd()
{
if (\mb_strlen($this->extractedNationalPrefix) > 0) {
// Put the extracted NDD back to the national number before attempting to extract a new NDD.
$this->nationalNumber = $this->extractedNationalPrefix . $this->nationalNumber;
// Remove the previously extracted NDD from prefixBeforeNationalNumber. We cannot simply set
// it to empty string because people sometimes incorrectly enter national prefix after the
// country code, e.g. +44 (0)20-1234-5678.
$indexOfPreviousNdd = \mb_strrpos($this->prefixBeforeNationalNumber, $this->extractedNationalPrefix);
$this->prefixBeforeNationalNumber = \mb_substr(\str_pad($this->prefixBeforeNationalNumber, $indexOfPreviousNdd), 0, $indexOfPreviousNdd);
}
return ($this->extractedNationalPrefix !== $this->removeNationalPrefixFromNationalNumber());
}
/**
* @param string $nextChar
* @return bool
*/
private function isDigitOrLeadingPlusSign($nextChar)
{
$plusCharsMatcher = new Matcher(PhoneNumberUtil::$PLUS_CHARS_PATTERN, $nextChar);
return \preg_match('/' . PhoneNumberUtil::DIGITS . '/' . PhoneNumberUtil::REGEX_FLAGS, $nextChar)
|| (\mb_strlen($this->accruedInput) === 1 &&
$plusCharsMatcher->matches());
}
/**
* Checks to see if there is an exact pattern match for these digits. If so, we should use this
* instead of any other formatting template whose leadingDigitsPattern also matches the input.
* @return string
*/
public function attemptToFormatAccruedDigits()
{
foreach ($this->possibleFormats as $numberFormat) {
$m = new Matcher($numberFormat->getPattern(), $this->nationalNumber);
if ($m->matches()) {
$nationalPrefixSeparatorsMatcher = new Matcher(self::$nationalPrefixSeparatorsPattern, $numberFormat->getNationalPrefixFormattingRule());
$this->shouldAddSpaceAfterNationalPrefix = $nationalPrefixSeparatorsMatcher->find();
$formattedNumber = $m->replaceAll($numberFormat->getFormat());
// Check that we did not remove nor add any extra digits when we matched
// this formatting pattern. This usually happens after we entered the last
// digit during AYTF. Eg: In case of MX, we swallow mobile token (1) when
// formatted but AYTF should retain all the number entered and not change
// in order to match a format (of same leading digits and length) display
// in that way.
$fullOutput = $this->appendNationalNumber($formattedNumber);
$formattedNumberDigitsOnly = PhoneNumberUtil::normalizeDiallableCharsOnly($fullOutput);
if ($formattedNumberDigitsOnly === $this->accruedInputWithoutFormatting) {
// If it's the same (i.e entered number and format is same), then it's
// safe to return this in formatted number as nothing is lost / added.
return $fullOutput;
}
}
}
return '';
}
/**
* returns the current position in the partially formatted phone number of the character which was
* previously passed in as a parameter of $this->inputDigitAndRememberPosition().
* @return int
*/
public function getRememberedPosition()
{
if (!$this->ableToFormat) {
return $this->originalPosition;
}
$accruedInputIndex = 0;
$currentOutputIndex = 0;
$currentOutputLength = \mb_strlen($this->currentOutput);
while ($accruedInputIndex < $this->positionToRemember && $currentOutputIndex < $currentOutputLength) {
if (\mb_substr($this->accruedInputWithoutFormatting, $accruedInputIndex, 1) == \mb_substr($this->currentOutput, $currentOutputIndex, 1)) {
$accruedInputIndex++;
}
$currentOutputIndex++;
}
return $currentOutputIndex;
}
/**
* Combines the national number with any prefix (IDD/+ and country code or national prefix) that
* was collected. A space will be inserted between them if the current formatting template
* indicates this to be suitable.
* @param string $nationalNumber
* @return string
*/
private function appendNationalNumber($nationalNumber)
{
$prefixBeforeNationalNumberLength = \mb_strlen($this->prefixBeforeNationalNumber);
if ($this->shouldAddSpaceAfterNationalPrefix && $prefixBeforeNationalNumberLength > 0
&& \mb_substr($this->prefixBeforeNationalNumber, $prefixBeforeNationalNumberLength - 1, 1)
!= self::$seperatorBeforeNationalNumber
) {
// We want to add a space after the national prefix if the national prefix formatting rule
// indicates that this would normally be done, with the exception of the case where we already
// appended a space because the NDD was surprisingly long.
return $this->prefixBeforeNationalNumber . self::$seperatorBeforeNationalNumber . $nationalNumber;
}
return $this->prefixBeforeNationalNumber . $nationalNumber;
}
/**
* Attempts to set the formatting template and returns a string which contains the formatted
* version of the digits entered so far.
* @return string
*/
private function attemptToChooseFormattingPattern()
{
// We start to attempt to format only when at least MIN_LEADING_DIGITS_LENGTH digits of national
// number (excluding national prefix) have been entered.
if (\mb_strlen($this->nationalNumber) >= self::$minLeadingDigitsLength) {
$this->getAvailableFormats($this->nationalNumber);
// See if the accrued digits can be formatted properly already.
$formattedNumber = $this->attemptToFormatAccruedDigits();
if ($formattedNumber !== '') {
return $formattedNumber;
}
return $this->maybeCreateNewTemplate() ? $this->inputAccruedNationalNumber() : $this->accruedInput;
}
return $this->appendNationalNumber($this->nationalNumber);
}
/**
* Invokes inputDigitHelper on each digit of the national number accrued, and returns a formatted
* string in the end
* @return string
*/
private function inputAccruedNationalNumber()
{
$lengthOfNationalNumber = \mb_strlen($this->nationalNumber);
if ($lengthOfNationalNumber > 0) {
$tempNationalNumber = '';
for ($i = 0; $i < $lengthOfNationalNumber; $i++) {
$tempNationalNumber = $this->inputDigitHelper(\mb_substr($this->nationalNumber, $i, 1));
}
return $this->ableToFormat ? $this->appendNationalNumber($tempNationalNumber) : $this->accruedInput;
}
return $this->prefixBeforeNationalNumber;
}
/**
* Returns true if the current country is a NANPA country and the national number beings with
* the national prefix
* @return bool
*/
private function isNanpaNumberWithNationalPrefix()
{
// For NANPA numbers beginning with 1[2-9], treat the 1 as the national prefix. The reason is
// that national significant numbers in NANPA always start with [2-9] after the national prefix.
// Numbers beginning with 1[01] can only be short/emergency numbers, which don't need the
// national prefix.
return ($this->currentMetadata->getCountryCode() == 1) && (\mb_substr($this->nationalNumber, 0, 1) == '1')
&& (\mb_substr($this->nationalNumber, 1, 1) != '0') && (\mb_substr($this->nationalNumber, 1, 1) != '1');
}
/**
* Returns the national prefix extracted, or an empty string if it is not present.
* @return string
*/
private function removeNationalPrefixFromNationalNumber()
{
$startOfNationalNumber = 0;
if ($this->isNanpaNumberWithNationalPrefix()) {
$startOfNationalNumber = 1;
$this->prefixBeforeNationalNumber .= '1' . self::$seperatorBeforeNationalNumber;
$this->isCompleteNumber = true;
} elseif ($this->currentMetadata->hasNationalPrefixForParsing()) {
$m = new Matcher($this->currentMetadata->getNationalPrefixForParsing(), $this->nationalNumber);
// Since some national prefix patterns are entirely optional, check that a national prefix
// could actually be extracted.
if ($m->lookingAt() && $m->end() > 0) {
// When the national prefix is detected, we use international formatting rules instead of
// national ones, because national formatting rules could contain local formatting rules
// for numbers entered without area code.
$this->isCompleteNumber = true;
$startOfNationalNumber = $m->end();
$this->prefixBeforeNationalNumber .= \mb_substr($this->nationalNumber, 0, $startOfNationalNumber);
}
}
$nationalPrefix = \mb_substr($this->nationalNumber, 0, $startOfNationalNumber);
$this->nationalNumber = \mb_substr($this->nationalNumber, $startOfNationalNumber);
return $nationalPrefix;
}
/**
* Extracts IDD and plus sign to $this->prefixBeforeNationalNumber when they are available, and places
* the remaining input into $this->nationalNumber.
* @return bool true when $this->accruedInputWithoutFormatting begins with the plus sign or valid IDD
* for $this->defaultCountry.
*/
private function attemptToExtractIdd()
{
$internationalPrefix = "\\" . PhoneNumberUtil::PLUS_SIGN . '|' . $this->currentMetadata->getInternationalPrefix();
$iddMatcher = new Matcher($internationalPrefix, $this->accruedInputWithoutFormatting);
if ($iddMatcher->lookingAt()) {
$this->isCompleteNumber = true;
$startOfCountryCallingCode = $iddMatcher->end();
$this->nationalNumber = \mb_substr($this->accruedInputWithoutFormatting, $startOfCountryCallingCode);
$this->prefixBeforeNationalNumber = \mb_substr($this->accruedInputWithoutFormatting, 0, $startOfCountryCallingCode);
if (\mb_substr($this->accruedInputWithoutFormatting, 0, 1) != PhoneNumberUtil::PLUS_SIGN) {
$this->prefixBeforeNationalNumber .= self::$seperatorBeforeNationalNumber;
}
return true;
}
return false;
}
/**
* Extracts the country calling code from the beginning of $this->nationalNumber to
* $this->prefixBeforeNationalNumber when they are available, and places the remaining input
* into $this->>nationalNumber.
* @return bool true when a valid country calling code can be found
*/
private function attemptToExtractCountryCallingCode()
{
if ($this->nationalNumber === '') {
return false;
}
$numberWithoutCountryCallingCode = '';
$countryCode = $this->phoneUtil->extractCountryCode($this->nationalNumber, $numberWithoutCountryCallingCode);
if ($countryCode === 0) {
return false;
}
$this->nationalNumber = $numberWithoutCountryCallingCode;
$newRegionCode = $this->phoneUtil->getRegionCodeForCountryCode($countryCode);
if (PhoneNumberUtil::REGION_CODE_FOR_NON_GEO_ENTITY == $newRegionCode) {
$this->currentMetadata = $this->phoneUtil->getMetadataForNonGeographicalRegion($countryCode);
} elseif ($newRegionCode != $this->defaultCountry) {
$this->currentMetadata = $this->getMetadataForRegion($newRegionCode);
}
$countryCodeString = (string)$countryCode;
$this->prefixBeforeNationalNumber .= $countryCodeString . self::$seperatorBeforeNationalNumber;
// When we have successfully extracted the IDD, the previously extracted NDD should be cleared
// because it is no longer valid.
$this->extractedNationalPrefix = '';
return true;
}
/**
* Accrues digits and the plus sign to $this->accruedInputWithoutFormatting for later use. If
* $nextChar contains a digit in non-ASCII format (e.g. the full-width version of digits), it
* is first normalized to the ASCII version. The return value is $nextChar itself, or its
* normalized version, if $nextChar is a digit in non-ASCII format. This method assumes its
* input is either a digit or the plus sign.
* @param string $nextChar
* @param bool $rememberPosition
* @return string
*/
private function normalizeAndAccrueDigitsAndPlusSign($nextChar, $rememberPosition)
{
if ($nextChar == PhoneNumberUtil::PLUS_SIGN) {
$normalizedChar = $nextChar;
$this->accruedInputWithoutFormatting .= $nextChar;
} else {
$normalizedChar = PhoneNumberUtil::normalizeDigits($nextChar, false);
$this->accruedInputWithoutFormatting .= $normalizedChar;
$this->nationalNumber .= $normalizedChar;
}
if ($rememberPosition) {
$this->positionToRemember = \mb_strlen($this->accruedInputWithoutFormatting);
}
return $normalizedChar;
}
/**
* @param string $nextChar
* @return string
*/
private function inputDigitHelper($nextChar)
{
// Note that formattingTemplate is not guaranteed to have a value, it could be empty, e.g.
// when the next digit is entered after extracting an IDD or NDD.
$digitMatcher = new Matcher(self::$digitPattern, $this->formattingTemplate);
if ($digitMatcher->find($this->lastMatchPosition)) {
$tempTemplate = $digitMatcher->replaceFirst($nextChar);
$this->formattingTemplate = $tempTemplate . \mb_substr($this->formattingTemplate, \mb_strlen(
$tempTemplate,
'UTF-8'
), null, 'UTF-8');
$this->lastMatchPosition = $digitMatcher->start();
return \mb_substr($this->formattingTemplate, 0, $this->lastMatchPosition + 1);
}
if (\count($this->possibleFormats) === 1) {
// More digits are entered than we could handle, and there are no other valid patterns to
// try.
$this->ableToFormat = false;
} // else, we just reset the formatting pattern.
$this->currentFormattingPattern = '';
return $this->accruedInput;
}
}

View File

@@ -0,0 +1,34 @@
<?php
namespace libphonenumber;
/**
* Country code source from number
*/
class CountryCodeSource
{
/**
* The country_code is derived based on a phone number with a leading "+", e.g. the French
* number "+33 1 42 68 53 00".
*/
const FROM_NUMBER_WITH_PLUS_SIGN = 0;
/**
* The country_code is derived based on a phone number with a leading IDD, e.g. the French
* number "011 33 1 42 68 53 00", as it is dialled from US.
*/
const FROM_NUMBER_WITH_IDD = 1;
/**
* The country_code is derived based on a phone number without a leading "+", e.g. the French
* number "33 1 42 68 53 00" when defaultCountry is supplied as France.
*/
const FROM_NUMBER_WITHOUT_PLUS_SIGN = 2;
/**
* The country_code is derived NOT based on the phone number itself, but from the defaultCountry
* parameter provided in the parsing function by the clients. This happens mostly for numbers
* written in the national format (without country code). For example, this would be set when
* parsing the French number "01 42 68 53 00", when defaultCountry is supplied as France.
*/
const FROM_DEFAULT_COUNTRY = 3;
const UNSPECIFIED = 4;
}

Some files were not shown because too many files have changed in this diff Show More