DPC2019: Automatic Web Page Optimisation in Pure PHP – Albert Peschar

By | January 21, 2020


Automatic Web Page Optimisation in Pure PHP
– Albert Peschar>>I’m allowed to start, so that’s what I
will do. Last night, we had the speaker dinner, and
afterwards they gave us espresso which is a very nice touch so I’m sensitive to coffee
but didn’t sleep as well. I lay thinking, and I don’t know if you’ve
been to the kind of talk where somebody presents, usually a JavaScript library, someone presents
some thing they made or read about. It’s kind of neat, but when you listen to
them and present it, go through the whole presentation you release this is not a good
idea. This is a bad idea. We don’t want web development to go in this
direction. I don’t know if any of you have had that experience
at a talk? Nobody? All right. Well, I realised last night that this might
be one of those talks. [Laughter]. I decided to add a bit of a disclaimer that
I’m going to present some information about what is important in web page optimisation
a project which is a bit of a Band-Aid. When you have the opportunity to start building
your application and you can think about performance from the beginning, you’re not going to need
this. It’s something that, if you have a legacy
app, and you don’t want to rebuild it, for performance first, then you can add this,
and it can solve some of your problems. But it’s not going to make everything perfect. That’s a recurring topic, so for development,
we never have time to think about doing everything perfectly, so we keep adding layers upon layers
in order to get something in the end that actually works, right? Because we have a lot of JavaScript on our
pages, we get faster JavaScript engines. What do we do? Add more JavaScript. There’s the eternal balance of performance
in that sense. That’s the diagram for that. I think it’s a familiar story for some of
you, at least if you’re contractors or working for an agency – somebody like that. I’ve been doing contracting for a decade and
I have a lot of customers asking me about performance. That’s the topic that keeps coming back. My site is slow, what can we do about it? How can we improve it? It’s important to the customer because it’s
important to their customers in the sense that if you have a slow site, it’s not good
for your conversions. There is lots of research showing that. One sample is that a high percentage of users
will leave if it takes longer than three seconds to show a usable page. That’s on mobile. But a similar percentage applies on desktop
and other findings like 100 milliseconds increase in latency causing conversions to drop by
seven per cent. Just ridiculous. That’s only at certain points on the curve
of this latency, but still that shows that a performance improvements can make a huge
difference in your conversions which are important to your customer, obviously. Lastly, the reason that I like performance
optimisations is that I really like – I really hate working on slow slights, because if you
change something, you have to reload it, it’s really painful. If you’re clicking around in order to test
some functionality, it is the it’s the same stuff. Personally, that’s the main reason I care. So people usually find out about this stuff
because they use some tools – GTMetrix is probably the most popular and the one you’ve
possibly heard and used is Google Page Insights. Who hasn’t used this tool in at least two
people who don’t want to put their hands up! In my experience, lots of clients learn about
performance first when they use this tool. They never have thought themselves my site
is slow unless it’s really bad. They put it through the tool and Google gives
them a bad grade which is like a report mark at school, and Google says your site scores
60 out of 100 which is obviously not very good. Google’s opinion is important to them because
they know that Google cares about speed for SEO purposes right? What the tools do is use some heuristics in
order to generate a score for a web page. Not a very large part of the score. The Google page is a small part of the score,
and metric is not a part of the score. The only part of the score at the most is
the actual low speed measured. Most of it is heuristics. But those heuristics generally do a good job
at predicting the low speed. This is a report from a client side where
we got 22 out of 100 which is obviously a bit demoralising. So you get the re action. Google graded my site badly which has a huge
psychological impact on the customer, whether or not the actual experience of the user is
very bad. That’s why as developers, I’ve never seen
it, you’ve probably done – this missing information from the tool saying is bad but none of the
things that it says are really that important, and that’s why it is fine as it is. I don’t have to do those optimisations. Will anybody admit to having said that at
some point to a customer? Right. So that is what I used to do as well. Like, the first thing I tried to do in this
journey is to re-educate the summer go say these things are just tips, and they’re not
that important, so we’re not going to do it because it’s not worth the effort. But they weren’t happy with that. I wasn’t terribly successful with that always. And there is a point to not being terribly
successful at that, because although it’s true that not all sides score badly are slow,
because there are a number of heuristics that don’t make that much sense, like Google Page
Insights has a fantastic one which is levered browser caching. You see in the second line that it’s not happy,
and it won’t give you a perfect square because of Google Analytics. You can get rid of Google Analytics to solve
this. I have a feeling that Google is not always
perfectly co-ordinated between teams. But where is it anyway? But generally the relationship the correlation
holds, like if you get a perfect score on the tool, then you can be reasonably sure
that your site will be pretty fast. Google page insights used to be pretty simple. It used to have nine rules. Nine simple rules relatively simple rules. That, together, made up with certain weights,
distributed across the rules that made them the score. These days, they have, they use a different
tool called Lighthouse under the hood which is available as a chrome extension, like it’s
installed by default, actually. It is neat, not only checking for performance,
accessibility, SEO, whether your site is a progressive WhatsApp, and some JavaScript
best practices, but we’re not really interested in those. However, they now have 26 rules for performance
which we are going to go over now one by one. No, we’re not. Thank God! Luckily, you can more or less categories those
with a few exceptions as rules regarding latency, bandwidth, and round trips, latency meaning
the amount of time that the client needs to get a particular resource from the server,
which obviously is a bit of a – before you have a resource, nothing is going to happen,
so before you get the HTML page, nothing’s going to have. If you have a slow server response, you’re
dead already. The other thing we’re talking about is minification
and resources, and optimisation, that’s all bandwidth minimisation, especially morning
on mobile because if you have a slow connection, that’s going to be a strong difference arising
from the size of the resources like images that need to be downloaded. On desktop, it’s less important. These days, we all have fibre connections. Unless one is on an ADSL connection, there
won’t be much of a problem, even if your resources are a bit bigger. The most interesting part to me is the round-trip
story which has to do with the amount of teems that the client has to go back to the server
in order to get some puzzle piece in order to be able to render a page, generally talking
about CSS and JS. We will go over that. For example, you’re thinking you have a style
sheet, sometimes the style sheet might import other style sheets and those style sheets
might have a web font. At this point, the client first goes to the
server, get the style sheet, and the style sheet says you need this other one too. It has to go again. Then the style sheet says you need the font. It has to go again. Only after it goes back the last time can
it finally actually render the page. So then if you have 100 milliseconds needed
there to get a resource, suddenly, it’s 400 milliseconds in the best case. So a simple diagram that I think you should
be familiar with, I’m going to go over it anyway, because it’s fundamental to the whole
story, the way that web pages are built up and loaded, there is HTML which refers to
other roars, some of which are loaded synchronously and some of which are loaded asynchronously. After they’ve downloaded the HTML, anything
that is in the head, CSS and JS resources are going to have to be downloaded before
anything can be rendered. I’m sure that most of you are aware of that. Additionally, you can have images, and asynch
JS on your page which doesn’t have to be downloaded for anything to be rendered because it’s done,
it’s done simultaneously, and it doesn’t block the rendering. There is a little bit of a thing there that
asynch JS can slow down your site because of course, it’s kind of hogging bandwidth
that is not used at the same time for downloading other resources, and especially in mobile
devices, JavaScript execution is not as cheap as you think, and it can block a CPU, and
delay interaction with the page and delay rendering as well. This looks a lot like a waterfall diagram
that you can get yourself for your own websites using freewebpagetest.org. How many have used this? I guess about half. It’s very useful. It’s the only tool you need for proper real
performance optimisation. As opposed to heuristics-based checks that
you have with page sites. You put in your URL and you get a report. It’s a pretty big report. There are some metrics at the top. Where is my – oh, yes. We’ve got these metrics. Which are – the most interesting thing is
a picture which is this. And this one is not pretty. Because what you’re looking at, the green
line is when the render happens. What you’re looking at is the download page. You need all these other resources in order
to finally and last JavaScript has been downloaded, finally get a render. Of course, if it looks like this, on desktop,
1.7 seconds, it’s not good, it’s not pretty, but all right. But if you’re doing this on mobile, it’s going
to be a massacre. What do you want it to look like is more like
this. Where, in fact, here this is mostly due to
CPU, like parsing the page, and actually laying out everything. You can see here, the CPU usage. But, in fact, the page can be rendered right
after the first HTML has been downloaded and it’s waiting for a couple of images. It doesn’t have to wait for the images. They’re loaded slowly, the page will be rendered
nevertheless. Statistics that you see at the top here pretty
useful, although people usual ly stair at themselves blind, at least that’s how we say
it in Dutch, at the load time. The load time isn’t always relevant, because
it’s waiting also for images to complete loading. What you’re mostly interested in is the start
render time which is the third column and individually complete time. Visual completion, that means is that everything
that is on the page, the page isn’t going to change visually after this moment, so basically,
last two seconds of load, something is happening, but it doesn’t matter. It doesn’t influence what the user sees and
we’re basically interested in perceptual load speed so don’t care so much about what is
going on in the background. When the user feels like something is happening. You make sure that you have a first render
quickly, even if you don’t have images, for example. At least the user will start seeing the structure
of the page, for example, I do that on Facebook, like when you open something on Facebook,
it gives you this outline, which is barely visible to you if it loads quickly, but, it
kind of tricks you into thinking that it is downloading earlier than it actually is, because
you cannot see the details in the few hundred milliseconds that it happens. So you’re basically convinced that it happens
quicker than it actually does. And interesting statistics are also the size
of the page which in this case is a whopping two and a half megabytes. So it’s a bit of. And the amount of requests can also have an
impact, although generally will President 2 these days, it’s not that bad. These days, like post-optimisation is 26,
27 and only half a meg which is still a lot for a web page. It’s 2019. You can also get a repeat view which gives
you an idea whether you’re messing up caching. In this case, you’re not, because there’s
only one request that needs to be done in order to show the page again so that shows
you that the cache is working fine. So, there’s a few basic thing that you need
to do in order to get performance right which are basically just server configuration, although
it’s surprising how often it’s not done still. You need to make sure, of course, that static
resources are cached telling the browser you can keep this file for a longer time. Is anybody unfamiliar with how to configure
caching? Right. That’s what I thought. Pretty much the same thing. Compression is a flag on the server saying
you can send something, gzip compress it so it takes less bandwidth. HTTP/2. I don’t know how many of you today went to
the HTTP/2 talk? With HTTP/2, the problem of having many requests
needed to build a page is a lot less important, because as opposed to HTTP 1, there isn’t
much of a performance hit when you try to request many files at the same time, because
of the – because HTTP/2 allows multiplexing on the same connection, so you don’t need
the same connection for each simultaneous download. Keep Alive has been around 1997. I was on a customer site, and it wasn’t enabled,
and it means for each file, there needs to be a new TCP connection, a new TLS connection,
a new HTTP request which of course doesn’t add up to a pretty picture for download times
for resources. You can test that, and you can test that on
the command line using cURL, basically. If you check HTTP/2 compression, caching,
check the headers on individual files. It’s pretty basing. You get the results and it will tell you somewhere
down here it will tell you that you have HTTP/2. You can see it in the developer console on
your browser. So that is pretty basic stuff. The more interesting thing is, in order to
get really to the Holy Grail of performance, you want to get rid of all the render-blocking
CSS and JS, that after it has rendered, it shows the page in its full glory. That doesn’t very often because it’s a bit
of a pain. These days, if you’re joining a JavaScript
app with webpack and such build tools, it’s easier to integrate everything into the page,
especially the CSS. But almost nobody does this for an actual
web app which means you have the CSS request after the HTML download to deal with. If you want to really do this, like to get
like the maximum – the minimum amount of time necessary for first render, you have to put
in a bit of work. For CSS, you have to find out what CSS you
actually need in order to render a page. Not all the dependencies you’re adding to
the page but only the CSS is necessary for that page and put that inline in the HTML. And for JS, it’s even a bigger pain because
if you try to make scripts asynchronous, you will notice that your current application
doesn’t work that way, and things are dependent on each other and dependent on all sorts of
stuff, so it means basically, you have to rewrite parts of were your application to
make that work. Your life is going to be like this. At least for me, it started looking like that
at some point. If I wanted to do these optimisations for
my customers, I had to do the same thing over, and over, and over again. The added problem that if other members of
the team aren’t quite aware of why something is done a certain way, then will he undo your
optimisation and have to do it again at some point. So, suddenly, you have another job. And especially if you’re dealing with a legacy
app where you don’t want to rewrite everything, that’s a problem, and God forbid, because
isle sure nobody of you are working with this, if you’re using WordPress, you don’t have
control over anything. There are all sorts of plugin, and every plugin
is the most important plugin in the world and every plugin wants to have a script and
style sheet on every page, regardless where you’re using the plugin on your site. So that also can be a problem. At some point, me and a colleague decided
to build a fool to fix that, called Phast which will make your site fast and it’s PHP,
which is creative. And I had had moved to Bulgaria – I’m Dutch,
from here – but I moved to Bulgaria four years ago because I work as a contractor and at
Sol point I wanted to have an agency because I had so many projects that I thought I should
expand, and I should other people do the projects for me. But of course it meant I had somebody in my,
an employee of my service, so that means that at some point, I wasn’t that interested in
doing projects any more, I was we’re going to spend a couple of months full-time building
this tool without much of a business model. But, hey, you are the guys that will profit
from that. That’s actually Milko my ex-colleague, because
but now I’m working alone again, and he’s organising PHP Bulgaria – I wanted to throw
that in – and this software is so advanced that he did his Masters thesis on it, and
the people at the university were impressed. So the basic idea is, you have a slow page,
you put … yeah! I know, I spent a couple of hours on this
one, but … [Laughter]. All right, you get it. It’s a little more complicated than that. It looks like this. You – we’re looking at the output of your
page, and we convert that into a – we parse the page, and then we change a lot of stuff
about the structure, and then we out put optimised structure of the page HTML again to the browser. We do this on every request, and we try to
do this as quickly as possible so it doesn’t affect the response time of your server. In PHP, you’ve got OB Start which can parse
like a function and function on the output your page generates. That’s how it works under the hood. At this side, for dramatic effect, so, the
idea is that instead of having to do the optimisations on your application, you just install this
library, and you don’t have to do anything, basically, unless there is some incompatibility
arises which it does styles. We can then add some new fancy features to
the library and push that out, and you can update it so everybody gets to use the same
optimisation without having to apply and do your application manually. The installation is pretty much just a composer
install, and then you just call this function. Where your index PHP like before your application
runs. . It’s possible to integrate it as a middle
ware in a cleaner way than the output offering. The most basic feature we do is image optimisation,
which just changes the URL of the image to go to a service that optimised the image,
and if possible outputs it as web P if the user is using Google Chrome. This way, you don’t have to optimise any images
ahead of time because we don’t know which images will be accessed, we just do it on
the fly, on the first load, it causes a bit of a delay, but it’s not too terrible. After that, it’s cached, so there’s no real
impact. On the server load after that. But in the course of doing this, this is very
simple, of course, we are just replacing an image source so you can do this with array
X. This is how I’ve done it in all the projects,
but this point, we wanted to do a bit more advanced changed to the DOM, to the markup
of the page, so we’re reusing PHP DOM documents which works like this. I’m sure many have used it. It’s very nice but not HTML5 compliant, so
I don’t know how many of you think that this P is inside the div? How many of you think that it is outside the
div? You’re all wrong, in fact! But, PHP agrees with you, because in HTML5,
there is no such thing as a self-closing tag any more, or rather, I didn’t say that quite
right, self-closing tags are indicated by the tag name, and the slash whether or not
it is there does not matter. It’s not interpreted any more, it’s not XML. If you have the slash on a div, it doesn’t
do anything. That is how browsers parse this, browse it
as a P inside a div, but if you use DOM documents, it doesn’t work. Especially the legacy application I’m talking
about, people will have weird markup going on. It works somehow, and you break it by using
the this tool, and then you try to do a bit more of client re-education in saying yes,
it’s broken because your markup sucks, so you’re going to have to fix the application. But of course nobody wants to hear that because
it worked before. You installed a tool and suddenly it breaks. It’s not our application, it’s your tool,
right? So we needed to make it HTML5 compliant. Now I looked on stack overflow and saw how
is the best way to parse overflow? RegX. We’re not parsing the whole page but tokenising
the whole page and – we’re not doing naīve passing with RegX but I wanted to throw that
in, because you’re not supposed to do that. It’s faster than DOM documents because we’re
not building a DOM because it’s in the object model, of course, you parse the entire page
into set-up objects, and when you’re done manipulating, you serialise the output again. That way, we can output – all the HTML that
you have on your site, we can output it as soon as you output it to browser. The filter doesn’t need for the entire to
be generated or to start working. I wanted to look at how the critical CSS inlining
actually works, both from an interesting perspective of how this library works and also because
you can apply this knowledge in your own project, even if you don’t want to use the library. Why are we doing this? If we inline critical CSS, we don’t need to
download the CSS if we open the page, especially if it is all in the cache, and we don’t need
to parse it either. We can render the page before background loading
external CSS and that saves us an extra round trip to the server. The general looks like this with you worse. You have a lot of dependencies on your site,
and you have a very simple page that actually uses maybe ten per cent of all the styles
you were loading, like, for example, in bootstrap people generally don’t use everything. But if you don’t make a distinction which
rules you’re using or not using, you can try inlining your entire style sheet but this
will make your page very big which won’t help either because that way, you start not being
able to fit the page in a certain amount of network packets any more and that is slowing
down the load if you inline everything, and, of course, you can forget about caching. This is the general problem: you’re only using,
like, two rules in this case out of a style sheet with thousands. So what we do is we parse both the page and
the style sheet, and we copy the rules from the style sheet that we need into the page. Pretty simple. You can do this as well. Tools for this. You can Google that critical CSS. There are tools that use a headless browser
to load the page and then figure out which part of the style sheet you actually need
to put on the page. The trouble with that is you need to do had
a that every time you make a change, so I for one wasn’t going to do that. JavaScript is a bit more complicated, because
it’s not as simple as it seems, and not as simple as we wanted it to be. You want most of your JavaScript oftentimes
to load after the initial render because it’s not critical to the page. Even if you have a few components on the page
that use it, generally, the layout of your site is not dependent on JavaScript. If you can get the layout, like the – if you
can get 50 or 0 per cent of the visual content of your South to render without JavaScript,
then that increases the perceptual over time immensely. – perceptual load time immensely. In that sense, it’s similar to CSS. We can just do this, right? Except that you can’t, because you’re dealing
with a number of problems as the difference how asynch scripts or deferred scripts are
executed verses normally. The order is undefined. If you do this to your script, a jQuery plugin,
it won’t work. The application might be – before the tend
cities. It might work and something assist different
than it normally does. You can’t use asynch on inline scripts to
you have to make them external, so that’s a bit of work. Lots of scripts are dependent on DOM content
loaded in such events quick like they only execute behaviour after the DOM has finished
loading, but if you use asynch, the script might be loaded after the DOM has finished
loading. The script will never happen and it will wait
forever and, of course, your application is also broken. Lastly – again, I hope this doesn’t apply
to this audience – if one uses document rights, it is not going to work any more because the
way document-write works is that it injects the HTML that you output into the parse at
that moment, and normally, when scripts are suited synchronously, the document is kept
open and it hasn’t completed yet. You inject something into the parser. You can’t do that once the DOM is complete,
the DOM has been closed, so document write stops working. What we do is we load our scripts using completely
custom architecture, like we don’t use the browser script execution mechanisms any more. We disable all the scripts that are on the
page, and we then download them using Ajax, and then we use eval to execute them. This is of course a horrible idea. But it actually works. Yes. And in that sense, there’s an interesting
peculiarity of JavaScript that I’m sure most of you won’t be familiar but which I find
very interesting. In this case, what is going happen? Does anybody have an idea? Oh, yes, I’m an idiot. Yes, you’re right. Let’s say that varA immediately of A. Yes. If it says var a, it would be undefined. If it is executed in global scope and you
have var, it will possible late on the global scope on the window property because if you’re
running something in global scope, your scope is the window property. This doesn’t work, we need this to work, but
luckily, there is a trick that looks like this. And when you use eval like this, imagine it
advice Var A and not A. When you use an eval like this, it’s triggered by whenever you
use an expression to express the eval function or you assign eval to a variable than the
original. When you access directly, then it runs in
the global scope which is retarded but it’s part of the spec. We should be happy about that. Regarding the event, what we do is we overload
the properties in the browser that scripts use to determine whether or not the loading
has finished yet because jQuery, for example, checks the ready state, and if it says the
ready state is complete, then I will start executing my scripts right away. We need to mask the ready state as well. We’re not sure when the unload event happens
because that depends on when images are loped so it might happen during script execution
or afterwards, or before. So what we do is mask the unload event, like
stop it from propagating from any of the event handlers that are installed by the scripts. Then we run the scripts using eval again,
then we clean up after ourselves and trigger ourselves manually. The trigger event is not in the browser, but
it’s just takes a couple of lines in order to trigger an event. That’s why I didn’t put it in here. For document.write, it’s another gem. It’s a bit more complicated than that. For example, if you document.write a script,
you want the script to run, and there are a couple of other edge cases, but generally,
we mask the document will be the Wight function that you try to write, and after the script
has executed, away insert it into the DOM manually. The things work more or less as they did before. Remember the thing with Google Analytics? So, Google Analytics uses a script to insert
another script dynamically into the page. What we want to do is we want to proxy it
on the server side to extend the cache duration but we can’t do that easily because we don’t
know the SSE of the script before we insert the loading script into the page. So we overwrite a pen child so we can filter
the insertions into the page and change the SSE. Pretty, isn’t it? There are a couple of more features but I’m
not going to go over that because this was the most interesting part. Removing comments and what else did we not
cover? Inlining small images like logos that you
don’t need to learn externally because they’re a few K. If you have Google maps, YouTube, those are
delayed as well. So what you get with this approach is a very
fast first render. Now, which on most sides works very well because
none of the visuals are dependent on JavaScript. And people can start looking at the page and
deciding what kind of interaction they want to do before it has finished loading. I would also – this is a bit of a psychological
thing, because it’s not that important. But it does help with that. And it saves bandwidth due to the optimisation
and minification, and those are things that you can easily do without such a convoluted
approach. Of course, the best place to use this, discussed
like when you’re not in control of the application, like if you’re using WordPress or CMS or if
you don’t have the time or will to dig down into the application and change stuff. Downsides are there, of course. It takes about 60 to 80 milliseconds to do
the processing. On the server side before outputting the page. Debugging is a little more difficult because
of the way that scripts are executed so you don’t know where an error is. It’s pretty to overcome by disabling it for
a second. You can do it with an URL parameter. If your application is dependent on JavaScript
for the rendering, then it is not going to help you, because you need the JavaScript
to execute, so then you’re on your own. Yes, I wanted to go over the comparisons but
we have only five minutes. I’m going to leave that in case there are
no questions. So, there is GitHub, of course. You can compose and install. It’s GPL. There’s a WordPress extension, plugin, which
is used by 800 people now. Which is not a lot, but it works at least. Like if it works on WordPress, it should work
in your application because it won’t be much more fucked up! That’s it, basically. [Applause]. Questions? No questions? Okay. There’s a question.>>[Inaudible].>>So the question is regarding the CSS optimisation,
if I have a script on my page that then creates markup that uses the CSS class that’s not
in the optimised CSS because it wasn’t on the page originally, will that still work? The answer to that is of course yes. That will still work because the way it works
is that we send the initial optimised CSS for the initial render, but immediately after
that, we also fire an asynchronous go to get the original CSS and replace the optimised
CSS with the original CSS in the page before executing the scripts. If that makes any sense? Any other questions? Right. Actually, I don’t know how many of you are
familiar with PageSpeed. Have any of you used this? It’s a server-side module for Apache that
does a few of the same optimisations and different ones. I used this before before we started working
on this tool. It works okay. There are a couple of downsides to it that
made me decide to want to do it differently. First of all, it works probabilistically with
metrics to perform a given optimisation at a point. You can configure it away but it’s kind of
complicated. We want something that does the implications
every time because we wanted to go over the perfect score. Besides with the PHP solution, it’s PHP so
it’s easy to edit, it’s not C. Page is written in C. You don’t have to compile it for your
web server. If you don’t have access to your server, you
can configure it without that. And that also calls it to be easier to set
up and us you do compose-install and you’re done. It’s also painful to update your web server
when you have PageSpeed installed. That’s it. If there are no more questions, then we’re
done. [Applause]. Anybody who likes to talk performance can
chat to me afterwards.

Leave a Reply

Your email address will not be published. Required fields are marked *