Sequential Image Loading for Optimized Load Times

If you’re creating a page that is very image heavy, you probably want to make sure that the images further up the page take loading priority over the ones further down the page.

This is especially important for mobile sites where you don’t want a huge image at the bottom of your page taking precious bandwidth away from the more important images above the fold.

There are a lot of ways to accomplish this, but I think I have the easiest solution — sequential loading. Continue reading “Sequential Image Loading for Optimized Load Times”

Escape Tokens with Markup in Drupal 8

If you’ve ever tried to use markup in custom tokens, you’ve probably run into the situation where you needed to display raw HTML instead of escaped HTML which isn’t easy to do out of the box and doesn’t look like it’s going to get any better any time soon.

Instead of the expected output of

<p>some text in a paragraph</p>

You’ll get this lovely piece of HTML

&lt;p&gt;some text in a paragraph&lt;/p&gt;

Which is fine unless you were expecting raw HTML.

So, how can we fix this?

How Tokens Work

To figure out how to fix this problem, we first need to figure out what the problem is. On line 204 of core/lib/Drupal/Core/Utility/Token.php we see this

$replacements[$token] = $value instanceof MarkupInterface ? $value : new HtmlEscapedText($value);

Generally you’re sending a string to Drupal::token()->replace() which will send your replacement through HtmlEscapedText and, as the name implies, will escape the value resulting in some ugly escaped HTML instead of our pretty raw HTML.

This same line also tells us how we might get around this though.

MarkupInterface

According to line 204 of Token.php, if we send an instance of MarkupInterface as a replacement value for a token, it won’t be escaped.

The simplest way I could find to get an instance of MarkupInterface was to use Markup::create which takes the value you pass it, and sets that to the string value of the markup, and will return your raw string.

So all we need to do in our tokens hook is something like this

function mymodule_tokens($type, $tokens, array $data, array $options, \Drupal\Core\Render\BubbleableMetadata $bubbleable_metadata) {
    ...
    foreach ($tokens as $token => $original) {
        $replacements[$original] = Markup::create($replacementValue);
    }
    ....
    return $replacements;
}

Doing this will ensure that your token markup is not escaped and you can stop banging your head against the desk like I have been.

Response Caching with Laravel 5

Caching is a great way to speed up an application. You can cache views, queries, variables, and tons of other things very easily, but what if you want to cache an entire response?

There are a few different places we could cache a response, in the controller is where I see it done most of the time, but I wanted a more general-use method of caching a response. A great way to do it is in a middleware class.

Our middleware needs to do two things:

  1. Check if our response has been cached, and if it has then return it
  2. If it has not been cached, cache it for the next request

For the first requirement, I’ll use the standard handle()  method you see in most middleware

/**
 * Handle an incoming request.
 *
 * @param  \Illuminate\Http\Request  $request
 * @param  \Closure  $next
 * @return mixed
 */
public function handle($request, Closure $next)
{
    $key = $this->generateKey($request->fullUrl());
    if (Cache::has($key)) {
        // you can also run some cache invalidating logic here if needed
        return response(Cache::get($key));
    }
    return $next($request);
}

We need some sort of unique key for our cached response, so I decided to take the full URL and run it through a simple function to generate a slugged string of the incoming URL

protected function generateKey($url)
{
    return 'response_' . str_slug($url);
}

For the second requirement we can use terminate() . From the docs

If you define a terminate method on your middleware, it will automatically be called after the response is sent to the browser.

So after a response has been sent to the browser, we’ll get that response back and then we can cache it.

/**
 * Handle caching of a request if it doesn't already exist
 */
public function terminate($request, $response)
{
    $key = $this->generateKey($request->fullUrl());
    // if the cache key doesn't exist then we store the entire response
    if (!Cache::has($key)) {
        // cache for 24 hours
        Cache::put($key, $response->getContent(), 1440);
    }
}

Here I’m storing the response for 24 hours, but you can raise or lower that depending on your needs.

Full middleware

<?php

namespace App\Http\Middleware;

use Closure;
use Cache;

class CacheResponse
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {

        $key = $this->generateKey($request->fullUrl());

        if (Cache::has($key)) {
            return response(Cache::get($key));
        }

        return $next($request);
    }

    /**
     * Handle caching of a request if it doesn't already exist
     */
    public function terminate($request, $response)
    {
        $key = $this->generateKey($request->fullUrl());

        // if the cache key doesn't exist then we store the whole response
        if (!Cache::has($key)) {
            // cache for 24 hours
            Cache::put($key, $response->getContent(), 1440);
        }
    }

    protected function generateKey($url)
    {
        return 'response_' . str_slug($url);
    }
}

Now we need to register our middleware in app/Http/Kernel.php  which you can do by adding our new class to the $routeMiddleware  array

'cacheroute' => \App\Http\Middleware\CacheResponse::class,

And finally, add the middleware to any routes that you want to cache

Route::get('/', function () {
    return view('welcome');
})->middleware('cacheroute');

And now you have simple middleware that you can use to cache any of your responses.

Using Headless Chrome to Find Link Redirects

Following redirects to find a final destination — seemingly easy at first. CURL the URL, let curl follow the redirects, then spit out the final destination. That was my initial thought… then the “gotcha”. We also need to follow javascript redirects to find the “final-final” destination.

Enter Headless Chrome, a PhantomJS-like tool for “viewing” web pages without a browser. The big benefit with Headless Chrome over PhantomJS being that Headless Chrome uses the latest version of the Blink rendering engine as opposed to PhantomJS’ WebKit rendering engine, the very engine that Chrome ditched a while back. What this (hopefully) means for us is that when you load up a page in Headless Chrome and export it to a PDF, it should look the same as if you were to open it up in the Chrome browser. This seems like a perfect fit for our problem. Continue reading “Using Headless Chrome to Find Link Redirects”