Server-side PHP Library
03 Mar 2019 » Server Side
When, a few months time ago, I started writing the series of blog posts on Server-side digital marketing, I did not initially know what to expect. If the number of comments per post is a valid survey method, then I can now say that this series has been the most popular in my blog so far. I have even received internal requests from colleagues about this topic after reading one of these posts.
As the next step, I wanted to show how to put together all the Adobe tools to work in a website and execute everything server-side. To achieve this, I am building a library in PHP to cover the ECID service, Analytics and Target for server-side execution, which I will explain in this post. Then, probably the next post, I will use a simple HTML website to show how to use this library.
You will have noticed that I have excluded Audience Manager. This tool is more complex to use in a server-side environment. I will show how to use it at its full extent in a future post and update the library.
PHP library
I would have wanted to show this example in AEM, but I am not an expert in this tool, as you probably have realised from my posts. It would have taken me forever to prepare this post. Instead, I will use PHP, which requires a simpler setup and I feel more confident with. By no means what I am going to show is how exactly it should be implemented. What I want is to give an idea on how to do it.
As I said earlier, I am working on a PHP library, which you can find here: https://gitlab.com/pmonjo/aec-server-side-php. It includes ECID, Analytics and Target and some very, very basic tests.
A few comments about this library:
- This code is NOT endorsed nor developed by Adobe.
- Use it at your own risk. I will not provide any support and I still have to decide which license I want to release it under.
- It has been almost 10 years since I wrote code professionally. The code can be organised and written in a much better way, that is for sure. The point is not about the code itself, but how to use the Adobe tools server-side.
- The library is not finished nor comprehensive. The initial code only contains what I want to show. Expect a lot of changes in the code.
- The library does not address all the capabilities that the APIs offer.
- Feel free to fork it. It is just one click away.
- If you want to contribute, leave a comment at the bottom.
Putting it all together
In summary, the process for implementation using this PHP library is as follows.
- Before rendering the page
- Generate the data layer. For example:
$data_layer = array( 'page_name' => 'Home', 'url' => (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on' ? "https" : "http") . "://$_SERVER[HTTP_HOST]$_SERVER[REQUEST_URI]", 'referrer' => $_SERVER['HTTP_REFERER'], 'user_agent' => $_SERVER['HTTP_USER_AGENT'], 'ip_client' => $_SERVER['REMOTE_ADDR'], 'host' => $_SERVER['HTTP_HOST'] );
- Read the configuration with all the needed parameters (servers, report suites…)
$config = new Adobe\Config(/* Path to config */ 'ADBServerConfig.json');
-
Retrieve the ECID, either stored from a previous visit or generate a new one. In the following example, I am storing any previously generated ECID object in a cookie namedServerSideECID
, in JSON format.
[UPDATE] The solution I previously provided here is not the best and has some issues. I will write a new post where I will explain how to deal with the ECID in web environments. However, if you are using this solution for headless implementations, then you need to use a local database, keeping the result of the ECID request and refreshing it from time to time. - Initialise Target
$target = new Adobe\Target($config,$ecid,$data_layer['user_agent'],$data_layer['ip_client'],$data_layer['host']);
- Generate the data layer. For example:
- While rendering the page
- For each regional mbox, make a request to Target.
$mbox = new Adobe\Mbox(/* Mbox name */); $mbox->setPageURL($data_layer['url']); $mbox->addParameter("at_property", /* at_property for your workspace */); $target->requestMbox($mbox); $mbox_content = $target->getMboxContent();
- If Target sends back some contents, replace the mbox default content with the response from Target
<?php if ($mbox_content) : ?> <?php echo $mbox_content ?> <?php else : ?> <!-- DEFAULT CONTENT --> <?php endif; ?>
- For each regional mbox, make a request to Target.
- After rendering the page
- Send the Analytics beacon
$analytics = new Adobe\Analytics($config,$ecid,$data_layer['user_agent'],$data_layer['ip_client']); $analytics->setPageName($data_layer['page_name']); $analytics->setURL($data_layer['url']); $analytics->setEvar(1,$data_layer['page_name']); $analytics->setProp(1,$data_layer['url']); $analytics->setEvent(1); if (isset($data_layer['referrer'])) { $analytics->setReferrer($data_layer['referrer']); } if (isset($mbox)) { $analytics->setTnta($mbox->getTnta()); } $analytics->sendHit();
- Log anything you want
- Send the Analytics beacon
In theory, it is a simple as that.
The devil is in the detail
Yeah, I know, it is not that simple in real life. Some things to take into account.
Data layer
The data layer should be a native object with all the information you need to use in digital marketing, just as you would do in JavaScript. The W3C recommendation is in JSON format, which makes it very convenient for traditional client-side implementations, but not so much for server-side. I do not recommend it in this case, but you can use it as an inspiration.
The main problem is that, usually, a web page is a combination of components, each of which can potentially provide information to the data layer. This means that, until the whole page has not been rendered, the data layer is incomplete.
I can think of two potential solutions:
- Execute all components at the beginning and store temporarily the output of each of them. Then, generate the data layer and, subsequently, start rendering the page with the output from the components.
- Progressively populate the data layer. Capture initially all the data you can and, as the web page is rendered, update the data layer. As long as you do not need the data before it is generated, this approach should work. It helps to leave the Analytics call at the bottom.
ECID storage
[UPDATE] As explained above, the ECID service in web environment should not be run server-side, only for headless implementations.
When using the standard JavaScript library (VisitorAPI.js), a cookie is used to store the ECID information (the AMCV cookie). Moving this functionality server-side, means that you now have to provide your own storage for the ECID service. You do not want to generate a new one on every page view, as all your reports, personalisations and optimisations will break.
Here you have a couple of ideas:
Be lazy as I am and create a cookie. If this cookie is not present, it means a new request todpm.demdex.net
is needed. Put the information from the response in this cookie, so that it comes with every HTTP request to the server. In summary, you are using the browser as storage.- Use a database. You will need a key to retrieve the data
, which will probably come from a cookie you manage. However, the difference is that this cookie contains only the key to the database.Obviously, if this key is not present in the database, you need togenerate a new one andinvokedpm.demdex.net
. I recommend this option. Which key to use will differ in each implementation: a chatbot ID, a device ID (for IoT devices)…
In both cases, the cookie should not be a session cookie, as this would mean that you lose the visitor information very quickly. I would suggest to use an expiration of 2 years.
The minimum data that you should store is: MID, DCS region, Blob and UUID (which is the value of the demdex cookie that will come in the response).
Regional mboxes
Although, in theory, you could still use the concept of a global mbox, I think it would be very difficult to implement. For starters, the Visual Experience composer (VEC) in Target will not work. Remember that there is no mbox.js/at.js in the website, which is needed by the VEC. To keep things simple, we have to go back to the time of regional mboxes and use the forms interface in Target.
The good thing about the new Target API is that it supports batching. So, for example, if in a page there are multiple regional mboxes, you can retrieve their contents in a single API call.
Adobe EMEA Summit 2019
The next few weeks are going to be a bit different for me, as I will be travelling. Then, in May, I will be running a lab at the Adobe EMEA Summit 2019 presenting something very similar to the topic of this post. Therefore, my cadence of one post every 2 weeks is very likely to be affected. If you see that I am not posting for some time, do not think that I have given up on my blog; it is just that I am struggling to post anything. Rest assured that I will continue posting after this craziness. I am enjoying it and I have a long list of topics I want to write about.
Another consequence of this lab is that I will be writing more about this example here, as I refine it. I may add more blog posts or update this one, I still have not decided which way to go. In any case, stay tuned!