Home > Programming, Servers & Scripts >

[ SKILL BOOST +100 ] How To Protect Your Landers with PHP and to Run on a CDN (5)


07-30-2017 07:15 PM #1 danielt (Member)
[ SKILL BOOST +100 ] How To Protect Your Landers with PHP and to Run on a CDN

Hey everyone. Given there are a lot of posts with questions regarding how landers get on a CDN or how to combine PHP on a lander and why,
I decided to give it a shot to show you a simple version of a system I made for myself to have landers that run on a CDN but also benefit the powers
of dynamic programming, in my case PHP.

This is inspired from Caurmen's awesome post: Get Your Landers Up And Running On A CDN - The Simplest, Lowest-Cost Way Ever!,
everything he covers there is very up to date, do read it, it will be very useful for today's tutorial.

Ok, contents of this:
1. a link to a .zip file with all the files ( including a simple quizz landing page I used in the past - still works beautifully )
2. instructions on how to use the javascript framework - no more getUrlParam('myAwesomeUrlParam') - there is a better easier way
3. instructions about how to use the PHP included in the framework and how it works
4. how to tie all this to a CDN

Here we go.

1. As promised, here is a .zip file ( below is a picture of the folder structure, its contents and a short explanation of them ) --> click here <--
Click image for larger version. 

Name:	stm.demo.jpg 
Views:	161 
Size:	86.1 KB 
ID:	16155

Please change the name of htaccess to .htaccess

2. The javascript framework is in the /assets/js/scripts.js file. It's not really a framework since it's just bare bones with the right tools to help you do
pretty much everything you need to do, without the need of embedding a large library like jQuery when it's not necessary.

In order to use it in the landing page, every existing function ( or whatever you decide to add to the framework ), will be accessed
via the same familiar sign $ ( like jQuery ), I'm posting the code for it here as well:

Code:
+(function () {
    "use strict";

    /*
     * This is where we initiate the small framework.
     * Every function or property will be called like this:
     * $.alert();
     * or
     * $.params.yourparam - for URL params
     */
    window.$ = window.$||{
    params: {},
    isInit: !1
    };
    $.init = function (cb) {
    window.onload = function () {};
    $.setProperties(function () {
        cb&&cb();
    });
    };
    
    /*
     * To avoid calling the URL parsing function over and over,
     * this section takes every parameter from the URL string
     * and makes a key in the $.params object with the value specified
     * in the URL 
     * ie: ?yourparam=yourvalue
     * can be accessed like this
     * $.params.yourparam 
     * 
     * Most examples make use of a variation of this by
     * calling something like getUrlParam('yourparam')
     */
    $.setProperties = function (cb) {

    /*
     * Here we capture the window.location object ( we cache it in the script )
     */
    try {
        $.location = JSON.stringify(window.top.location);
    } catch (error) {
        $.location = JSON.stringify(window.self.location);
    }

    var l = $.getWindowLocation();
    var params = l.search.substr(1);
    var pairs = params.split('&');

    for (var i in pairs) {
        var pair = decodeURIComponent(pairs[i]).split('=');
        $.params[pair[0]] = pair[1]||!1;
    }

    cb&&cb();
    };

    /*
     * This is a simple built in function for Android vibration
     * Just call this in your landing page in the LP_FUNCTIONS section, like this:
     * $.vibrate();
     */
    $.vibrate = function (interval) {
    interval = interval||[1000, 500, 1000, 500, 1000, 500, 1000, 500, 1000];
    navigator.vibrate = navigator.vibrate||navigator.webkitVibrate||navigator.mozVibrate||navigator.msVibrate;
    $.vibrate&&(navigator.vibrate&&navigator.vibrate(interval));
    };
    
    /*
     * This takes the text inside the div with the ID 'alert' and shows it
     * as alert contents
     */
    $.alert = function (msg, cb) {

    if ($.params.alert)
    {
        if (confirm($.get('#alert')))
        {
        cb&&cb();
        }
    }

    };
    
    /*
     * This function returns the element with the specified ID
     */
    $.id = function (id) {
    return document.getElementById(id);
    };
    
    /*
     * This function returns the elements with the specified CLASS
     */
    $.class = function (cssClass) {
    return document.getElementsByClassName(cssClass);
    };
    
    /*
     * This function returns the html inside the specified HTML Selector
     */
    $.html = function (selector) {
    var el = document.querySelector(selector);
    return el&&(function () {
        return el.length>1 ? el[0].innerHTML : el.innerHTML;
    })();
    };
    
    /*
     * This function sets the inner HTML of an element and works like this:
     * - you can specify either an ID or a CLASS
     * - if a CLASS is specified, every element with that class will have that HTML inside
     * - if there is a URL param with the same name as your selector ie: carrier, 
     * the script will fill the inner HTML with the value from the URL param or
     * you can specify a default value in case that one does not exist, like so:
     * $.set('#carrier', 'My default carrier');
     *
     */

    $.set = function (selector, value) {
    var els = document.querySelectorAll(selector);

    for (var i = 0; i<els.length; i++) {
        var el = els[i];
        el.innerHTML = value||$.params[selector.substr(1)];
    }
    };
    
    /*
     * This function gets the inner HTML of a selector, ID or CLASS
     */
    $.get = function (selector) {
    var els = document.querySelectorAll(selector);

    for (var i = 0; i<els.length; i++) {
        return els[i].innerHTML;
    }
    };

    /*
     * This function lets you play with browser cookies and you can use it like so:
     * - $.cookie.set('mycookie', 'my value for the cookie', 1); // valid 1 day
     * - $.cookie.get('mycookie'); // read the cookie value
     */
    $.cookie = {
    set: function (name, value, days) {
        var d = new Date();
        d.setTime(d.getTime()+(days*24*60*60*1000));
        var expires = "expires="+d.toUTCString();
        document.cookie = name+"="+value+";"+expires+";path=/";
    },
    get: function (name) {
        name += "=";
        var ca = document.cookie.split(';');
        for (var i = 0; i<ca.length; i++) {
        var c = ca[i];
        while (c.charAt(0)===' ') {
            c = c.substring(1);
        }
        if (c.indexOf(name)===0) {
            return c.substring(name.length, c.length);
        }
        }
        return "";
    }
    };
    
    $.getWindowLocation = function () {
    return JSON.parse($.location);
    };
    
    /*
     * This function lets you make ajax calls for other resources if needed:
     * - $.ajax.get('/my/local-resource.js'); // the default method is GET
     * - $.ajax.get('/my/form.php', 'POST', function(){ alert('done'); }, {param:value});
     */
    $.ajax = {
    get: function (url, method, cb, data) {
        var xhr = new XMLHttpRequest();
        xhr.open((method||'get'), url);
        if (method==='post') {
        xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
        }
        $.ajax.process(xhr, cb);
        xhr.send(data);
    },
    process: function (xhr, cb) {
        xhr.onreadystatechange = function () {
        var DONE = 4; // readyState 4 means the request is done.
        var OK = 200; // status 200 is a successful return.
        if (xhr.readyState===DONE) {
            (xhr.status===OK)&&(cb&&cb(xhr));
        }
        };
    }
    };

    /*
     * Here we initiate the system
     */
    $.init(function () {
    $.isInit = !0;
    window.LPFUNCTIONS&&window.LPFUNCTIONS();
    $.alert();
    });
})();
For most people starting out this might look complicated, it's not, you'll get the hang of it once you play around with the files in the .zip.
In order to have them work locally install WAMP if you're on Windows or MAMP if on Mac as a local http server with PHP enabled.

These small functions will let you clean up your landers and also keep a very very low size to them even when everything is compiled
( don't be afraid of the word, we'll go over it in a bit )

3. The PHP framework
The reason why I also included this is to show you how easy it is to have a structured landing page project, with every component
separated from the main LP ( images, css, javascript ) and have PHP glue them together ( compile them ).

This is done via the .htaccess file that reads the URL request path http://yourdomain.com/ecom/1en.html?...erparam=value2
The .htaccess files reads everything in the URL and then tells the script where to look for the right files and below I'm gonna break this process down:

a. it looks for the first part of the URL ecom or 1click or whatever and sets the inner URL ( the one that only Apache sees ), hey, set the URL variable: niche=[whatever_is_in_there]
This means you can have a structure to your landers and a single system to control it. If you have several niches you do campaigns on ( ecommerce, 1click, etc ) you can easily separate them.

b. next, it looks for the number 1en.html
In the folder structure, the landers are inside the niche folder and inside the parts folder, within numbered folders. In our example 1en.html = /ecom/parts/lp1/
Here, you will name your .php file with the same number, using the following pattern lp1.tpl.php - the reason for using the number here and the .tpl extra naming is
for better editing management - if you have several LPs open in your editor, you want to easily spot them

c. after the LP number, the system also tries to figure out the language
I've set the language in the path not in the query params for the following reason: if you have a page that is being processed dynamically at the first request ( then saved on a CDN )
all subsequent requests via the query param will trigger the first cached page - meaning you can have the LP with the wrong language - hence it's better to have it processed before we save it to the CDN ( on the fly )

Now the main parts of the LP have been set, the system glues them together, adding the CSS inline ( no matter how large that is ) - this improves loading speeds quite a bit
and also render times quite a bit ( double win ).

It also adds the JS inline at the bottom of the page ( even if you edit it in separate files ) so it won't block the page. Now, the cool trick here ( you will see if you view the source code ) is that the JS gets packed
( almost like encoding but saves space and makes it a lot harder to modify - good for protecting your landers )

If at any time you want to split test your LPs against the non-packed JS version, just add the noe folder in the URL like this:
http://yourdomain.com/ecom/noe/1en.html?yourparam=value&someotherparam=value2

NOE - No Encoding - simple, huh?

Here is the end result of the compiled page --> click here <-- with packed JS ( you can save it locally and open it to see the result )

4. Tie this to a CDN
Every CDN ( do go over Caurmen's tutorial ) has the option of specifying a target server for where to take your files from.
In your case this can be: http://yourdomain.com/ecom/1en.html?...erparam=value2

This is the source target ( where the CDN goes to take content when there is nothing cached on the network ).

Given the above is the source target, you also need a CDN url for your LPs ( this way you get to host ALL the LP and its contents on the CDN - not just the js /css / img ),
something like this:
http://cdn.yourdomain.com/

Here is a diagram of the CDN flow:
Click image for larger version. 

Name:	stm.demo.cdn-flow.jpg 
Views:	99 
Size:	92.3 KB 
ID:	16156

After the source target is set, you can access your PHP compiled cached LPs on the CDN like so:
http://cdn.yourdomain.com/ecom/1en.h...erparam=value2

And voila, you have a blazing fast landing page, cached on the CDN, ready for serving at milliseconds speed.
The current system makes it efficient and this is just the stripped down version for it,
no LP extra protection, no extra features, still very powerful.


EXTRA:
In the /ecom/parts/lp1/ folder you will see a lang.yaml file.
.yaml files are good for storing structured data in an even more human readable format than JSON ( still love you JSON )

If you add more contents to the file, ie: more language translations, the system will automatically pick them if you change the language in the path:
http://yourdomain.com/ecom/1fr.html?yourparam=value&someotherparam=value2
http://yourdomain.com/ecom/1tr.html?yourparam=value&someotherparam=value2
http://yourdomain.com/ecom/1es.html?yourparam=value&someotherparam=value2

Do ask questions, my teaching skills are of a 2 year old, I might have missed a thing or 2.

EXTRA 2 [ Later Edit ]:
You will notice there is no click url specified on the page. This is because we specify it via the URL
params giving you full mobility when changing domains.

If you click on any gift card at the end, you will get an alert with: Click Url param not set.

Just add this to your params: &clickUrl=http://myclickurl.com/click ( or whatever your clickurl is )

----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
This is a framework my team uses as well for our campaigns, it's a live thing ( not ancient scripts ) and I've never put it out there until now,
do take advantage of it to make your landers fast and small.

----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

Cheers,
Daniel


08-01-2017 10:24 AM #2 nickpeplow (AMC Alumnus)

Nice lander framework!

- How does this protect your landers?
- I don't think any of this really involves or requires php, just htaccess?
- jquery min from google cdn is likely to be cached already in browser, so faster than loading a custom script
- we did extensive split testing and regional servers beat a cdn in most cases


08-01-2017 12:57 PM #3 danielt (Member)

Hey mate. Good questions, will take them 1 by 1.

1. It protects the maker from ripping - this does not protect from getting into spytools for instance, that's a different topic
2. PHP in this case is for minifying / packing the JS / CSS on the fly and compiling it into one file - of course PHP can do a lot moar
3. jQuery min is around 83kb whereas the above LP with CSS / JS included is around 30kb without the extra call overhead - instant render
4. this I haven't done but if the LP is compiled as the example, the gains would be marginal

I see that most affiliates use 2-3% of jQuery just because of the ease of use for selecting a thing or 2 ( animations included )
which is both overkill and performance hog - when you want fast loading LPs, even on desktop.

As mentioned above, the LP spytool protection, cloacking even on client side, is a different topic, not in the above example.

Lemme know if I can help with it.

Cheers,
Daniel


09-05-2017 03:08 AM #4 erikgyepes (Moderator)

Amazing share, thanks a lot Daniel!


04-15-2020 03:32 PM #5 jeremie (Moderator)

Interested in the ZIP file. It is not available anymore unfortunately with the link.
Does anyone still have it?

Thanks!


Home > Programming, Servers & Scripts >