├── .ddev
└── config.yaml
├── .gitignore
├── .scrutinizer.yml
├── .travis.yml
├── LICENSE.md
├── README.md
├── composer.json
├── ecs.php
├── examples
└── simple-email-form
│ ├── controller.php
│ └── index.php
├── phpunit.xml.dist
├── rector.php
├── src
├── Cookie.php
├── HttpHeader.php
├── Mautic.php
└── Mautic
│ ├── Config.php
│ ├── Contact.php
│ ├── Cookie.php
│ └── Form.php
└── tests
├── CookieTest.php
├── HttpHeaderTest.php
├── Mautic
├── ContactTest.php
├── CookieTest.php
└── FormTest.php
└── MauticTest.php
/.ddev/config.yaml:
--------------------------------------------------------------------------------
1 | name: mautic-form-submit
2 | type: php
3 | docroot: "examples/simple-email-form/"
4 | php_version: "8.1"
5 | webserver_type: nginx-fpm
6 | xdebug_enabled: false
7 | additional_hostnames: []
8 | additional_fqdns: []
9 | use_dns_when_possible: true
10 | composer_version: "2"
11 | web_environment: []
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | build
2 | composer.lock
3 | vendor
4 | .idea
5 | .phpunit.result.cache
--------------------------------------------------------------------------------
/.scrutinizer.yml:
--------------------------------------------------------------------------------
1 | filter:
2 | excluded_paths: [tests/*]
3 |
4 | checks:
5 | php:
6 | remove_extra_empty_lines: true
7 | remove_php_closing_tag: true
8 | remove_trailing_whitespace: true
9 | fix_use_statements:
10 | remove_unused: true
11 | preserve_multiple: false
12 | preserve_blanklines: true
13 | order_alphabetically: true
14 | fix_php_opening_tag: true
15 | fix_linefeed: true
16 | fix_line_ending: true
17 | fix_identation_4spaces: true
18 | fix_doc_comments: true
19 |
20 | tools:
21 | external_code_coverage:
22 | timeout: 600
23 | runs: 2
24 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: php
2 |
3 | php:
4 | - 5.6
5 | - 7.0
6 | - 7.1
7 |
8 | # This triggers builds to run on the new TravisCI infrastructure.
9 | # See: http://docs.travis-ci.com/user/workers/container-based-infrastructure/
10 | sudo: false
11 |
12 | cache:
13 | directories:
14 | - $HOME/.composer/cache
15 |
16 | before_install:
17 | # turn off XDebug
18 | - phpenv config-rm xdebug.ini || return
19 |
20 | # install dependencies in parallel
21 | - travis_retry composer global require hirak/prestissimo
22 |
23 | # install PHPSTAN globally for PHP 7+
24 | - if [[ $TRAVIS_PHP_VERSION != '5.6' ]]; then composer global require phpstan/phpstan-shim; fi
25 |
26 | before_script:
27 | - travis_retry composer self-update
28 | - travis_retry composer install --no-interaction
29 | - COMPOSER_PROCESS_TIMEOUT=0 composer test-server > /dev/null 2>&1 &
30 |
31 | script:
32 | # Run code standards analysis
33 | - vendor/bin/phpcs --standard=psr2 src/
34 |
35 | # Run PHPUnit with test coverage report
36 | - vendor/bin/phpunit --coverage-text --coverage-clover=coverage.clover
37 |
38 | # Run PHPSTAN analysis for PHP 7+
39 | - if [[ $TRAVIS_PHP_VERSION != '5.6' ]]; then ~/.composer/vendor/phpstan/phpstan-shim/phpstan.phar analyse src tests -l 5; fi
40 |
41 | after_script:
42 | - if [[ $TRAVIS_PHP_VERSION != '7.1' ]]; then php vendor/bin/ocular code-coverage:upload --format=php-clover coverage.clover; fi
43 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | # The MIT License (MIT)
2 |
3 | Copyright (c) 2017, Matthew Allan (matthew.james.allan@gmail.com) and the JSON Reference contributors
4 |
5 | > Permission is hereby granted, free of charge, to any person obtaining a copy
6 | > of this software and associated documentation files (the "Software"), to deal
7 | > in the Software without restriction, including without limitation the rights
8 | > to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | > copies of the Software, and to permit persons to whom the Software is
10 | > furnished to do so, subject to the following conditions:
11 | >
12 | > The above copyright notice and this permission notice shall be included in
13 | > all copies or substantial portions of the Software.
14 | >
15 | > THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | > IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | > FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | > AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | > LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | > OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | > THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # PHP library for submitting Mautic Form from 3rd party app
2 |
3 | [](https://scrutinizer-ci.com/g/escopecz/mautic-form-submit/?branch=master)
4 | [](https://scrutinizer-ci.com/g/escopecz/mautic-form-submit/build-status/master)
5 | [](https://scrutinizer-ci.com/g/escopecz/mautic-form-submit/?branch=master)
6 |
7 | Submitting a form can get handy if you want to process the data with your app, but you want to send them to Mautic too. Mautic can then run automated tasks triggered by the form submission. [Read more about it](https://medium.com/@jan_linhart/the-simplest-way-how-to-submit-a-form-data-to-mautic-1454d3afd005) in the original post.
8 |
9 | Since the new Mautic versions prefer cookie tracking over IP tracking which makes more tedious to submit the form as the tracked contact, this library will take care of the cookie sending via CURL. It will also listen the cookie from the response and updates the contact cookie with the values from the submit response. This way if the contact ID changes because of contact merge, the contact will continue browsing under the new contact ID.
10 |
11 | The automatic cookie handling requires that your form will be on a page tracked by the Mautic JS tracking which provides the Mautic contact cookie in the first place.
12 |
13 | ## Install
14 |
15 | ### Via Composer
16 |
17 | ```bash
18 | composer require escopecz/mautic-form-submit
19 | ```
20 |
21 | ## Usage
22 |
23 | ```php
24 | // Require Composer autoloader
25 | require __DIR__.'/vendor/autoload.php';
26 |
27 | // Define the namespace of the Mautic object
28 | use Escopecz\MauticFormSubmit\Mautic;
29 |
30 | // Define the namespace of the Mautic configuration object
31 | use Escopecz\MauticFormSubmit\Mautic\Config;
32 |
33 | // It's optional to declare the configuration object to change some default values.
34 | // For example to disable Curl verbose logging.
35 | $config = new Config;
36 | $config->setCurlVerbose(true);
37 |
38 | // Instantiate the Mautic object with the base URL where the Mautic runs
39 | $mautic = new Mautic('https://mymautic.com');
40 |
41 | // Create a new instance of the Form object with the form ID 342
42 | $form = $mautic->getForm(342);
43 |
44 | // Submit provided data array to the form 342
45 | $result = $form->submit(['f_email' => 'john@doe.email']);
46 | ```
47 |
48 | - The integer passed to the `getForm()` method must be ID of the Mautic form.
49 | - The array passed to the `submit()` method must be associative array of `['mautic_field_alias' => 'the_value']`.
50 |
51 | For working example see the `examples` dir.
52 |
53 | ## Run project
54 | ```
55 | ddev start
56 | ```
57 | Project url: https://mautic-form-submit.ddev.site/
58 |
59 | ## Testing
60 |
61 | ```
62 | composer test
63 | composer cs
64 | composer phpstan
65 | ```
66 |
67 | ### Current status
68 |
69 | [Travis](https://travis-ci.org/escopecz/mautic-form-submit)
70 | [Scrutinizer](https://scrutinizer-ci.com/g/escopecz/mautic-form-submit)
71 |
72 | ## License
73 |
74 | The MIT License (MIT). Please see [License File](LICENSE.md) for more information.
75 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "escopecz/mautic-form-submit",
3 | "type": "library",
4 | "description": "A library for submitting Mautic form from a 3rd pary PHP app",
5 | "keywords": [
6 | "mautic"
7 | ],
8 | "license": "MIT",
9 | "authors": [
10 | {
11 | "name": "John Linhart",
12 | "email": "john.linhart@mautic.org",
13 | "homepage": "https://mautic.org",
14 | "role": "Developer"
15 | }
16 | ],
17 | "require": {
18 | "php": ">=8.1",
19 | "ext-curl": "*"
20 | },
21 | "require-dev": {
22 | "phpunit/phpunit" : "^10.5",
23 | "scrutinizer/ocular": "~1.9",
24 | "rector/rector": "^1.2",
25 | "phpstan/phpstan": "^1.11",
26 | "symplify/easy-coding-standard": "^12.3"
27 | },
28 | "autoload": {
29 | "psr-4": {
30 | "Escopecz\\MauticFormSubmit\\": "src"
31 | }
32 | },
33 | "autoload-dev": {
34 | "psr-4": {
35 | "Escopecz\\MauticFormSubmit\\Test\\": "tests"
36 | }
37 | },
38 | "scripts": {
39 | "test": "phpunit",
40 | "test-coverage": "phpdbg -qrr vendor/bin/phpunit",
41 | "cs": "vendor/bin/ecs --fix",
42 | "phpstan": "vendor/bin/phpstan analyse src tests -l 5"
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/ecs.php:
--------------------------------------------------------------------------------
1 | withPaths([
12 | __DIR__ . '/examples',
13 | __DIR__ . '/src',
14 | __DIR__ . '/tests',
15 | ])
16 | ->withRootFiles()
17 | ->withConfiguredRule(
18 | ArraySyntaxFixer::class,
19 | ['syntax' => 'short']
20 | )
21 | ->withRules([
22 | NoUnusedImportsFixer::class,
23 | ListSyntaxFixer::class,
24 | ]);
25 |
--------------------------------------------------------------------------------
/examples/simple-email-form/controller.php:
--------------------------------------------------------------------------------
1 | $val) {
14 | $_SESSION[$key] = $val;
15 | }
16 |
17 | if (isset($_POST['email_label']) && isset($_POST[$_POST['email_label']]) && isset($_POST['mautic_base_url']) && isset($_POST['form_id'])) {
18 |
19 | // It's optional to create a Config DTO object and pass it to the Mautic object.
20 | // For example to set Curl verbose logging to true.
21 | $config = new Config;
22 | $config->setCurlVerbose(true);
23 |
24 | $mautic = new Mautic($_POST['mautic_base_url'], $config);
25 | $form = $mautic->getForm($_POST['form_id']);
26 |
27 | $info = $form->submit(
28 | [
29 | $_POST['email_label'] => $_POST[$_POST['email_label']],
30 | ]
31 | );
32 |
33 | $_SESSION['info'] = $info;
34 | }
35 |
36 | header(rtrim('Location: http://'.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'], '/controller.php'));
37 | die();
38 | }
39 | }
40 |
41 | session_start();
42 | new Controller;
43 |
--------------------------------------------------------------------------------
/examples/simple-email-form/index.php:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
9 |
10 |
11 | Simple Email Form Submit
12 |
13 |
14 |
15 | Create a Mautic Form with Email field (must have f_email
label). Fill in its ID below. The JS tracking will start working on this page then. The cookie will be populated with Mautic Contact ID which will be used to send the submission.
16 |
44 | Last Response:
45 |
46 |
47 |
48 |
49 |
57 |
58 |
59 |
60 |
--------------------------------------------------------------------------------
/phpunit.xml.dist:
--------------------------------------------------------------------------------
1 |
2 |
12 |
13 |
14 | tests
15 |
16 |
17 |
18 |
19 | src/
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/rector.php:
--------------------------------------------------------------------------------
1 | paths([
11 | __DIR__ . '/src',
12 | __DIR__ . '/tests',
13 | ]);
14 |
15 | $rectorConfig->sets([
16 | LevelSetList::UP_TO_PHP_81,
17 | SetList::DEAD_CODE,
18 | SetList::CODE_QUALITY,
19 | SetList::NAMING,
20 | SetList::TYPE_DECLARATION,
21 | ]);
22 | };
--------------------------------------------------------------------------------
/src/Cookie.php:
--------------------------------------------------------------------------------
1 | store[$key])) {
26 | return filter_var($this->store[$key], FILTER_SANITIZE_SPECIAL_CHARS);
27 | }
28 |
29 | return filter_input(INPUT_COOKIE, $key, FILTER_SANITIZE_SPECIAL_CHARS);
30 | }
31 |
32 | /**
33 | * Get cookie with FILTER_SANITIZE_NUMBER_INT
34 | */
35 | public function getInt(string $key): int
36 | {
37 | if (isset($this->store[$key])) {
38 | return (int) filter_var($this->store[$key], FILTER_SANITIZE_NUMBER_INT);
39 | }
40 |
41 | return (int) filter_input(INPUT_COOKIE, $key, FILTER_SANITIZE_NUMBER_INT);
42 | }
43 |
44 | public function set(string $key, mixed $value): bool
45 | {
46 | $this->store[$key] = $value;
47 |
48 | return setcookie($key, (string) $value);
49 | }
50 |
51 | public function clear(string $key): static
52 | {
53 | setcookie($key, '', ['expires' => time() - 3600]);
54 | unset($_COOKIE[$key]);
55 | unset($this->store[$key]);
56 |
57 | return $this;
58 | }
59 |
60 | public function getSuperGlobalCookie(): array
61 | {
62 | return $_COOKIE;
63 | }
64 |
65 | /**
66 | * Return all cookies as array merged with current state
67 | */
68 | public function toArray(): array
69 | {
70 | return array_merge($this->getSuperGlobalCookie(), $this->store);
71 | }
72 |
73 | /**
74 | * Creates unique cookie file in system tmp dir and returns absolute path to it.
75 | */
76 | public function createCookieFile(): string|false
77 | {
78 | return tempnam(sys_get_temp_dir(), 'mauticcookie');
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/src/HttpHeader.php:
--------------------------------------------------------------------------------
1 | parse($textHeaders);
16 | }
17 |
18 | public function getHeaderValue(string $key): ?string
19 | {
20 | return $this->headers[$key] ?? null;
21 | }
22 |
23 | public function getCookieValue(?string $key): ?string
24 | {
25 | return $this->cookies[$key] ?? null;
26 | }
27 |
28 | /**
29 | * Parse text headers and fills in cookies and headers properites
30 | */
31 | private function parse(string $headers): void
32 | {
33 | foreach (preg_split('/\r\n|\r|\n/', $headers) as $i => $line) {
34 | if ($i === 0) {
35 | $this->headers['http_code'] = $line;
36 | } else {
37 | [$key, $value] = explode(': ', $line);
38 |
39 | if ($key === 'Set-Cookie') {
40 | [$textCookie] = explode(';', $value);
41 | [$cookieKey, $cookieValue] = explode('=', $textCookie);
42 |
43 | $this->cookies[$cookieKey] = $cookieValue;
44 | } else {
45 | $this->headers[$key] = $value;
46 | }
47 | }
48 | }
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/src/Mautic.php:
--------------------------------------------------------------------------------
1 | baseUrl = rtrim(trim($baseUrl), '/');
28 | $this->cookie = new MauticCookie;
29 | $this->contact = new Contact($this->cookie);
30 | $this->config = $config ?: new Config;
31 | }
32 |
33 | public function getBaseUrl(): string
34 | {
35 | return $this->baseUrl;
36 | }
37 |
38 | public function getForm(int $id): Form
39 | {
40 | return new Form($this, $id);
41 | }
42 |
43 | public function setContact(Contact $contact): static
44 | {
45 | $this->contact = $contact;
46 |
47 | return $this;
48 | }
49 |
50 | public function getContact(): Contact
51 | {
52 | return $this->contact;
53 | }
54 |
55 | public function getCookie(): MauticCookie
56 | {
57 | return $this->cookie;
58 | }
59 |
60 | public function getConfig(): Config
61 | {
62 | return $this->config;
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/src/Mautic/Config.php:
--------------------------------------------------------------------------------
1 | curlVerbose;
23 | }
24 |
25 | /**
26 | * Set Curl verbose logging option
27 | */
28 | public function setCurlVerbose(bool $curlVerbose): static
29 | {
30 | $this->curlVerbose = $curlVerbose;
31 |
32 | return $this;
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/src/Mautic/Contact.php:
--------------------------------------------------------------------------------
1 | ip = $this->getIpFromServer();
21 | }
22 |
23 | /**
24 | * Returns Contact ID
25 | */
26 | public function getId(): int
27 | {
28 | return (int) $this->cookie->getContactId();
29 | }
30 |
31 | /**
32 | * Set Mautic Contact ID to global cookie
33 | */
34 | public function setId(int $contactId): static
35 | {
36 | $this->cookie->setContactId($contactId);
37 |
38 | return $this;
39 | }
40 |
41 | public function getIp(): ?string
42 | {
43 | return $this->ip;
44 | }
45 |
46 | public function setIp(string $ip): void
47 | {
48 | $this->ip = $ip;
49 | }
50 |
51 | public function getSessionId(): ?string
52 | {
53 | return $this->cookie->getSessionId();
54 | }
55 |
56 |
57 | public function setSessionId(string $sessionId): static
58 | {
59 | $this->cookie->setSessionId($sessionId);
60 |
61 | return $this;
62 | }
63 |
64 | public function setDeviceId(string $deviceId): static
65 | {
66 | $this->cookie->setDeviceId($deviceId);
67 |
68 | return $this;
69 | }
70 |
71 | /**
72 | * Guesses IP address from $_SERVER
73 | */
74 | public function getIpFromServer(): string
75 | {
76 | $ip = '';
77 | $ipHolders = [
78 | 'HTTP_CLIENT_IP',
79 | 'HTTP_X_FORWARDED_FOR',
80 | 'HTTP_X_FORWARDED',
81 | 'HTTP_X_CLUSTER_CLIENT_IP',
82 | 'HTTP_FORWARDED_FOR',
83 | 'HTTP_FORWARDED',
84 | 'REMOTE_ADDR'
85 | ];
86 |
87 | foreach ($ipHolders as $ipHolder) {
88 | if (!empty($_SERVER[$ipHolder])) {
89 | $ip = $_SERVER[$ipHolder];
90 | if (str_contains((string) $ip, ',')) {
91 | // Multiple IPs are present so use the last IP which should be
92 | // the most reliable IP that last connected to the proxy
93 | $ips = explode(',', (string) $ip);
94 | $ips = array_map('trim', $ips);
95 | $ip = end($ips);
96 | }
97 | $ip = trim((string) $ip);
98 | break;
99 | }
100 | }
101 |
102 | return $ip;
103 | }
104 | }
105 |
--------------------------------------------------------------------------------
/src/Mautic/Cookie.php:
--------------------------------------------------------------------------------
1 | getInt(self::MTC_ID)) !== 0) {
40 | return $mtcId;
41 | } elseif ($mauticSessionId = $this->getSessionId()) {
42 | return $this->getInt($mauticSessionId);
43 | }
44 |
45 | return null;
46 | }
47 |
48 | /**
49 | * Set Mautic Contact ID cookies
50 | * Note: Call setMauticSessionId prior to this
51 | */
52 | public function setContactId(int $contactId): static
53 | {
54 | $this->set(self::MTC_ID, $contactId);
55 |
56 | if ($sessionId = $this->getSessionId()) {
57 | $this->set($sessionId, $contactId);
58 | }
59 |
60 | return $this;
61 | }
62 |
63 | /**
64 | * Unit Mautic Contact ID cookies
65 | */
66 | public function unsetContactId(): static
67 | {
68 | $this->clear(self::MTC_ID);
69 |
70 | if ($sessionId = $this->getSessionId()) {
71 | $this->clear($sessionId);
72 | }
73 |
74 | return $this;
75 | }
76 |
77 | /**
78 | * Returns Mautic session ID if it exists in the cookie
79 | */
80 | public function getSessionId(): ?string
81 | {
82 | if ($mauticSessionId = $this->get(self::MAUTIC_SESSION_ID)) {
83 | return $mauticSessionId;
84 | }
85 |
86 | if ($mauticSessionId = $this->get(self::MTC_SID)) {
87 | return $mauticSessionId;
88 | }
89 |
90 | return null;
91 | }
92 |
93 | /**
94 | * Set Mautic Session ID cookies
95 | */
96 | public function setSessionId(string $sessionId): static
97 | {
98 | $this->set(self::MAUTIC_SESSION_ID, $sessionId);
99 | $this->set(self::MTC_SID, $sessionId);
100 |
101 | return $this;
102 | }
103 |
104 | /**
105 | * Set Mautic Device ID cookies
106 | */
107 | public function setDeviceId(string $deviceId): static
108 | {
109 | $this->set(self::MAUTIC_DEVICE_ID, $deviceId);
110 |
111 | return $this;
112 | }
113 |
114 | /**
115 | * Unset Mautic Session ID cookies
116 | */
117 | public function unsetSessionId(): static
118 | {
119 | $this->clear(self::MAUTIC_SESSION_ID);
120 | $this->clear(self::MTC_SID);
121 |
122 | return $this;
123 | }
124 | }
125 |
--------------------------------------------------------------------------------
/src/Mautic/Form.php:
--------------------------------------------------------------------------------
1 | mautic->getCookie()->getSuperGlobalCookie();
27 | $request = $this->prepareRequest($data);
28 |
29 | $ch = curl_init($request['url']);
30 | curl_setopt($ch, CURLOPT_POST, 1);
31 | curl_setopt($ch, CURLOPT_POSTFIELDS, $request['query']);
32 |
33 | if (isset($request['header'])) {
34 | curl_setopt($ch, CURLOPT_HTTPHEADER, $request['header']);
35 | }
36 |
37 | if (isset($request['referer'])) {
38 | curl_setopt($ch, CURLOPT_REFERER, $request['referer']);
39 | }
40 |
41 | if (isset($request['cookie'])) {
42 | curl_setopt($ch, CURLOPT_COOKIEFILE, $this->mautic->getCookie()->createCookieFile());
43 | }
44 |
45 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
46 | curl_setopt($ch, CURLOPT_VERBOSE, $this->mautic->getConfig()->getCurlVerbose());
47 | curl_setopt($ch, CURLOPT_HEADER, 1);
48 |
49 | foreach ($curlOpts as $key => $value) {
50 | curl_setopt($ch, $key, $value);
51 | }
52 |
53 | $result = curl_exec($ch);
54 | $response = $this->prepareResponse($result);
55 | $response['info'] = curl_getinfo($ch);
56 | curl_close($ch);
57 |
58 | $contact = $this->mautic->getContact();
59 | $httpHeader = new HttpHeader($response['header']);
60 | $sessionId = $httpHeader->getCookieValue(Cookie::MAUTIC_SESSION_ID);
61 | $deviceId = $httpHeader->getCookieValue(Cookie::MAUTIC_DEVICE_ID);
62 | $contactId = $httpHeader->getCookieValue($sessionId);
63 |
64 | if ($sessionId) {
65 | $contact->setSessionId($sessionId);
66 | }
67 |
68 | if ($deviceId) {
69 | $contact->setDeviceId($deviceId);
70 | }
71 |
72 | if ($contactId) {
73 | $contact->setId((int)$contactId);
74 | }
75 |
76 | return [
77 | 'original_cookie' => $originalCookie,
78 | 'new_cookie' => $this->mautic->getCookie()->toArray(),
79 | 'request' => $request,
80 | 'response' => $response,
81 | ];
82 | }
83 |
84 | /**
85 | * Prepares data for CURL request based on provided form data, $_COOKIE and $_SERVER
86 | */
87 | public function prepareRequest(array $data): array
88 | {
89 | $contact = $this->mautic->getContact();
90 | $request = ['header' => []];
91 |
92 | $data['formId'] = $this->id;
93 |
94 | // return has to be part of the form data array so Mautic would accept the submission
95 | if (!isset($data['return'])) {
96 | $data['return'] = '';
97 | }
98 |
99 | $request['url'] = $this->getUrl();
100 | $request['data'] = ['mauticform' => $data];
101 |
102 | if ($contactId = $contact->getId()) {
103 | $request['data']['mtc_id'] = $contactId;
104 | }
105 |
106 | if ($contactIp = $contact->getIp()) {
107 | $request['header'][] = "X-Forwarded-For: $contactIp";
108 | $request['header'][] = "Client-Ip: $contactIp";
109 | }
110 |
111 | if ($sessionId = $contact->getSessionId()) {
112 | $request['header'][] = "Cookie: mautic_session_id=$sessionId";
113 | $request['header'][] = "Cookie: mautic_device_id=$sessionId";
114 | }
115 |
116 | if (isset($_SERVER['HTTP_REFERER'])) {
117 | $request['referer'] = $_SERVER["HTTP_REFERER"];
118 | }
119 |
120 | $request['query'] = http_build_query($request['data']);
121 |
122 | return $request;
123 | }
124 |
125 | /**
126 | * Process the result and split into headers and content
127 | */
128 | public function prepareResponse(string|bool $result): array
129 | {
130 | $response = ['header' => null, 'content' => null];
131 | $d = "\r\n\r\n"; // Headers and content delimiter
132 |
133 | if (is_string($result) && str_contains($result, $d)) {
134 | [$header, $content] = explode($d, $result, 2);
135 | if (stripos($header, '100 Continue') !== false && str_contains($content, $d)) {
136 | [$header, $content] = explode($d, $content, 2);
137 | }
138 | $response['header'] = $header;
139 | $response['content'] = htmlentities($content);
140 | }
141 |
142 | return $response;
143 | }
144 |
145 | /**
146 | * Builds the form URL
147 | */
148 | public function getUrl(): string
149 | {
150 | return sprintf('%s/form/submit?formId=%d', $this->mautic->getBaseUrl(), $this->id);
151 | }
152 |
153 | public function getId(): int
154 | {
155 | return $this->id;
156 | }
157 | }
158 |
--------------------------------------------------------------------------------
/tests/CookieTest.php:
--------------------------------------------------------------------------------
1 | assertNull($cookie->get($key));
22 |
23 | $val = 452423;
24 | $cookie->set($key, $val);
25 |
26 | $this->assertSame((string) $val, $cookie->get($key));
27 | $this->assertSame($val, $cookie->getInt($key));
28 |
29 | $cookie->clear($key);
30 |
31 | $this->assertSame(0, $cookie->getInt($key));
32 | }
33 |
34 | /**
35 | * @runInSeparateProcess
36 | */
37 | function test_super_global_cookie(): void
38 | {
39 | $cookie = new Cookie();
40 |
41 | $this->assertTrue(is_array($cookie->getSuperGlobalCookie()));
42 | }
43 |
44 | /**
45 | * @runInSeparateProcess
46 | */
47 | function test_to_array(): void
48 | {
49 | $cookie = new Cookie();
50 | $key = 'some_cookie';
51 | $val = 452423;
52 | $cookie->set($key, $val);
53 |
54 | $this->assertSame([$key => $val], $cookie->toArray());
55 | }
56 |
57 | function test_get_cookie_file(): void
58 | {
59 | $cookie = new Cookie;
60 | $file = $cookie->createCookieFile();
61 |
62 | $this->assertTrue(is_string($file));
63 | $this->assertTrue(file_exists($file));
64 | $this->assertTrue(is_writable($file));
65 | $this->assertTrue(unlink($file));
66 | }
67 | }
--------------------------------------------------------------------------------
/tests/HttpHeaderTest.php:
--------------------------------------------------------------------------------
1 | testTextHeader);
34 | $this->assertEquals('6txmz3mu2dslmkhrera668e', $httpHeader->getCookieValue('mautic_session_id'));
35 | $this->assertEquals('18061', $httpHeader->getCookieValue('mtc_id'));
36 | $this->assertEquals('18061', $httpHeader->getCookieValue('6txmz3mu2dslmkhrera668e'));
37 | $this->assertEquals('Apache/2.4.33 (Unix) OpenSSL/1.0.2o PHP/7.1.16', $httpHeader->getHeaderValue('Server'));
38 | }
39 | }
--------------------------------------------------------------------------------
/tests/Mautic/ContactTest.php:
--------------------------------------------------------------------------------
1 | baseUrl);
23 | $contact = $mautic->getContact();
24 |
25 | $this->assertInstanceOf(Contact::class, $contact);
26 | $this->assertSame(0, $contact->getId());
27 | $this->assertSame('', $contact->getIp());
28 | }
29 |
30 | /**
31 | * @runInSeparateProcess
32 | */
33 | function test_set_get_id(): void
34 | {
35 | $contactId = 452;
36 | $mautic = new Mautic($this->baseUrl);
37 | $contact = $mautic->getContact();
38 | $contact->setId($contactId);
39 |
40 | $this->assertSame($contactId, $contact->getId());
41 | }
42 |
43 | function test_set_get_ip(): void
44 | {
45 | $ip = '345.2.2.2';
46 | $mautic = new Mautic($this->baseUrl);
47 | $contact = $mautic->getContact();
48 | $contact->setIp($ip);
49 |
50 | $this->assertSame($ip, $contact->getIp());
51 | }
52 |
53 | /**
54 | * @runInSeparateProcess
55 | */
56 | function test_get_id_from_mtc_id_cookie(): void
57 | {
58 | $contactId = 4344;
59 | $cookie = new Cookie;
60 | $cookie->setContactId($contactId);
61 | $contact = new Contact($cookie);
62 |
63 | $this->assertSame($contactId, $contact->getId());
64 | $cookie->clear(Cookie::MTC_ID);
65 | }
66 |
67 | /**
68 | * @runInSeparateProcess
69 | */
70 | function test_get_id_from_mautic_session_id_cookie(): void
71 | {
72 | $contactId = 4344;
73 | $sessionId = 'slk3jhkn3gkn23lkgn3lkgn';
74 | $cookie = new Cookie;
75 | $cookie->setSessionId($sessionId)
76 | ->setContactId($contactId);
77 | $contact = new Contact($cookie);
78 |
79 | $this->assertEquals($contactId, $contact->getId());
80 | $cookie->unsetSessionId()
81 | ->unsetContactId();
82 | }
83 |
84 | function test_get_ip_from_server(): void
85 | {
86 | $contactIp = '345.2.2.2';
87 | $_SERVER['REMOTE_ADDR'] = $contactIp;
88 | $contact = new Contact(new Cookie);
89 |
90 | $this->assertSame($contactIp, $contact->getIp());
91 | unset($_SERVER['REMOTE_ADDR']);
92 | }
93 |
94 | function test_get_ip_from_server_method(): void
95 | {
96 | $contact = new Contact(new Cookie);
97 |
98 | $this->assertSame('', $contact->getIpFromServer());
99 |
100 | $contactIp = '345.2.2.2';
101 | $_SERVER['REMOTE_ADDR'] = $contactIp;
102 |
103 | $this->assertSame($contactIp, $contact->getIpFromServer());
104 | unset($_SERVER['REMOTE_ADDR']);
105 | }
106 |
107 | function test_get_ip_from_server_method_when_multiple_ips(): void
108 | {
109 | $contact = new Contact(new Cookie);
110 |
111 | $this->assertSame('', $contact->getIpFromServer());
112 |
113 | $_SERVER['REMOTE_ADDR'] = '222.333.444.4., 555.666.777.7, 345.2.2.2';
114 |
115 | // The last IP from the list is the right one
116 | $this->assertSame('345.2.2.2', $contact->getIpFromServer());
117 | unset($_SERVER['REMOTE_ADDR']);
118 | }
119 |
120 | /**
121 | * @runInSeparateProcess
122 | */
123 | function test_set_session_id_to_cookie(): void
124 | {
125 | $cookie = new Cookie;
126 | $contact = new Contact($cookie);
127 |
128 | $this->assertSame(null, $contact->getSessionId());
129 |
130 | $sessionId = 'sadfasfd98fuasofuasd9f87asfo';
131 | $contact->setSessionId($sessionId);
132 |
133 | $this->assertSame($sessionId, $contact->getSessionId());
134 | $cookie->unsetSessionId();
135 | }
136 |
137 | /**
138 | * @runInSeparateProcess
139 | */
140 | function test_set_contact_id_to_cookie(): void
141 | {
142 | $cookie = new Cookie;
143 | $contact = new Contact($cookie);
144 |
145 | $this->assertSame(0, $contact->getId());
146 |
147 | $contactId = 2332;
148 | $contact->setId($contactId);
149 |
150 | $this->assertSame($contactId, $contact->getId());
151 | $this->assertEquals($contactId, $cookie->getContactId());
152 | $cookie->unsetContactId();
153 | }
154 | }
--------------------------------------------------------------------------------
/tests/Mautic/CookieTest.php:
--------------------------------------------------------------------------------
1 | assertEquals(null, $cookie->getContactId());
20 |
21 | $contactId = 4344;
22 | $cookie->setContactId($contactId);
23 |
24 | $this->assertEquals($contactId, $cookie->getContactId());
25 | $cookie->unsetContactId();
26 |
27 | $this->assertEquals(null, $cookie->getContactId());
28 |
29 | }
30 |
31 | /**
32 | * @runInSeparateProcess
33 | */
34 | function test_set_get_unset_session_id(): void
35 | {
36 | $cookie = new Cookie;
37 |
38 | $this->assertSame(null, $cookie->getSessionId());
39 |
40 | $sid = 'kjsfk3j2jnfl2kj3rl2kj';
41 | $cookie->set(Cookie::MAUTIC_SESSION_ID, $sid);
42 |
43 | $this->assertSame($sid, $cookie->getSessionId());
44 | $cookie->clear(Cookie::MAUTIC_SESSION_ID);
45 |
46 | $this->assertSame(null, $cookie->getSessionId());
47 |
48 | $cookie->set(Cookie::MTC_SID, $sid);
49 | $this->assertSame($sid, $cookie->getSessionId());
50 |
51 | $cookie->clear(Cookie::MTC_SID);
52 | $this->assertSame(null, $cookie->getSessionId());
53 | }
54 |
55 | /**
56 | * @runInSeparateProcess
57 | */
58 | function test_set_get_unset_session_id2(): void
59 | {
60 | $cookie = new Cookie;
61 |
62 | $this->assertSame(null, $cookie->getSessionId());
63 |
64 | $sid = 'kjsfk3j2jnfl2kj3rl2kj';
65 | $cookie->setSessionId($sid);
66 |
67 | $this->assertSame($sid, $cookie->getSessionId());
68 |
69 | $cookie->unsetSessionId();
70 |
71 | $this->assertSame(null, $cookie->getSessionId());
72 |
73 | $cookie->set(Cookie::MTC_SID, $sid);
74 |
75 | $this->assertSame($sid, $cookie->getSessionId());
76 |
77 | $cookie->unsetSessionId();
78 |
79 | $this->assertSame(null, $cookie->getSessionId());
80 | }
81 | }
--------------------------------------------------------------------------------
/tests/Mautic/FormTest.php:
--------------------------------------------------------------------------------
1 | baseUrl);
19 | $formId = 3434;
20 | $form = new Form($mautic, $formId);
21 |
22 | $this->assertSame($formId, $form->getId());
23 | }
24 |
25 | function test_get_id_int_in_mautic_object(): void
26 | {
27 | $mautic = new Mautic($this->baseUrl);
28 | $formId = 3434;
29 | $form = $mautic->getForm($formId);
30 |
31 | $this->assertSame($formId, $form->getId());
32 | }
33 |
34 | function test_prepare_request(): void
35 | {
36 | $mautic = new Mautic($this->baseUrl);
37 | $formId = 3434;
38 | $form = new Form($mautic, $formId);
39 | $data = [
40 | 'email' => 'john@doe.email',
41 | 'first_name' => 'John',
42 | 'last_name' => 'Doe',
43 | ];
44 | $request = $form->prepareRequest($data);
45 |
46 | $this->assertSame($this->baseUrl.'/form/submit?formId='.$formId, $request['url']);
47 | $this->assertSame($data['email'], $request['data']['mauticform']['email']);
48 | $this->assertSame($data['first_name'], $request['data']['mauticform']['first_name']);
49 | $this->assertSame($data['last_name'], $request['data']['mauticform']['last_name']);
50 | $this->assertSame($formId, $request['data']['mauticform']['formId']);
51 | $this->assertSame('', $request['data']['mauticform']['return']);
52 | }
53 |
54 | /**
55 | * @dataProvider response_result_provider
56 | */
57 | function test_prepare_response($result, $expectedHeader, $expectedContentType): void
58 | {
59 | $mautic = new Mautic($this->baseUrl);
60 | $formId = 3434;
61 | $form = $mautic->getForm($formId);
62 |
63 | $response = $form->prepareResponse($result);
64 |
65 | $this->assertSame($expectedHeader, $response['header']);
66 | switch ($expectedContentType) {
67 | case 'string':
68 | $this->assertIsString($response['content']);
69 | break;
70 | case 'null':
71 | $this->assertNull($response['content']);
72 | break;
73 | default:
74 | throw new \InvalidArgumentException("Nieznany typ: $expectedContentType");
75 | }
76 | }
77 |
78 | static function response_result_provider(): array
79 | {
80 | $continue = "HTTP/1.1 100 Continue";
81 | $header = "HTTP/1.1 302 Found\r
82 | Date: Wed, 17 Apr 2019 11:41:44 GMT\r
83 | Content-Type: text/html; charset=UTF-8\r
84 | Location: ...";
85 | $content = '';
86 |
87 | $d = "\r\n\r\n"; // Delimiter between headers and content
88 |
89 | return [
90 | // Normal response: headers + content
91 | [$header . $d . $content, $header, 'string'],
92 |
93 | // Continue response: 100 Continue + headers + content
94 | [$continue . $d . $header . $d . $content, $header, 'string'],
95 |
96 | // cURL returning false because of failure to execute request
97 | [false, null, 'null'],
98 | ];
99 | }
100 |
101 | function test_get_url(): void
102 | {
103 | $mautic = new Mautic($this->baseUrl);
104 | $formId = 3434;
105 | $form = $mautic->getForm($formId);
106 |
107 | $this->assertSame($this->baseUrl.'/form/submit?formId='.$formId, $form->getUrl());
108 | }
109 | }
--------------------------------------------------------------------------------
/tests/MauticTest.php:
--------------------------------------------------------------------------------
1 | baseUrl);
20 | $this->assertSame($this->baseUrl, $mautic->getBaseUrl());
21 | }
22 |
23 | function test_get_form(): void
24 | {
25 | $mautic = new Mautic($this->baseUrl);
26 | $formId = 7;
27 | $form = $mautic->getForm($formId);
28 |
29 | $this->assertInstanceOf(Form::class, $form);
30 | $this->assertSame($formId, $form->getId());
31 | }
32 |
33 | /**
34 | * @runInSeparateProcess
35 | */
36 | function test_get_contact(): void
37 | {
38 | $mautic = new Mautic($this->baseUrl);
39 | $contact = $mautic->getContact();
40 |
41 | $this->assertInstanceOf(Contact::class, $contact);
42 | $this->assertSame(0, $contact->getId());
43 | $this->assertSame('', $contact->getIp());
44 | }
45 |
46 | /**
47 | * @runInSeparateProcess
48 | */
49 | function test_get_set_contact(): void
50 | {
51 | $mautic = new Mautic($this->baseUrl);
52 | $contactId = 4;
53 | $contactIp = '234.3.2.33';
54 | $contact = new Contact(new Cookie);
55 | $contact->setId($contactId)
56 | ->setIp($contactIp);
57 | $mautic->setContact($contact);
58 | $contactB = $mautic->getContact();
59 |
60 | $this->assertInstanceOf(Contact::class, $contactB);
61 | $this->assertSame($contact->getId(), $contactB->getId());
62 | $this->assertSame($contact->getIp(), $contactB->getIp());
63 | $this->assertSame($contactId, $contactB->getId());
64 | $this->assertSame($contactIp, $contactB->getIp());
65 | }
66 | }
--------------------------------------------------------------------------------