<?xml version='1.0' encoding='UTF-8'?><rss xmlns:atom="http://www.w3.org/2005/Atom" xmlns:openSearch="http://a9.com/-/spec/opensearchrss/1.0/" xmlns:blogger="http://schemas.google.com/blogger/2008" xmlns:georss="http://www.georss.org/georss" xmlns:gd="http://schemas.google.com/g/2005" xmlns:thr="http://purl.org/syndication/thread/1.0" version="2.0"><channel><atom:id>tag:blogger.com,1999:blog-1356169494544655761</atom:id><lastBuildDate>Wed, 14 Oct 2026 11:56:03 +0000</lastBuildDate><category>PHP</category><category>servers</category><category>linux</category><category>microsoft</category><category>open source</category><category>programming</category><category>Laravel</category><category>security</category><category>web development</category><category>devops</category><category>performance</category><category>SEO</category><category>nginx</category><category>postgres</category><category>Varnish</category><category>hhvm</category><category>javascript</category><category>logging</category><category>mobile</category><category>toys</category><category>Doctrine</category><category>adventures in IT</category><category>apache</category><category>architecture</category><category>caching</category><category>cakePHP</category><category>centos</category><category>crazy monkey man</category><category>database</category><category>docker</category><category>ssh</category><category>steve ballmer</category><category>testing</category><category>virtual</category><category>.mobi</category><category>C#</category><category>CK editor</category><category>SMIL</category><category>UIX</category><category>amazon</category><category>android</category><category>angular 2</category><category>ansible</category><category>api</category><category>asp</category><category>asterisk</category><category>azure</category><category>backlinks</category><category>bitcoin</category><category>blockchain</category><category>compression</category><category>ddd</category><category>deploy</category><category>domain driven design</category><category>drupal</category><category>github</category><category>grok</category><category>html</category><category>jenkins</category><category>logstash</category><category>media</category><category>monitoring</category><category>monolog</category><category>mpeg-ts</category><category>oop</category><category>orm</category><category>pci</category><category>phoenix</category><category>pi</category><category>puppet</category><category>python</category><category>queue</category><category>raspberry</category><category>ruby</category><category>soap</category><category>solid</category><category>spam</category><category>ssl</category><category>standards</category><category>streaming</category><category>twitter</category><category>ubuntu</category><category>vlc</category><category>vm</category><category>wordpress</category><category>zabbix</category><title>Adventures in IT</title><description></description><link>http://blog.mystery.co.za/</link><managingEditor>noreply@blogger.com (Andrew Beak)</managingEditor><generator>Blogger</generator><openSearch:totalResults>108</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><item><guid isPermaLink="false">tag:blogger.com,1999:blog-1356169494544655761.post-7344223976455162507</guid><pubDate>Thu, 22 Jul 2021 08:41:00 +0000</pubDate><atom:updated>2021-07-22T09:44:37.523+01:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">architecture</category><category domain="http://www.blogger.com/atom/ns#">ddd</category><category domain="http://www.blogger.com/atom/ns#">domain driven design</category><category domain="http://www.blogger.com/atom/ns#">solid</category><title>Translating a bit of the idea behind domain driven design into code architecture</title><description> I&#39;ve often participated in &lt;strike&gt;arguments&lt;/strike&gt; discussions about whether thin models or thin controllers should be preferred.&amp;nbsp;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The wisdom of a thin controller is that if you need to test your controller in isolation then you need to stub the dependencies of your request and response. It also violates the single responsibility principal because the controller could have multiple reasons to change.&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&amp;nbsp;Seemingly, the alternative is to settle on having fat models. This results in having domain logic right next to your persistence logic. If you ever want to change your persistence layer you&#39;re going to be in for a painful time. That&#39;s a bit of a cargo cult argument because honestly who does that, but it&#39;s also a violation of the single responsibility principal.&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&amp;nbsp;One way to decouple your domain logic from both persistence and controller is to use the &quot;repository pattern&quot;.&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjfHxC_KS3xgT7myj-LUA5IjK6QbeMokkr8H4rDEq5m9keDvZjQDLvLTYZufBrKDz25XO4tI6ObCYInj2Mibn3NNLBshwYAy3T6-BLlb4eIe4f3DkPnGnmI51l5Z2AFlU1JHTKxzx-A4_Qk/s561/repositorypattern.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;561&quot; data-original-width=&quot;531&quot; height=&quot;320&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjfHxC_KS3xgT7myj-LUA5IjK6QbeMokkr8H4rDEq5m9keDvZjQDLvLTYZufBrKDz25XO4tI6ObCYInj2Mibn3NNLBshwYAy3T6-BLlb4eIe4f3DkPnGnmI51l5Z2AFlU1JHTKxzx-A4_Qk/s320/repositorypattern.jpg&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&amp;nbsp;Here we encapsulate domain logic into a data service. This layer deals exclusively with implementing your domain logic and is entirely reliant on using the repository layer to fetch data.&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The repository layer takes care of sorting, aggregating, and filtering your data. It doesn&#39;t have intelligence beyond these basic functions. It is unable to make a call directly to the actual data store but rather uses an entity to do so. This keeps the mechanism of retrieving data from storage separate from the logic required to massage it into a shape that is useful to your data service.&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&amp;nbsp;In code I usually namespace the domain to keep it clear what I&#39;m trying to do. So I&#39;d end up with namespaces something like this:&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;blockquote style=&quot;border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;&quot;&gt;&lt;div&gt;&lt;span style=&quot;font-family: courier;&quot;&gt;&amp;nbsp;\App\Domain\Service
&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;font-family: courier;&quot;&gt;&amp;nbsp;\App\Domain\Repository
&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;font-family: courier;&quot;&gt;&amp;nbsp;\App\Domain\Entity
&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;font-family: courier;&quot;&gt;&amp;nbsp;\App\Domain\Contract&amp;nbsp;&lt;/span&gt;&lt;/div&gt;&lt;/blockquote&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&amp;nbsp;The &quot;Contract&quot; namespace is where I keep my interfaces. I personally like grouping them up like that, but your mileage may vary.&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&amp;nbsp;If you&#39;re looking for more concrete code examples, I blogged about this many years ago (&lt;a href=&quot;https://blog.mystery.co.za/2015/01/separating-business-logic-from.html&quot; target=&quot;_blank&quot;&gt;here&lt;/a&gt;). I&#39;m not sure that adding code to the explanation actually helps to improve understanding though. The principal of what we&#39;re trying to do is to decouple our domain logic from our persistence mechanism and our controller logic. We want to adhere to SOLID principals and have code that we can test and maintain without swearing.&lt;/div&gt;</description><link>http://blog.mystery.co.za/2021/07/translating-bit-of-idea-behind-domain.html</link><author>noreply@blogger.com (Andrew Beak)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjfHxC_KS3xgT7myj-LUA5IjK6QbeMokkr8H4rDEq5m9keDvZjQDLvLTYZufBrKDz25XO4tI6ObCYInj2Mibn3NNLBshwYAy3T6-BLlb4eIe4f3DkPnGnmI51l5Z2AFlU1JHTKxzx-A4_Qk/s72-c/repositorypattern.jpg" height="72" width="72"/><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-1356169494544655761.post-3224650672475425050</guid><pubDate>Mon, 11 Nov 2019 17:34:00 +0000</pubDate><atom:updated>2019-11-12T10:01:55.602+00:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">docker</category><category domain="http://www.blogger.com/atom/ns#">grok</category><category domain="http://www.blogger.com/atom/ns#">logging</category><category domain="http://www.blogger.com/atom/ns#">logstash</category><category domain="http://www.blogger.com/atom/ns#">monolog</category><category domain="http://www.blogger.com/atom/ns#">PHP</category><title>Grokking PHP monolog context into Elastic</title><description>&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
An indexed and searchable centralized log is one of those tools that once you&#39;ve had it you&#39;ll wonder how you managed without it.&amp;nbsp; &lt;br /&gt;
&amp;nbsp; &lt;br /&gt;
I&#39;ve experienced a couple of advantages to using a central log - debugging, monitoring performance, and catching unknown problems.&lt;br /&gt;
&lt;br /&gt;
&lt;h4&gt;
Debugging&lt;/h4&gt;
Debugging becomes easier because instead of poking around grepping text logs on servers you&#39;re able to 
use a GUI to contrast and compare values between different time 
ranges.&lt;br /&gt;
&lt;br /&gt;
A ticket will often include sparse information about the 
problem and observed error, but if you know more or less when a problem 
occurred then you can check the logs of all your systems at that time.&lt;br /&gt;
&lt;br /&gt;
Problem behaviour in your application can occur as a result of the 
services you depend on.&amp;nbsp; A database fault will produce errors in your 
application, for example.&lt;br /&gt;
&lt;br /&gt;
If you log your database errors and your application errors in the same central platform then it&#39;s much more convenient to compare behaviour between the systems.&lt;br /&gt;
&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;clear: right; float: left; margin-bottom: 1em; margin-right: 1em; text-align: left;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEha2NlBigMTRnX3dRInL7MjUyys0vLFqMhZXbT5ZLqDsxy2lvKY9LR2c9z5sEw7smLdKQbunkbhCyQZ3z136bklvuH0s4YjjX_tM-wE4fzMKTAlTVfYwds0FElYp2Sf-G_U2J04Y42686V2/s1600/Astronaut-graffiti-tshirt01-HQ-01.png&quot; imageanchor=&quot;1&quot; style=&quot;clear: right; margin-bottom: 1em; margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;1080&quot; data-original-width=&quot;900&quot; height=&quot;320&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEha2NlBigMTRnX3dRInL7MjUyys0vLFqMhZXbT5ZLqDsxy2lvKY9LR2c9z5sEw7smLdKQbunkbhCyQZ3z136bklvuH0s4YjjX_tM-wE4fzMKTAlTVfYwds0FElYp2Sf-G_U2J04Y42686V2/s320/Astronaut-graffiti-tshirt01-HQ-01.png&quot; width=&quot;266&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;&lt;br /&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;br /&gt;
I&#39;ve often found that knowing the application context makes it a lot easier to debug a problem.&amp;nbsp; I can also more easily reason about the path my code would have taken.&amp;nbsp; If I&#39;m feeling energetic I can make an attempt to reproduce it and play it back in slow motion with my debugger.&lt;br /&gt;
&lt;h4&gt;
Monitoring performance &lt;/h4&gt;
Indexing your logs lets you use a query language to search them.&amp;nbsp; All of the popular logging platforms include a DSL that lets you perform aggregations and other statistical functions on values contained in your logs.&lt;br /&gt;
&lt;br /&gt;
This makes it a lot easier for your team to ask and answer questions about historical behaviour.&lt;br /&gt;
&lt;br /&gt;
You can very easily ask &quot;find me a list of all endpoints that take longer than 0.5 seconds to respond&quot;, or &quot;find me all the requests that took longer than two standard deviations above mean to respond to&quot;.&lt;br /&gt;
&lt;br /&gt;
Central logging doesn&#39;t enable you to answer questions, you could 
already do that manually.&amp;nbsp; Central logging makes it easy to answer 
questions, which encourages you to ask more of them. &lt;br /&gt;
&lt;br /&gt;
Being able to query patterns makes it easier to identify client use patterns that are harmful to your server.&amp;nbsp; This lets you mitigate them on either the server or the client.&lt;br /&gt;
&lt;br /&gt;
You could notice, for example, that a client is not respecting your cache control headers and is performing a repeat call to the API when it should be relying on a local cache.&amp;nbsp; This lets you engage the client developers with alternatives and SDK&#39;s to help them take advantage of your caching strategy.&lt;br /&gt;
&lt;br /&gt;
&lt;h4&gt;
Managing verbosity and unknowns &lt;/h4&gt;
Anomaly detection is a powerful addition to central logging.&amp;nbsp; Logging is great for analyzing historical performance, but is too verbose to be useful for monitoring in any level of detail.&amp;nbsp; &lt;br /&gt;
&lt;br /&gt;
Machine learning algorithms can analyze your logs and find outliers in message frequency or values.&amp;nbsp; They can do this quite cheaply in real time after an initial learning process.&amp;nbsp; Your central logs can become an early warning system for problems that your customers haven&#39;t reported yet.&lt;br /&gt;
&lt;br /&gt;
I&#39;m a fan of &lt;a href=&quot;https://unomaly.com/&quot; target=&quot;_blank&quot;&gt;Unomaly&lt;/a&gt;, but there are lots of other options out there, Splunk and Elastic both include machine learning options for example.&lt;br /&gt;
&lt;br /&gt;
&lt;h4&gt;
A PHP implementation &lt;/h4&gt;
So now that you&#39;re convinced that central logging will improve your debugging, allow you to identify performance bottlenecks, and make auditing more convenient what is the next step in your PHP project? &lt;br /&gt;
&lt;br /&gt;
&lt;a href=&quot;https://github.com/Seldaek/monolog&quot; target=&quot;_blank&quot;&gt;Monolog &lt;/a&gt;is the de facto standard PHP logging library. Part of its fancy features include the ability to log a contextual array of information, which is provided as a second parameter, like this:&lt;br /&gt;
&lt;br /&gt;
&lt;pre style=&quot;background-color: white; color: black; font-family: &#39;DejaVu Sans Mono&#39;; font-size: 11.3pt;&quot;&gt;&lt;span style=&quot;background-color: #f7faff; color: #660000;&quot;&gt;$log&lt;/span&gt;&lt;span style=&quot;background-color: #f7faff;&quot;&gt;-&amp;gt;info(&lt;/span&gt;&lt;span style=&quot;background-color: #f7faff; color: green; font-weight: bold;&quot;&gt;&quot;Example of something being logged&quot;&lt;/span&gt;&lt;span style=&quot;background-color: #f7faff;&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;background-color: #f7faff; color: #660000;&quot;&gt;$visitDetails&lt;/span&gt;&lt;span style=&quot;background-color: #f7faff;&quot;&gt;);&lt;/span&gt;
&lt;/pre&gt;
&lt;br /&gt;
This results in the array being output as a JSON string at the end of your log message and can be very useful indeed.&lt;br /&gt;
&lt;br /&gt;
Some examples of the useful information you can put in the context could be a request id that lets you group log entries from any particular request.&amp;nbsp; You&#39;d assign a request a unique id when you first start processing it, and then consistently include this id in your logs.&lt;br /&gt;
&lt;br /&gt;
Monolog lets you push logs straight into Elastic, but if we push our logs to a local logstash instance then we save ourselves some network I/O and allow for some buffering in the event that Elastic is down.&lt;br /&gt;
&lt;br /&gt;
Logstash also lets you grok your logs and massage them into a form that Elastic can understand.&lt;br /&gt;
&lt;br /&gt;
Actually writing the grok pattern is a lot like writing a regex pattern, but luckily there are some tools that will greatly speed this up for you.&lt;br /&gt;
&lt;br /&gt;
Take a look at:&lt;br /&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;https://grokdebug.herokuapp.com/&quot;&gt;https://grokdebug.herokuapp.com/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/logstash-plugins/logstash-patterns-core/blob/master/patterns/grok-patterns&quot;&gt;https://github.com/logstash-plugins/logstash-patterns-core/blob/master/patterns/grok-patterns&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/kkos/oniguruma/blob/master/doc/RE&quot;&gt;https://github.com/kkos/oniguruma/blob/master/doc/RE&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
Oniguruma lets you use regex in the Grok pattern, like this: &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;(?&amp;lt;field_name&amp;gt;the pattern here)&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
For my out of the box installation of Monolog the logstash configuration looked like this:&lt;br /&gt;
&lt;br /&gt;
&lt;script src=&quot;https://gist.github.com/andybeak/5ab3def0fb060e6d6b5045bda4de5571.js?file=logstash.conf&quot;&gt;&lt;/script&gt;

The aim here is to use grok to tokenize your log line into fields.  You can then use the json filter to further expand any json fields that you&#39;ve grokked.&lt;br /&gt;
&lt;br /&gt;
In my example the field &quot;context&quot; is grokked from the context that can be added as &quot;extra data&quot;.  See the &lt;a href=&quot;https://github.com/Seldaek/monolog/blob/master/doc/01-usage.md#adding-extra-data-in-the-records&quot; rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot;&gt;Monolog manual&lt;/a&gt; for details.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
I was able to get Monolog to push logs to Logstash by using a SockerHandler, like this:&lt;br /&gt;
&lt;script src=&quot;https://gist.github.com/andybeak/5ab3def0fb060e6d6b5045bda4de5571.js?file=index.php&quot;&gt;&lt;/script&gt;

&lt;br /&gt;
When you mix this up with AWS managed Elasticsearch (&lt;a href=&quot;https://github.com/awslabs/logstash-output-amazon_es&quot; target=&quot;_blank&quot;&gt;and their plugin&lt;/a&gt;) you can very quickly get up and running with Kibana.&lt;br /&gt;
&lt;br /&gt;
Example repository is at &lt;a href=&quot;https://github.com/andybeak/monolog-elk-demo&quot;&gt;https://github.com/andybeak/monolog-elk-demo&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;/div&gt;
</description><link>http://blog.mystery.co.za/2019/11/grokking-php-monolog-context-into.html</link><author>noreply@blogger.com (Andrew Beak)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEha2NlBigMTRnX3dRInL7MjUyys0vLFqMhZXbT5ZLqDsxy2lvKY9LR2c9z5sEw7smLdKQbunkbhCyQZ3z136bklvuH0s4YjjX_tM-wE4fzMKTAlTVfYwds0FElYp2Sf-G_U2J04Y42686V2/s72-c/Astronaut-graffiti-tshirt01-HQ-01.png" height="72" width="72"/><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-1356169494544655761.post-224615615125981290</guid><pubDate>Sat, 20 Oct 2018 11:55:00 +0000</pubDate><atom:updated>2018-10-20T12:55:54.563+01:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">media</category><category domain="http://www.blogger.com/atom/ns#">mpeg-ts</category><category domain="http://www.blogger.com/atom/ns#">pi</category><category domain="http://www.blogger.com/atom/ns#">raspberry</category><category domain="http://www.blogger.com/atom/ns#">streaming</category><category domain="http://www.blogger.com/atom/ns#">vlc</category><title>Streaming MPEG-TS to your Raspberry Pi</title><description>&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;table cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;clear: right; float: left; margin-bottom: 1em; text-align: right;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhbdSoss_1zs-cc9kpPgyJFmJXZfNmhx8dG0CPGLVJN6KbHSBXJldN2AhWa-Mmr_nnQpSO8OFthBjt0HiAyBjc5qgrPv7NSc8ShyphenhyphenpOevaIRRKWvmr7Fd-KQPDtei0HAUG8ufUtGU_KBbIYt/s1600/Raspberry-Penguin.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;1600&quot; data-original-width=&quot;1068&quot; height=&quot;200&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhbdSoss_1zs-cc9kpPgyJFmJXZfNmhx8dG0CPGLVJN6KbHSBXJldN2AhWa-Mmr_nnQpSO8OFthBjt0HiAyBjc5qgrPv7NSc8ShyphenhyphenpOevaIRRKWvmr7Fd-KQPDtei0HAUG8ufUtGU_KBbIYt/s200/Raspberry-Penguin.png&quot; width=&quot;133&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;Raspberry Penguin&lt;br /&gt;CC by &lt;a href=&quot;https://openclipart.org/detail/191477/raspberry-penguin&quot; id=&quot;src&quot; target=&quot;_blank&quot; title=&quot;View page&quot;&gt;openclipart.org&lt;/a&gt;&lt;span class=&quot;meta_sp&quot;&gt;&lt;/span&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
Usually people focus on getting camera streams off their Pi, but I 
wanted to set up a test environment that would let me play with some 
encoding and distribution software.&lt;br /&gt;
&lt;br /&gt;
 
For the client in this scenario I&#39;m running a Raspberry Pi 3 Model B 
which I have plugged into an ethernet cable on my home network. I&#39;m 
using my Ubuntu 18.04 laptop to serve up the multicast stream.&lt;br /&gt;
&lt;br /&gt;
 
If you&#39;re new to multicast the first piece of reading you should do is to read about the reserved IP ranges on &lt;a href=&quot;https://en.wikipedia.org/wiki/Multicast_address&quot; rel=&quot;nofollow noopener&quot; target=&quot;_blank&quot;&gt;Wikipedia&lt;/a&gt;.
 You can&#39;t broadcast on just any old address, but you do have a very 
wide selection to choose from. I decided to broadcast on 
udp://239.255.0.1 and port 5000, because why not?&lt;br /&gt;
&lt;br /&gt;
 
I downloaded a video from https://www.stockio.com/free-videos/ and 
saved it into a folder. From there all you need to do is run ffmpeg on 
the server, thusly:&lt;br /&gt;
&lt;br /&gt;
&lt;pre spellcheck=&quot;false&quot;&gt;ffmpeg -re -i costa.mp4 -bsf:v h264_mp4toannexb -acodec &lt;span class=&quot;hljs-built_in&quot;&gt;copy&lt;/span&gt; -vcodec &lt;span class=&quot;hljs-built_in&quot;&gt;copy&lt;/span&gt; -f mpegts udp:&lt;span class=&quot;hljs-comment&quot;&gt;//239.255.0.1:5000&lt;/span&gt;&lt;/pre&gt;
&amp;nbsp; &lt;br /&gt;
There are actually some parameters in there that ffmpeg gave me in 
its error output when I tried to play the file. If you use the same file
 as me then you need to include them, otherwise you might not need to.&lt;br /&gt;
&lt;br /&gt;
 
Once ffmpeg had started streaming the file I quickly opened up VLC on
 the Pi and tuned to the network stream that my laptop was streaming on.
 Hurray! Everything is working, my UDP multicast is up and running, now I
 can play with some more advanced features.&lt;br /&gt;
&lt;br /&gt;
 
Here is a screenshot from my Pi:&lt;br /&gt;
&lt;br /&gt;
&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto; text-align: center;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhQd7F1SXkCEI9sLuSMXHYghQI8qIaSsBt_qjzfBWcaVpwOP1RxfT0wh1sw11ucnPruVlmbUghucIy-oVPjixc7ZO4pyOq3TztgDk3OWaNgEssVU5ME9Hh6-Z9ImBGv9UXaNJnbwkAU39_T/s1600/pi.PNG&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;1000&quot; data-original-width=&quot;1600&quot; height=&quot;250&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhQd7F1SXkCEI9sLuSMXHYghQI8qIaSsBt_qjzfBWcaVpwOP1RxfT0wh1sw11ucnPruVlmbUghucIy-oVPjixc7ZO4pyOq3TztgDk3OWaNgEssVU5ME9Hh6-Z9ImBGv9UXaNJnbwkAU39_T/s400/pi.PNG&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;
        
          &lt;figure class=&quot;reader-cover-image--full-bleed relative&quot;&gt;&lt;figcaption class=&quot;reader-cover-image__caption t-12 t-black--light t-normal&quot; itemprop=&quot;caption&quot;&gt;Captured from Raspberry Pi, video from https://www.stockio.com/free-videos/&lt;/figcaption&gt;&lt;/figure&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
In case you don&#39;t want to have to dash over to your other window to see 
the stream before the clip ends you should know that ffmpeg is able 
generate test patterns for you. If you&#39;re like me and don&#39;t want to 
spend too much time reading the &lt;a href=&quot;https://trac.ffmpeg.org/wiki/FilteringGuide&quot; rel=&quot;nofollow noopener&quot; target=&quot;_blank&quot;&gt;ffmpeg manual&lt;/a&gt;, I found &lt;a href=&quot;https://news.mistserver.org/news/88/Generating+a+live+test+stream+from+a+server+using+command+line&quot; rel=&quot;nofollow noopener&quot; target=&quot;_blank&quot;&gt;another site&lt;/a&gt; that has a good little snippet that you can just copy/paste. &lt;br /&gt;
</description><link>http://blog.mystery.co.za/2018/10/streaming-mpeg-ts-to-your-raspberry-pi.html</link><author>noreply@blogger.com (Andrew Beak)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhbdSoss_1zs-cc9kpPgyJFmJXZfNmhx8dG0CPGLVJN6KbHSBXJldN2AhWa-Mmr_nnQpSO8OFthBjt0HiAyBjc5qgrPv7NSc8ShyphenhyphenpOevaIRRKWvmr7Fd-KQPDtei0HAUG8ufUtGU_KBbIYt/s72-c/Raspberry-Penguin.png" height="72" width="72"/><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-1356169494544655761.post-8278700210823286003</guid><pubDate>Sat, 29 Sep 2018 21:15:00 +0000</pubDate><atom:updated>2018-09-29T22:15:17.522+01:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">azure</category><category domain="http://www.blogger.com/atom/ns#">python</category><category domain="http://www.blogger.com/atom/ns#">security</category><title>Using Azure Active directory as an OAuth2 provider for Django</title><description>Azure Active Directory is a great product and is invaluable in the 
enterprise space. In this article we&#39;ll be setting it up to provide 
tokens for the OAuth2 client credentials grant. This authorization flow 
is useful when you want to authorize server-to-server communication that
 might not be on behalf of a user.&lt;br /&gt;
&lt;br /&gt;
This diagram, by Microsoft, shows the client credentials grant flow.&lt;br /&gt;
&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto; text-align: center;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgqUBe_W_O9rx5vEpI0kwk2lR-FlyUhizrE9867YqUIPdod7A5nAaHChAhTdxPXHFDFiRYrkH6NcaY_9-Q7BRaD1fiIHOKRrmt-kOCDOjETvykr4t2IgirPVzV7XoyiwGHpVAXUWjh4r3vK/s1600/active-directory-protocols-oauth-client-credentials-grant-flow.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;319&quot; data-original-width=&quot;864&quot; height=&quot;147&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgqUBe_W_O9rx5vEpI0kwk2lR-FlyUhizrE9867YqUIPdod7A5nAaHChAhTdxPXHFDFiRYrkH6NcaY_9-Q7BRaD1fiIHOKRrmt-kOCDOjETvykr4t2IgirPVzV7XoyiwGHpVAXUWjh4r3vK/s400/active-directory-protocols-oauth-client-credentials-grant-flow.jpg&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;From &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/active-directory/develop/v1-oauth2-client-creds-grant-flow&quot; target=&quot;_blank&quot;&gt;Microsoft documentation&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&amp;nbsp;The flow goes like this:&lt;br /&gt;
&lt;ol&gt;
&lt;li&gt;The client sends a request to Azure AD for a token&lt;/li&gt;
&lt;li&gt;Azure AD verifies the attached authentication information and issues an access token&lt;/li&gt;
&lt;li&gt;The client calls the API with the access token. The API server is 
able to verify the validity of the token and therefore the identity of 
the client.&lt;/li&gt;
&lt;li&gt;The API responds to the client&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;
Setting up Azure AD as an OAuth2 identity provider&lt;/h2&gt;
The first step is to create applications in your AD for both your API server and the client. You can find step-by-step instructions on how to register the applications on the Microsoft page at &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/active-directory/develop/quickstart-v1-integrate-apps-with-azure-ad&quot; rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot;&gt;Integrating applications with Azure Active Directory.&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
We need to generate a key for the client. From your AD blade in the Azure portal click on &quot;app registrations&quot; and then show all of them. Select the client application and then open it&#39;s settings. Create a new key and make sure you copy its value after saving.&lt;br /&gt;
&lt;br /&gt;
Now let&#39;s add the permissions that our client will ask for. For this article we&#39;re just going to use a basic &quot;access&quot; scope. We&#39;re not going to add custom scopes to the server application, but this is easily possible by editing the manifest of the server application.&lt;br /&gt;
&lt;br /&gt;
From the settings page of the client application click on &quot;Required permissions&quot;. Click &quot;Add&quot; and in the search box type in the name of the server application that you registered in AD. Select it from the list that appears and choose &quot;access&quot; under the delegated permissions.&lt;br /&gt;
&lt;br /&gt;
Now we have a fully fledged OAuth2 service set up and running, which is awesome. Let&#39;s call the Azure AD endpoint to get a token:&lt;br /&gt;
&lt;br /&gt;
&lt;script src=&quot;https://gist.github.com/andybeak/9c0163053c5c0296770b8968d0ddf1aa.js?file=curl_command.sh&quot;&gt;&lt;/script&gt;

You can get the application id by opening up the registered application from your Azure AD portal blade.&lt;br /&gt;
&lt;pre spellcheck=&quot;false&quot;&gt;&lt;/pre&gt;
Azure should respond with a JSON body that looks something like this:&lt;br /&gt;
&lt;br /&gt;
&lt;script src=&quot;https://gist.github.com/andybeak/9c0163053c5c0296770b8968d0ddf1aa.js?file=azure_response.json&quot;&gt;&lt;/script&gt;

&lt;br /&gt;
The access_token value will be a JWT token. If you haven&#39;t used them before go check out &lt;a href=&quot;https://jwt.io/&quot; rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot;&gt;jwt.io&lt;/a&gt;. This is the access token that you will present to your Django backend.&lt;br /&gt;
&lt;br /&gt;
If you copy the access token into the decoder on jwt.io then you&#39;ll see that the payload looks like this:&lt;br /&gt;
&lt;br /&gt;
&lt;script src=&quot;https://gist.github.com/andybeak/9c0163053c5c0296770b8968d0ddf1aa.js?file=jwt_payload.json&quot;&gt;&lt;/script&gt;

&lt;h2&gt;
Interlude&lt;/h2&gt;
Let&#39;s take a breather and watch a music video. Because, really, 8 bits should be enough for anybody.&lt;br /&gt;
&lt;br /&gt;
&lt;iframe allow=&quot;autoplay; encrypted-media&quot; allowfullscreen=&quot;&quot; frameborder=&quot;0&quot; height=&quot;562&quot; src=&quot;https://www.youtube.com/embed/GoGpLl-SUfk&quot; width=&quot;749&quot;&gt;&lt;/iframe&gt;

Okay, we&#39;re back and we&#39;re ready to get Django to authenticate the token.&lt;br /&gt;
&lt;h2&gt;
Validating the token in Django&lt;/h2&gt;
I&#39;m not going to go into the details of setting up your Django project. There is already an excellent &lt;a href=&quot;https://www.djangoproject.com/start/&quot; rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot;&gt;tutorial on their site&lt;/a&gt;. I&#39;m going to assume that you have an app in your project to handle the API.&lt;br /&gt;
&lt;br /&gt;
What we need to do is write some custom middleware for Django that will get the access token out of the &quot;Authorization&quot; HTTP header, validate the JWT, and check the signature against Azure&#39;s published signature. You can find out more about this on &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/active-directory/develop/v1-id-and-access-tokens#validating-tokens&quot; rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot;&gt;Microsoft&#39;s website&lt;/a&gt;, but this is actually an OAuth2 standard approach so just about any standards compliant documentation will do.&lt;br /&gt;
&lt;br /&gt;
We&#39;ll be using standard libraries to decode the JWT, but in order to properly verify it we will need to have the key. We don&#39;t want to hardcode the key into our API because Azure rotates their keys on a regular basis. Luckily the OAuth2 standard provides &lt;a href=&quot;https://tools.ietf.org/html/draft-ietf-oauth-discovery-06&quot; rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot;&gt;a defined way&lt;/a&gt; for an OAuth2 provider to share this sort of information.&lt;br /&gt;
&lt;br /&gt;
Azure publishes it&#39;s OAuth2 configuration file at &lt;a href=&quot;https://login.microsoftonline.com/common/.well-known/openid-configuration&quot; rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot;&gt;https://login.microsoftonline.com/common/.well-known/openid-configuration&lt;/a&gt;. There is also a configuration file that is specific to your tenant at &lt;a href=&quot;https://login.microsoftonline.com/%7Btenant%7D/.well-known/openid-configuration&quot; rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot;&gt;https://login.microsoftonline.com/{tenant}/.well-known/openid-configuration&lt;/a&gt;, but the general one is fine for us.&lt;br /&gt;
&lt;br /&gt;
In that document there is a key called &quot;jwks_url&quot; whose value is a url. On that url you will find the keys that Azure is currently using. We need to retrieve that document and find the key that was used in our access token.&lt;br /&gt;
&lt;br /&gt;
Right, so that&#39;s the plan. Decode the JWT to find what key was used to sign it and go fetch the key from Microsoft to verify the token. If anybody is able to trick our DNS and serve us content that we think is from Microsoft then they&#39;ll be able to pwn us, but otherwise as long as we trust the response from Microsoft we should be able to trust the JWT token the client gave us.&lt;br /&gt;
&lt;br /&gt;
I placed this into settings.py because swapping Azure AD for any other standard implementation of OAuth2 should be possible by just swapping the configuration endpoint. I also set up Memcached while I was busy in the settings, so I ended up with this extra settings information:&lt;br /&gt;
&lt;br /&gt;
&lt;script src=&quot;https://gist.github.com/andybeak/9c0163053c5c0296770b8968d0ddf1aa.js?file=settings.py&quot;&gt;&lt;/script&gt;

&lt;br /&gt;
Then I made a new file called checkaccesstoken.py and put it into the &quot;middleware&quot; directory of my API app, making sure that I registered it in settings.py&lt;br /&gt;
&lt;br /&gt;
In the __call__ method of the middleware I call out to the functions that are going to check the token:&lt;br /&gt;
&lt;script src=&quot;https://gist.github.com/andybeak/9c0163053c5c0296770b8968d0ddf1aa.js?file=call_method.py&quot;&gt;&lt;/script&gt;

&lt;br /&gt;
To get the token we use the &lt;a href=&quot;https://docs.djangoproject.com/en/dev/ref/request-response/&quot; rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot;&gt;Django request object&lt;/a&gt;:&lt;br /&gt;
&lt;br /&gt;
&lt;script src=&quot;https://gist.github.com/andybeak/9c0163053c5c0296770b8968d0ddf1aa.js?file=access_token.py&quot;&gt;&lt;/script&gt;

We make sure that the token is a Bearer token by defining a class constant called AUTH_TYPE_PREFIX as having the value &quot;Bearer&quot;.&lt;br /&gt;
&lt;br /&gt;
Now that we have the access token we need to check it. You could use any JWT library, but I chose to use &quot;pip install pyjwt cryptography&quot;. This JWT library will throw a DecodeError if the token is invalid, which we catch and respond with 401 to the user.&lt;br /&gt;
&lt;br /&gt;
&lt;script src=&quot;https://gist.github.com/andybeak/9c0163053c5c0296770b8968d0ddf1aa.js?file=check_access_token.py&quot;&gt;&lt;/script&gt;

The public key is available over the network so we want to cache it to reduce the I/O time. Microsoft suggests that it is safe to cache for about a day.&lt;br /&gt;
&lt;br /&gt;
&lt;script src=&quot;https://gist.github.com/andybeak/9c0163053c5c0296770b8968d0ddf1aa.js?file=get_public_key.py&quot;&gt;&lt;/script&gt;

Now we&#39;re done! We can send a bearer access token to our API and be authenticated. Note that we&#39;re not doing any authorization, all we&#39;re doing here is authenticating. We haven&#39;t tied this client application to any form of role in our application. That&#39;s for another day :)&lt;br /&gt;
&lt;br /&gt;
References:&lt;br /&gt;
&lt;ol&gt;
&lt;li&gt;I placed the whole &lt;a href=&quot;https://gist.github.com/andybeak/9c0163053c5c0296770b8968d0ddf1aa&quot; rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot;&gt;middleware file in a Gist&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Microsoft article on &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/active-directory/develop/v1-oauth2-client-creds-grant-flow&quot; rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot;&gt;client credentials flow&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Microsoft advice on &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/active-directory/develop/v1-id-and-access-tokens#validating-tokens&quot; rel=&quot;noopener nofollow&quot; target=&quot;_blank&quot;&gt;validating tokens&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
</description><link>http://blog.mystery.co.za/2018/09/using-azure-active-directory-as-oauth2.html</link><author>noreply@blogger.com (Andrew Beak)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgqUBe_W_O9rx5vEpI0kwk2lR-FlyUhizrE9867YqUIPdod7A5nAaHChAhTdxPXHFDFiRYrkH6NcaY_9-Q7BRaD1fiIHOKRrmt-kOCDOjETvykr4t2IgirPVzV7XoyiwGHpVAXUWjh4r3vK/s72-c/active-directory-protocols-oauth-client-credentials-grant-flow.jpg" height="72" width="72"/><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-1356169494544655761.post-6143035869360746703</guid><pubDate>Thu, 06 Sep 2018 11:43:00 +0000</pubDate><atom:updated>2018-09-06T12:48:38.304+01:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">linux</category><category domain="http://www.blogger.com/atom/ns#">servers</category><category domain="http://www.blogger.com/atom/ns#">virtual</category><title>Standardizing infra - lxd is not Docker</title><description>&lt;table cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;float: right; margin-left: 1em; text-align: right;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhwPTv0EWK68tDbRZG165C1C9O83KF3F4SJx6ePjBBzZ56WVvKo8lrKDXJ2ce5sC5sozYcvwTrrNlNn2BS7qOtfUv5ltDwFiWmvlkhP2F76sK1fcVjdFDH4eJWoMJ4VGjhbF5_gQ2iaW12y/s1600/lxd-300x276.png&quot; imageanchor=&quot;1&quot; style=&quot;clear: right; margin-bottom: 1em; margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;276&quot; data-original-width=&quot;300&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhwPTv0EWK68tDbRZG165C1C9O83KF3F4SJx6ePjBBzZ56WVvKo8lrKDXJ2ce5sC5sozYcvwTrrNlNn2BS7qOtfUv5ltDwFiWmvlkhP2F76sK1fcVjdFDH4eJWoMJ4VGjhbF5_gQ2iaW12y/s1600/lxd-300x276.png&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;LXD logo from &lt;a href=&quot;https://blog.ubuntu.com/2015/04/28/getting-started-with-lxd-the-container-lightervisor&quot; target=&quot;_blank&quot;&gt;Ubuntu blog&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
Selling a software package that is deployed to a customer data-centre
 can be challenging due to the diversity found in the physical 
infrastructure.&lt;br /&gt;
&lt;br /&gt;
It is expensive to make (and test) adjustments to the software that allow it to run in a non-standard manner.&lt;br /&gt;
&lt;br /&gt;
Maintaining a swarm of snowflakes is not a scalable business 
practice. More deployment variations means more documentation and more 
pairs of hands needed to manage them. Uniformity and automation are what
 keep our prices competitive.&lt;br /&gt;
&lt;br /&gt;
Opposing the engineering team&#39;s desire for uniformity is the 
practical need to fit our solution into the customers data-centre and 
budget. We can&#39;t define a uniform physical infrastructure that all of 
our customers must adhere to. Well, I suppose we could, but we would 
only have a very small number of customers who are willing and able to 
fit their data-centre around us.&lt;br /&gt;
&lt;br /&gt;
We are therefore on the horns of a dilemma. Specifying standard 
physical hardware is impractical and limits the sales team. Allowing the
 sales team to adjust the infrastructure means that we have to tailor 
our platform to each deployment.&lt;br /&gt;
&lt;br /&gt;
Virtualization is the rational response to this dilemma. We can have a
 standard virtualized infrastructure and work with the sales team and 
the customer to make sure that the hardware is able to support this. 
This way we&#39;ll know that our software is always deployed on the same 
platform.&lt;br /&gt;
&lt;br /&gt;
LXD is a hypervisor for containers and is an expansion of LXC, the 
Linux container technology behind Docker. It&#39;s described by Stéphane 
Graber, an Ubuntu project engineer,as a &quot;daemon exporting an 
authenticated representational state transfer application programming 
interface (REST API) both locally over a unix socket and over the 
network using https. There are then two clients for this daemon, one is 
an OpenStack plugin, the other a standalone command line tool.&quot;&lt;br /&gt;
&lt;br /&gt;
It&#39;s not a replacement for Docker, even though Linux kernel provided 
containers are at the root of both technologies. So what is LXD for? An 
LXD container is an alternative to a virtual machine running in a 
traditional hypervisor.&lt;br /&gt;
&lt;br /&gt;
With virtualization (or LXD containerization), if your customer has 
limited rack-space or a limited budget you can still sell your software 
based on a standard platform. You can take what metal is available and 
install LXD containers to partitition the resources up into separated 
&quot;machines&quot;.&lt;br /&gt;
&lt;br /&gt;
If you like, you can use Docker to manage the deployment of your 
software into these LXD containers. Docker and LXD are complementary!&lt;br /&gt;
&lt;br /&gt;
In practical terms you can use tools like Ansible to automate the 
provisioning of your LXD containers on the customers metal. You are able
 to define in code the infrastructure and platform that your software 
runs on. And that means your engineering team wins at automation and 
your sales team wins at fitting the software into the customer 
data-centre.</description><link>http://blog.mystery.co.za/2018/09/standardizing-infra-lxd-is-not-docker.html</link><author>noreply@blogger.com (Andrew Beak)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhwPTv0EWK68tDbRZG165C1C9O83KF3F4SJx6ePjBBzZ56WVvKo8lrKDXJ2ce5sC5sozYcvwTrrNlNn2BS7qOtfUv5ltDwFiWmvlkhP2F76sK1fcVjdFDH4eJWoMJ4VGjhbF5_gQ2iaW12y/s72-c/lxd-300x276.png" height="72" width="72"/><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-1356169494544655761.post-7323264212677770404</guid><pubDate>Tue, 15 May 2018 08:04:00 +0000</pubDate><atom:updated>2018-05-15T09:05:49.319+01:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">adventures in IT</category><category domain="http://www.blogger.com/atom/ns#">blockchain</category><category domain="http://www.blogger.com/atom/ns#">toys</category><title>Is blockchain &quot;web scale&quot;?</title><description>&lt;div style=&quot;height: 0px;&quot;&gt;
For something to be truly awesome, it must be &quot;web scale&quot; right? The rather excellent video just below shows how hype can blind us to the real values of a technology. It&#39;s quite a famous trope in some circles and I think is a useful parallel to the excitement about blockchain.&lt;br /&gt;
&lt;br /&gt;
In it an enthusiastic user of a new technology repeats marketing hype despite having no understanding of the technical concerns involved. Watch the video and you&#39;ll hear every piece of marketing spin that surrounds NoSQL databases.&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;iframe allowfullscreen=&quot;&quot; class=&quot;YOUTUBE-iframe-video&quot; data-thumbnail-src=&quot;https://i.ytimg.com/vi/OC1JUW0EM9w/0.jpg&quot; frameborder=&quot;0&quot; height=&quot;266&quot; src=&quot;https://www.youtube.com/embed/OC1JUW0EM9w?feature=player_embedded&quot; width=&quot;320&quot;&gt;&lt;/iframe&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;br /&gt;
I usually get very excited about new technologies but I&#39;m quite underwhelmed by blockchain. In the commercial context I see it as a great solution that really just needs to find a problem to solve.&lt;br /&gt;
&lt;br /&gt;
Lots of companies are looking for the problem that the blockchain solution will solve. Facebook has a blockchain group that is dedicated to studying blockchain and how it can be used in Facebook.&lt;br /&gt;
&lt;br /&gt;
They don&#39;t seem to have found a particularly novel use case for it and appear to be set on using it to launch a Facebook crypto-currency. This would let Facebook track your financial transactions (https://cheddar.com/videos/facebook-plans-to-create-its-own-cryptocurrency) which to me sounds like juicy information to sell to advertisers.&lt;br /&gt;
&lt;br /&gt;
I&#39;m convinced that when we finally find that problem blockchain will be a godsend, but right now outside of cryptocurrency I&#39;m struggling to see how the cost and risk of blockchain is worthwhile in the commercial context.&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;
A publicly shared database&lt;/h3&gt;
Blockchain offers a distributed ledger system, which sounds like something we want right? Everybody can look at the list of transactions at any time, and verify that a particular transaction is genuine.&lt;br /&gt;
&lt;br /&gt;
Using boring traditional data services you&#39;d be forced to have a database and expose an API that lets authenticated users access it in ways that you directly control. How dull is that!&lt;br /&gt;
&lt;br /&gt;
Like communism, decentralizing control of data is a great idea because it promotes equality and encourages distributed effort focused on the greater good. Capitalists and other realists will point out that it&#39;s not machine learning algorithms that will be the oil of the future, it&#39;s data. Facebook isn&#39;t worth billions because it has clever algorithms, but rather because it controls data about people. Data has value, are you sure you want to give it away?&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;
Immutable&lt;/h3&gt;
Broadly speaking, instead of your company having a private database that it controls access to, you&#39;re able to have a shared database that you can only control the contents of if you spend more on hardware than the rest of the world.&lt;br /&gt;
&lt;br /&gt;
Wait, what? Your blockchain consultant didn&#39;t explain to you that anybody with a botnet is going to be able to rewrite your public ledger?&lt;br /&gt;
&lt;br /&gt;
Well it&#39;s true. The way that blockchain works means that the record is only immutable if there isn&#39;t an actor who controls more than 51% of the processing power in the network.&lt;br /&gt;
&lt;br /&gt;
If you have a private blockchain you&#39;re going to need to be certain that you can throw enough resources at it to prevent malicious actors from rewriting history. Or you could only allow trusted actors to use your blockchain, which sounds very much like a traditional access method.&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;
Who will mine your blocks?&lt;/h3&gt;
The most popular use for blockchain (by far) is to provide a ledger for currency, like Bitcoin and the other coins out there. When users mine the blockchain they are rewarded with coins which ultimately they hope to be able to convert into fiat currency or material goods/services at some point. There is a direct incentive to spend money on electricity to crunch the numbers to verify Bitcoin transactions.&lt;br /&gt;
&lt;br /&gt;
If you build your own private blockchain who is going to mine the blocks? What is the value in them doing so and are you going to end up mining the blocks yourself just to get the transactions processed? How is this cheaper than a traditional database?&lt;br /&gt;
&lt;br /&gt;
Given the problems with immutability it should be pretty clear that a private blockchain is a pretty risky way to avoid boring traditional data-sharing approaches like an API. And of course that&#39;s assuming that there will be people wanting to mine your blocks.&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;
Digital smart contracts will replace lawyers&lt;/h3&gt;
Blockchain lets you dispense with traditional signed contracts with suppliers and rather enter into digitally signed contracts. They&#39;re touted to replace lawyers (https://blockgeeks.com/guides/smart-contracts/).&lt;br /&gt;
&lt;br /&gt;
Smart contracts cut out the middle-man when it comes to transferring goods and services. They&#39;re an alternative to conveyancing costs, credit-card processing fees, escrow costs, and so on. Essentially you place the asset into the trust of the computer program which will then decide where to transfer it depending on whatever conditions you program into it.&lt;br /&gt;
&lt;br /&gt;
Instead of placing your house into escrow with a lawyer who is bound by professional conduct rules and has an insured lawyer&#39;s trust fund you can use a smart contract written by anybody. That&#39;s the democratizing power of blockchain!&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjzDqc42vRcf5agp4MmDxo4hjvW0RJlrSO8dWkeqk5-CAODv0MShM9azUvdoo3-kyk8BxM4kxHPSzc1MkxUZL-B2Q8dQgIl4VumeK7zTihg1r25jN00Sg1eNLI9SqoCbEJp12J6Hv9caF6z/s1600/7cc.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;469&quot; data-original-width=&quot;625&quot; height=&quot;239&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjzDqc42vRcf5agp4MmDxo4hjvW0RJlrSO8dWkeqk5-CAODv0MShM9azUvdoo3-kyk8BxM4kxHPSzc1MkxUZL-B2Q8dQgIl4VumeK7zTihg1r25jN00Sg1eNLI9SqoCbEJp12J6Hv9caF6z/s320/7cc.jpg&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;br /&gt;
Who can forget about the Ethereum fork which happened because of faulty code in a smart contract? I&#39;m not nearly arrogant enough to assume that I can code tighter code than the guys and girls who created the DAO - are you willing to bet your house that you are?&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;
I am horribly unfair&lt;/h3&gt;
Maybe it&#39;s not fair to use a high value asset as an example for considering a smart chain. What about other use cases touted by blockchain evangelists?&lt;br /&gt;
&lt;br /&gt;
Blockchain supposedly streamlines business decisions by eliminating back-and-forth decision making. A smart contract can simply have the business rules coded into it so instead of seeking approval from humans you can just rely on the contract to grant your request.&lt;br /&gt;
&lt;br /&gt;
For example, if you need to make a business trip it can happen automatically just so long as the coded requirements are met. As long as the contract is coded to be aware of every factor that goes into the decision it can automatically approve your travel request.&lt;br /&gt;
&lt;br /&gt;
That contract won&#39;t work for requesting a new monitor for your workstation though. You&#39;ll need a different contract for that. Or maybe you can extend the old contract and just add more rules to it?&lt;br /&gt;
&lt;br /&gt;
Given the rate cards for blockchain developers very simple administrative decisions can end up taking a lot more coding effort (and money) than you really need to be dedicating to simple decisions. And what happens if a management decision needs judgment that hasn&#39;t been coded?&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;
Banks are already using smart contracts&lt;/h3&gt;
Indeed they are (https://www.barclayscorporate.com/insight-and-research/technology-and-digital-innovation/what-does-blockchain-do.html).&lt;br /&gt;
&lt;br /&gt;
Barclays sees a future where identity theft is impossible because digital identity is immutable and publicly accessible for them to read and share with law enforcement. My financial records would be available to Barclays (and whoever else can read the blockchain) and they&#39;d be able to make a decision about opening an account quicker than the time it currently takes (about an hour the last time I did it at the branch).&lt;br /&gt;
&lt;br /&gt;
I wouldn&#39;t need to take my photo identity and proof of address documents to the bank, I would just need to show that I own the private key associated with the digital identity profile. This will prevent identity theft, according to Barclays, presumably because consumer computers are secure and digital keys can&#39;t be stolen.&lt;br /&gt;
&lt;br /&gt;
Trade documents are given as a good example of how digital signing and identity can be accomplished with blockchain.&lt;br /&gt;
&lt;br /&gt;
In blockchain how do you establish identity? The digital identity is established by ownership of a private key, but how do you link that to a physical entity? Surely you need some way to link the digital identity with the physical entity before you ship the goods that your smart contract says have been paid for.&lt;br /&gt;
&lt;br /&gt;
How do I know that wallet address 0xaa8e0d3ded810704c4d8bc879a650aad50f36bc5 is actually Acme Inc trading out of London and not Pirates Ltd trading out of Antigua? Who is responsible for authenticating the digital identities in blockchain?&lt;br /&gt;
&lt;br /&gt;
You can trust the blockchain (as long as the hash power is evenly distributed) but can you trust the digital identities on it?&lt;br /&gt;
&lt;br /&gt;
Digital signing and identity can also be managed through public key cryptography where a recognized and trusted central authority signs keys after verifying the owners identity. This isn&#39;t a new arena and blockchain doesn&#39;t solve the problems that public key cryptography has.&lt;br /&gt;
&lt;br /&gt;
There are already established digital signing solutions that don&#39;t rely on snake-oil. I signed my rent agreement digitally in the UK with my landlords who live in continental Europe. I hardly think that this space is a raison d&#39;être for block chain.&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;
Public, not private blockchains&lt;/h3&gt;
It seems that my beef is with the impractical nature of using private blockchain where existing solutions are more secure and cheaper. So, what about public blockchains, like Ethereum?&lt;br /&gt;
&lt;br /&gt;
In a traditional blockchain each node on the network needs a full copy of the entire chain in order to be able to verify transactions. Without this public scrutiny the blockchain is no longer secure.&lt;br /&gt;
&lt;br /&gt;
The problem with this is that Ethereum can only process very limited amounts of transactions per seconds. Currently it runs at about 45 transactions per second, which isn&#39;t an awful lot when you share it out amongst your company, and all the people speculatively trading Ethereum.&lt;br /&gt;
&lt;br /&gt;
Ethereum is considering a sharding approach where they will decentralize the chain slightly in order to improve transaction speed. A few nodes on the network will have more authority than others. These nodes will need to have explicit trust in each other, and obviously the network will need to follow suit.&lt;br /&gt;
&lt;br /&gt;
As a company do you want to commit to this level of trust? Who are the actors controlling these nodes? Who will they be in three years time? What countries laws are they bound by?&lt;br /&gt;
&lt;br /&gt;
Data you put into the blockchain will be shared with everybody, forever. Governments don&#39;t like competition when it comes to spying on people and are passing increasingly strict privacy laws - how will you plan for compliance when you don&#39;t control your data?&lt;br /&gt;
&lt;br /&gt;
Blockchain is web-scale!&lt;br /&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;/div&gt;
</description><link>http://blog.mystery.co.za/2018/05/is-blockchain-web-scale.html</link><author>noreply@blogger.com (Andrew Beak)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://img.youtube.com/vi/OC1JUW0EM9w/default.jpg" height="72" width="72"/><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-1356169494544655761.post-70143236624912782</guid><pubDate>Thu, 28 Dec 2017 12:29:00 +0000</pubDate><atom:updated>2017-12-28T12:29:36.264+00:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">architecture</category><category domain="http://www.blogger.com/atom/ns#">PHP</category><category domain="http://www.blogger.com/atom/ns#">programming</category><title>Component cohesion</title><description>&lt;table cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;float: right; margin-left: 1em; text-align: right;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgtuEDXIltlX3AAf2qUzgthswqmpvUcWHp_SF8UiK8amZSIZXmWdaCTCWRPhaa87Xi90emS6_GxDRELtGgUSzyJ6RMcGB4HPhnnb2gCS8-SYLrCIyTahn5fqXRc9XPWbiFR4T-vn-SsfSfq/s1600/hand-523233_1920.jpg&quot; imageanchor=&quot;1&quot; style=&quot;clear: right; margin-bottom: 1em; margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;730&quot; data-original-width=&quot;1600&quot; height=&quot;145&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgtuEDXIltlX3AAf2qUzgthswqmpvUcWHp_SF8UiK8amZSIZXmWdaCTCWRPhaa87Xi90emS6_GxDRELtGgUSzyJ6RMcGB4HPhnnb2gCS8-SYLrCIyTahn5fqXRc9XPWbiFR4T-vn-SsfSfq/s320/hand-523233_1920.jpg&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;Image: &lt;a href=&quot;https://pixabay.com/en/hand-keep-puzzle-finger-fit-523233/&quot; target=&quot;_blank&quot;&gt;Pixabay&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
Breaking your application down into &lt;a href=&quot;https://en.wikipedia.org/wiki/Component-based_software_engineering&quot; target=&quot;_blank&quot;&gt;components &lt;/a&gt;can be a useful approach to a &quot;divide and conquer&quot; methodology.&amp;nbsp; Assigning specific behaviour to a component and then defining interfaces for other components to access it allows you to develop a service driven architecture.&amp;nbsp;&lt;br /&gt;
&lt;br /&gt;
I&#39;m in the process of decomposing a monolithic application into services that will eventually become standalone micro-services.&amp;nbsp; Part of the task ahead lies in determining the service boundaries, which are analogous to software components for my micro-service application.&amp;nbsp;&lt;br /&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
I want components to be &lt;b&gt;modular &lt;/b&gt;to allow them to be developed and deployed as independently as possible.&amp;nbsp; I&#39;m using the approach suggested by Eric Evans in his&amp;nbsp;&lt;a href=&quot;https://www.amazon.co.uk/Domain-Driven-Design-Tackling-Complexity-Software/dp/0321125215&quot; target=&quot;_blank&quot;&gt;book on domain driven design&lt;/a&gt;&amp;nbsp;where he describes the concept of &quot;bounded contexts&quot;.&amp;nbsp; I like to think of a bounded context as being for domain models as a namespace is for classes.&amp;nbsp; These contexts are spaces where a domain model defined in the Ubiquitous Domain Language will have a precise and consistent meaning.&amp;nbsp; Keeping components modular helps to define and maintain these boundaries.&lt;br /&gt;
&lt;br /&gt;
I want my components to be &lt;b&gt;cohesive&lt;/b&gt; because I want my architecture to be so simple that people wonder why we need an architect at all.&amp;nbsp; It should be intuitively obvious why a group of classes belong together in a component and what part of my domain logic they&#39;re implementing.&amp;nbsp; &lt;a href=&quot;https://en.wikipedia.org/wiki/Cohesion_(computer_science)&quot; target=&quot;_blank&quot;&gt;Cohesion &lt;/a&gt;is a good thing and we&#39;re all familiar with writing cohesive &lt;i&gt;classes&lt;/i&gt;, but what principals are important to consider when looking at grouping up classes into &lt;i&gt;cohesive components&lt;/i&gt;?&amp;nbsp;&lt;br /&gt;
&lt;br /&gt;
Robert C Martin discusses three important concepts that govern component cohesion on his website (&lt;a href=&quot;http://butunclebob.com/ArticleS.UncleBob.PrinciplesOfOod&quot; target=&quot;_blank&quot;&gt;here&lt;/a&gt;)&lt;br /&gt;
&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;Release-Reuse equivalency principle (REP) - &lt;i&gt;the granule of release is the granule of reuse&lt;/i&gt;&lt;/li&gt;
&lt;li&gt;Common Closure principle (CCP) - &lt;i&gt;classes that change together are packaged together&lt;/i&gt;&lt;/li&gt;
&lt;li&gt;Common Reuse principle (CRP) - &lt;i&gt;Classes that are used together are packaged together&lt;/i&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;br /&gt;
The &lt;b&gt;Release-Reuse equivalence principle&lt;/b&gt; (REP) is very simple.&amp;nbsp; It states that classes that are packaged together into a component need to be able to be released together.&amp;nbsp; In practice this boils down to properly versioning your releases and having all of the classes in your component versioned and released together.&lt;br /&gt;
&lt;br /&gt;
The &lt;b&gt;Common Closure principle &lt;/b&gt;(CCP) states that you should gather together classes that change for the same reasons and the same times.&amp;nbsp; Conversely you should separate out classes that change for different reasons and at different times.&amp;nbsp;&lt;br /&gt;
&lt;br /&gt;
Remember that the S of SOLID stands for &quot;&lt;a href=&quot;https://en.wikipedia.org/wiki/Single_responsibility_principle&quot; target=&quot;_blank&quot;&gt;single responsibility principle&lt;/a&gt;&quot; (SRP) where a class should have only one reason to change?&amp;nbsp; The CCP is for components what the SRP is for classes.&lt;br /&gt;
&lt;br /&gt;
We can say that generally stuff that changes together should be kept together.&lt;br /&gt;
&lt;br /&gt;
The &lt;b&gt;Common Reuse principle&lt;/b&gt; (CRP) states that you should not force users of a component to depend on things they don&#39;t need.&amp;nbsp;&lt;br /&gt;
&lt;br /&gt;
The CRP more strongly suggests that we &lt;i&gt;do not &lt;/i&gt;include classes into a component that are not tightly bound to the function of the component.&amp;nbsp; Every time we touch one of those classes we will be forced to retest all of the client applications that are using the component.&amp;nbsp; Our deployment process will be heavier than it needs to be, and crucially we&#39;ll be deploying more than we have to.&lt;br /&gt;
&lt;br /&gt;
The CRP is a more general form of the&amp;nbsp;&lt;a href=&quot;https://en.wikipedia.org/wiki/Interface_segregation_principle&quot; target=&quot;_blank&quot;&gt;interface segregation principle&lt;/a&gt;&amp;nbsp;but suggests that a component should be built from classes that are commonly used together.&amp;nbsp;&lt;br /&gt;
&lt;br /&gt;
Generally speaking, we should avoid depending on things that we don&#39;t need.&lt;br /&gt;
&lt;br /&gt;
We&#39;ve seen three principles that govern how we group up classes into components.&amp;nbsp; The REP and CCP are inclusive about grouping up classes and suggest what classes do belong together.&amp;nbsp; The CRP is more strong about excluding classes from a component.&amp;nbsp; There is therefore a balance to be walked between these principals.&amp;nbsp;&lt;br /&gt;
&lt;br /&gt;
&lt;a href=&quot;https://www.industriallogic.com/people/tottinge&quot; target=&quot;_blank&quot;&gt;Tim Ottinger&lt;/a&gt; suggested a diagram that helps to see the cost of abandoning a principle.&amp;nbsp; The label on an edge is the cost of weakening adherence to the principle on the opposite vertex.&amp;nbsp; So, for example the cost of abandoning CCP is that we have too many components changing at one time.&lt;br /&gt;
&lt;br /&gt;
&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto; text-align: center;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhLmxdeBRmVSNQZGdXj2bI2YDOHdLGXy1RBuQ9KPKDfg4gqn1EsHwiUKft_RJ581Bb0ZX7sPe1P6wZ5QAxgPl9CksNaKaViDsQ1MEuo1-bx_8snx1FyP3vNC-Uc4VG1tr9vKuUdcY_OGpuS/s1600/tension_diagram.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhLmxdeBRmVSNQZGdXj2bI2YDOHdLGXy1RBuQ9KPKDfg4gqn1EsHwiUKft_RJ581Bb0ZX7sPe1P6wZ5QAxgPl9CksNaKaViDsQ1MEuo1-bx_8snx1FyP3vNC-Uc4VG1tr9vKuUdcY_OGpuS/s1600/tension_diagram.png&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;Diagram suggested by Tim Ottinger illustrating tension between component cohesion principles&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
Your application will fall somewhere within this triangle as you balance your focus between the principles.&amp;nbsp;&lt;br /&gt;
&lt;br /&gt;
This balance is dynamic and changes over time.&amp;nbsp; Robert C Martin notes that&amp;nbsp;“&lt;i&gt;A good architect finds a position in that tension triangle that meets the _current_ concerns of the development team, but is also aware that those concerns will change over time.&lt;/i&gt;”&lt;br /&gt;
&lt;br /&gt;
These principles will govern how I examine my monolith and identify classes that I can group together to form components.&lt;br /&gt;
&lt;br /&gt;</description><link>http://blog.mystery.co.za/2017/12/component-cohesion.html</link><author>noreply@blogger.com (Andrew Beak)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgtuEDXIltlX3AAf2qUzgthswqmpvUcWHp_SF8UiK8amZSIZXmWdaCTCWRPhaa87Xi90emS6_GxDRELtGgUSzyJ6RMcGB4HPhnnb2gCS8-SYLrCIyTahn5fqXRc9XPWbiFR4T-vn-SsfSfq/s72-c/hand-523233_1920.jpg" height="72" width="72"/><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-1356169494544655761.post-780544070039845504</guid><pubDate>Wed, 27 Dec 2017 11:43:00 +0000</pubDate><atom:updated>2017-12-27T12:05:06.289+00:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Laravel</category><category domain="http://www.blogger.com/atom/ns#">oop</category><category domain="http://www.blogger.com/atom/ns#">PHP</category><category domain="http://www.blogger.com/atom/ns#">programming</category><category domain="http://www.blogger.com/atom/ns#">web development</category><title>Writing SOLID Laravel code</title><description>&lt;table cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;float: right; margin-left: 1em; text-align: right;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEisH9VegSRgPZ8mrxqQR6SIn6Cgefk7L1E1Z-p7Dlzlzwbk2guYs1l5gL-GecAcDPqfXu424Yhkte3K3X6st9QRQJNTZk_TJLwnCN5o2bnfW3mbHbZjgeqkGPnGLz9t0fDt7n7GClPYfYhF/s1600/home-office-599475_1920.jpg&quot; imageanchor=&quot;1&quot; style=&quot;clear: right; margin-bottom: 1em; margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;1067&quot; data-original-width=&quot;1600&quot; height=&quot;213&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEisH9VegSRgPZ8mrxqQR6SIn6Cgefk7L1E1Z-p7Dlzlzwbk2guYs1l5gL-GecAcDPqfXu424Yhkte3K3X6st9QRQJNTZk_TJLwnCN5o2bnfW3mbHbZjgeqkGPnGLz9t0fDt7n7GClPYfYhF/s320/home-office-599475_1920.jpg&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;Image: &lt;a href=&quot;https://pixabay.com/en/home-office-laptop-notebook-startup-599475/&quot; target=&quot;_blank&quot;&gt;Pixabay&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
SOLID is a mnemonic acronym for five object-oriented design principals that are intended to make software designs more understandable (see &lt;a href=&quot;https://en.wikipedia.org/wiki/SOLID_(object-oriented_design)&quot;&gt;Wikipedia&lt;/a&gt;).  They were promoted by a chap called &lt;a href=&quot;https://en.wikipedia.org/wiki/Robert_C._Martin&quot; target=&quot;_blank&quot;&gt;Robert C Martin&lt;/a&gt; who has been programming since before I was born and is an authority on writing clean code.&lt;br /&gt;
&lt;br /&gt;
&amp;nbsp;Laravel is a PHP framework that implements the &lt;a href=&quot;https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller&quot;&gt;model-view-controller&lt;/a&gt; (MVC) pattern.  A lot of people think that their responsibility for OOP design ends with adopting a framework, but actually Laravel is relatively un-opinionated on your OOP design and you still need to think about writing code that is testable and maintainable.&lt;br /&gt;
&lt;br /&gt;
&amp;nbsp;The reason that SOLID principals matter becomes apparent when you work on a single project for a long time.  If you&#39;re writing throwaway applications for clients that you never expect to work on again (presumably because the client won&#39;t hire you again) then the quality of your code doesn&#39;t matter.  But if you&#39;re the guy stuck with managing and implementing change in an application that is actively growing and improving then you&#39;re going to want code that is easy to change, easy to test, and easy to deploy.&lt;br /&gt;
&lt;br /&gt;
The most common problem I&#39;ve seen in Laravel is &quot;fat controllers&quot; that use the ORM to get some data and then push it through to a view.&amp;nbsp; Let&#39;s take a look at an example I&#39;ve made.&amp;nbsp; Imagine that we&#39;re writing a payroll program.&amp;nbsp; We might write something like the following controller methods:&lt;br /&gt;
&lt;br /&gt;
&lt;script src=&quot;https://gist.github.com/andybeak/07dcdf61c9d0ceda01414d3feed489be.js?file=fat_controller.php&quot;&gt;&lt;/script&gt;

This is an unfortunately common Laravel pattern that is taught in countless tutorials.  We call the model from the controller, format the data, and then pass it on to the view.  This is the easiest way to teach the MVC pattern to beginners but unfortunately it violates the SOLID principals.  Let&#39;s see why, and how we can improve this code.
&lt;br /&gt;
&lt;br /&gt;
The &quot;S&quot; in SOLID stands for &lt;b&gt;single responsibility principal&lt;/b&gt;&lt;i&gt;&amp;nbsp;&lt;/i&gt;which requires that each module or class should have a single responsibility for the functionality of the application.&amp;nbsp; A more subtle understanding is put forward by Robert C Martin who says that &quot;&lt;i&gt;A class should have only one reason to change&lt;/i&gt;&quot;.&lt;br /&gt;
&lt;br /&gt;
The thinking behind limiting the reasons for changing a class comes from the observation that software is often developed by teams and that often a team is implementing a feature for a particular actor.&amp;nbsp; In our example the CEO of the company will have different requirements from the CFO, and when either of them requests a change then we want to limit the impact of that change.&amp;nbsp; The actor is the reason for software to change - they request a feature and a team goes ahead and implements it.&lt;br /&gt;
&lt;br /&gt;
In our controller above if the CEO requested a change then that change would definitely affect the CFO.&amp;nbsp; The teams working on the code would need to merge in code from each other.&amp;nbsp; If our code was properly designed then the controller class would be responsible to just one actor.&lt;br /&gt;
&lt;br /&gt;
&lt;script src=&quot;https://gist.github.com/andybeak/07dcdf61c9d0ceda01414d3feed489be.js?file=single_responsibility_example.php&quot;&gt;&lt;/script&gt;

In this example I&#39;ve moved the responsibility for calculating the employee pay to its own object.&amp;nbsp; This object will only change if the CFO requests a change to the way that wages are calculated and so adheres to the single responsibility principal.&amp;nbsp; We would similarly have an object that is responsible for counting the hours.&amp;nbsp; I&#39;ve chosen this way of solving the problem because the Facade pattern is very loaded in Laravel and I think it would just muddy the waters to use it here.&lt;br /&gt;
&lt;br /&gt;
Let&#39;s move on to &quot;O&quot; which is the &lt;b&gt;open-closed principal&lt;/b&gt;&amp;nbsp;which requires that &quot;&lt;i&gt;A software artifact should be open for extension but closed for modification&lt;/i&gt;&quot;.&amp;nbsp; It was developed by &lt;a href=&quot;https://en.wikipedia.org/wiki/Bertrand_Meyer&quot; target=&quot;_blank&quot;&gt;Betrand Meyer&lt;/a&gt; and holds that you should be able to extend on a modules functionality without having to change that module.&lt;br /&gt;
&lt;br /&gt;
The aim of the OCP is to protect important code that implements high level policies from changes made to code that implements low-level details.&amp;nbsp; We want the part of our code that is central to our application to be insulated from changes in other parts of the application.&lt;br /&gt;
&lt;br /&gt;
There is some level of separation in our Laravel application.&amp;nbsp; We can make a change to the View without there being any impact on the Controller, but within the controller above we have no such insulation.&amp;nbsp; If we make a change to the way we read the database then we will be affecting exactly the same function that is responsible for calculating wages!&lt;br /&gt;
&lt;br /&gt;
The open-closed principal seeks to prevent you from changing core functionality as a side-effect of adding new functionality to your application.&amp;nbsp; It works by separating out the application into a hierarchy of dependencies.&amp;nbsp; You can extend functionality from the lower levels of the hierarchy without changing the code in the higher levels.&lt;br /&gt;
&lt;br /&gt;
The &quot;L&quot; in SOLID is named for &lt;a href=&quot;https://en.wikipedia.org/wiki/Barbara_Liskov&quot; target=&quot;_blank&quot;&gt;Barbara Liskov&lt;/a&gt; who put forward what is now known as the &lt;b&gt;Liskov substitution principal&lt;/b&gt;.&amp;nbsp; The principal holds that &quot;&lt;i&gt;if S is a subtype of T, then objects of type T in a program may be replaced with objects of type S without altering any of the desirable properties of that program&lt;/i&gt;&quot;.&lt;br /&gt;
&lt;br /&gt;
&lt;script src=&quot;https://gist.github.com/andybeak/07dcdf61c9d0ceda01414d3feed489be.js?file=liskov_substitution.php&quot;&gt;&lt;/script&gt;

In the example above I&#39;ve amended the object to make it inherit from an interface.&amp;nbsp; Both the &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;PermanentEmployeePayCalculator &lt;/span&gt;class and the &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;TemporaryEmployeePayCalculator&lt;/span&gt;&lt;span style=&quot;font-family: inherit;&quot;&gt;&amp;nbsp;implement this interface and can be substituted for each other.&amp;nbsp; This makes a lot more sense if you consider an LSP violation, such as this one:&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: inherit;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;br /&gt;
&lt;script src=&quot;https://gist.github.com/andybeak/07dcdf61c9d0ceda01414d3feed489be.js?file=liskov_principal_violation.php&quot;&gt;&lt;/script&gt;

This violates the Liskov Substitution Principal because the methods have got different signatures.  You cannot substitute the subtypes of PayCalculator each other because they&#39;re incompatible.&amp;nbsp; The object that depended on them would need to implement some logic to be able to know how many parameters to pass to the method.&amp;nbsp; Adhering to the Liskov substitution principal removes this need and removes special cases from your code.&lt;br /&gt;
&lt;br /&gt;
&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto; text-align: center;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhZ737Tp6bodIeKeIuzdtlkyPCNlbCQqnrMWT44lCW5UsH8ReknXT3kl2Z5zBCVTG3wbQh5SNpSj2sgOSnrDpGpFKhruB8K3ov3FXjkMsHLsSaueBD4myjIvBq-KShNL4QCWnpJDJOeEwJf/s1600/lsp.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;196&quot; data-original-width=&quot;562&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhZ737Tp6bodIeKeIuzdtlkyPCNlbCQqnrMWT44lCW5UsH8ReknXT3kl2Z5zBCVTG3wbQh5SNpSj2sgOSnrDpGpFKhruB8K3ov3FXjkMsHLsSaueBD4myjIvBq-KShNL4QCWnpJDJOeEwJf/s1600/lsp.png&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;Adhering to the Liskov substitution principal&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
The &quot;I&quot; in SOLID was proposed by Robert C Martin and stands for &lt;b&gt;interface segregation&lt;/b&gt;.&amp;nbsp; The idea is that &lt;i&gt;code should not be made to depend on methods that it does not use&lt;/i&gt;.&amp;nbsp; By reducing the dependencies between classes you help to &lt;a href=&quot;https://en.wikipedia.org/wiki/Coupling_(computer_programming)&quot; target=&quot;_blank&quot;&gt;decouple&lt;/a&gt; your code making it easier to make changes in one section without impacting on others.&lt;br /&gt;
&lt;br /&gt;
Let&#39;s imagine that we separated out our controller into classes like the below diagram.&amp;nbsp; We have an Employee data object that is responsible for interacting with the persistence layer and returning results.&amp;nbsp; It has a method that the PayCalculator object uses to determine whether the employee needs to earn their overtime rate and a method that both objects use to fetch the list of hours that an employee has worked (which may or may not violate the single responsibility principal).&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;/div&gt;
&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto; text-align: center;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEivbJyDc_qgRpXy-_MzGBlupWf08OpvSpduvX0VmbNunsDGM2B6ZVEaKg_V3NCVGXC-gTNeuDmJuS3JZuL6UyNlK6HvGN50elhGvJ7phEN-s8KLkZfm0Jzi8mC4LojSAtiLV8pVOMBHU_er/s1600/Untitled+Diagram+%25281%2529.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;243&quot; data-original-width=&quot;321&quot; height=&quot;241&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEivbJyDc_qgRpXy-_MzGBlupWf08OpvSpduvX0VmbNunsDGM2B6ZVEaKg_V3NCVGXC-gTNeuDmJuS3JZuL6UyNlK6HvGN50elhGvJ7phEN-s8KLkZfm0Jzi8mC4LojSAtiLV8pVOMBHU_er/s320/Untitled+Diagram+%25281%2529.png&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;Violation of the ISP&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;br /&gt;
The problem here is that the HoursReporter is forced to depend on the &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;isHourOvertime()&lt;/span&gt;&amp;nbsp;function.&amp;nbsp; This introduces an additional coupling between the classes that we need to avoid if we want to adhere to the interface segregation principal.&lt;br /&gt;
&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto; text-align: center;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhA8WjHy9JyyQ9fqC9a7ExhOFZyEP3tidQ9pIQ5anl7y6tUHZ6lyggvqJM_roF3g4rSOb_hucdZEaEhSBqBEhyyNIhwBuaNcscJBt4zKwKsdxP6ZDl32j8SgiqkQgnLWWjFAUQ5xglmqxbS/s1600/isp.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;442&quot; data-original-width=&quot;521&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhA8WjHy9JyyQ9fqC9a7ExhOFZyEP3tidQ9pIQ5anl7y6tUHZ6lyggvqJM_roF3g4rSOb_hucdZEaEhSBqBEhyyNIhwBuaNcscJBt4zKwKsdxP6ZDl32j8SgiqkQgnLWWjFAUQ5xglmqxbS/s1600/isp.png&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;Adhering to the ISP&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;/div&gt;
&lt;br /&gt;
We can easily solve this problem by declaring an interface for the classes to depend on.&amp;nbsp; The interface for the HoursReporter class excludes the function that we do not want to depend on.&lt;br /&gt;
&lt;br /&gt;
The last letter in SOLID is &quot;D&quot; which stands for the&amp;nbsp;&lt;b&gt;dependency inversion principal&lt;/b&gt;&amp;nbsp;which holds that &lt;i&gt;the most flexible modules are those in which source code dependencies refer only to abstractions rather than concretions&lt;/i&gt;.&lt;br /&gt;
&lt;br /&gt;
To understand dependency inversion consider two things: flow of control and source code dependency.&amp;nbsp; We want to be able to have our source code dependencies to be independent of how control flows through our application.&lt;br /&gt;
&lt;br /&gt;
Some classes and files in our application are more prone to change than others.&amp;nbsp; They are &quot;volatile&quot; classes.&amp;nbsp; We want to minimise the effect of the changes in these classes to the more stable classes.&amp;nbsp; Ideally we want our business logic to be very stable and highly insulated from changes elsewhere in our system.&lt;br /&gt;
&lt;br /&gt;
In the diagram below I&#39;m illustrating a source code dependency hierarchy.&amp;nbsp; High level classes call functions in lower level classes, but in order to do so they need to depend on that class.&amp;nbsp; This means that your source code dependencies are &lt;u&gt;unavoidably &lt;/u&gt;tied to how your flow of control works.&lt;br /&gt;
&lt;br /&gt;
&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto; text-align: center;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEglrd8a2VT_iU95Lq04f9VGmKH9llYHGmaFC7qXejwZYF_mbogAszfgISfPwBwUPfvjMf9GaXB0yWfPlAdNbcoe6iRr60Tcja5ZN-loJuZO_fNXtLio6q2beQCXGlgZ50OPV_VJmpBfRR-K/s1600/calling_hierarchy.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;322&quot; data-original-width=&quot;621&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEglrd8a2VT_iU95Lq04f9VGmKH9llYHGmaFC7qXejwZYF_mbogAszfgISfPwBwUPfvjMf9GaXB0yWfPlAdNbcoe6iRr60Tcja5ZN-loJuZO_fNXtLio6q2beQCXGlgZ50OPV_VJmpBfRR-K/s1600/calling_hierarchy.png&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;Source code dependency hierarchy&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
The problem that arises from this dependency is that it becomes difficult to swap functionality.&amp;nbsp; A change to the source code of a low level object means that we need to rebuild all of the files that depend on it;&amp;nbsp; Admittedly rebuilding files in PHP is less of an issue than for statically typed languages that are built in advance, but we are still &lt;i&gt;directly impacting on files other than the one we are touching&lt;/i&gt;.&lt;br /&gt;
&lt;br /&gt;
Let&#39;s say, for example, we had a class that outputs the Employee wages to screen.&amp;nbsp; In the diagram above we would see the Employee object as the High Level object and perhaps a &quot;ScreenOutput&quot; object as a low-level object.&amp;nbsp; Our Employee object calls the ScreenOutput class directly, and so we have to mention the source code in Employee, like this:&lt;br /&gt;
&lt;br /&gt;
&lt;script src=&quot;https://gist.github.com/andybeak/07dcdf61c9d0ceda01414d3feed489be.js?file=di_violation.php&quot;&gt;&lt;/script&gt;

Now our CFO asks us to be able to print out the wages using the black and white printer in her office.&amp;nbsp; Uh-oh, now we need to rewrite our source-code dependency because the &quot;use&quot; statement refers specifically to a concretion.&lt;br /&gt;
&lt;br /&gt;
What happens if we want to make a change to the way that wages are displayed on the screen?&amp;nbsp; We can easily tweak the ScreenOutput object, but can we deploy it separately?&amp;nbsp; What impact is it going to have on all the places that depend on it?&lt;br /&gt;
&lt;br /&gt;
How could we fix this problem and allow ourselves to swap functionality in and out without affecting our source code dependencies?&amp;nbsp; How do we actually decouple these objects?&lt;br /&gt;
&lt;br /&gt;
The answer is to always depend on abstractions rather than concretions.&amp;nbsp; This insulates you from changes in the underlying files and lets you change and deploy parts of your application separately.&lt;br /&gt;
&lt;br /&gt;
&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto; text-align: center;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjhAzXQK-nRqDZNQ8kWJYFX1SUgJSitcBO_u9WsjxRyLho0w3sMZ1Rn1j2qDsTdAncmgK85kI_zzGqDCgkCeZ6ShM9gRPa-7f-zAmWYf4YV4AYv1I1efEJa16ePGlqBCqZux_ZDXwVWQVMk/s1600/dependency_inversion.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;186&quot; data-original-width=&quot;384&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjhAzXQK-nRqDZNQ8kWJYFX1SUgJSitcBO_u9WsjxRyLho0w3sMZ1Rn1j2qDsTdAncmgK85kI_zzGqDCgkCeZ6ShM9gRPa-7f-zAmWYf4YV4AYv1I1efEJa16ePGlqBCqZux_ZDXwVWQVMk/s1600/dependency_inversion.png&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;Using an interface to implement dependency inversion&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
In the diagram above the Employee object is calling the ScreenOutput class method through an interface.&amp;nbsp; The class has a source code dependency on the interface file (which shouldn&#39;t change often) and any code change in the ScreenOutput class will not affect the Employee object.&lt;br /&gt;
&lt;br /&gt;
The rules to follow for the dependency inversion principal are:&lt;br /&gt;
&lt;br /&gt;
&lt;ol&gt;
&lt;li&gt;Do not reference volatile concrete classes&amp;nbsp;&lt;/li&gt;
&lt;li&gt;Do not derive from volatile concrete classes&lt;/li&gt;
&lt;li&gt;Do not override concrete functions&lt;/li&gt;
&lt;li&gt;Never mention the name of anything concrete and volatile&lt;/li&gt;
&lt;/ol&gt;
&lt;br /&gt;
One way that you can accomplish this is through using a &lt;a href=&quot;https://en.wikipedia.org/wiki/Factory_(object-oriented_programming)&quot; target=&quot;_blank&quot;&gt;Factory &lt;/a&gt;to instantiate volatile concrete classes.&amp;nbsp; This removes the requirement to have a source code dependency on the class that you&#39;re instantiating in the object where you need it.&lt;br /&gt;
&lt;br /&gt;
Laravel approaches dependency inversion by using a &quot;service container&quot;.&amp;nbsp; Your code no longer depends on a concrete implementation of a class, but rather requests an instance of an object from the &lt;a href=&quot;https://laravel.com/docs/5.5/container&quot; target=&quot;_blank&quot;&gt;IoC container&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
In our controller code above the IoC container returns an instance of the HoursWorked model through the Facade pattern.&amp;nbsp; The controller is not directly dependent on the source code file of the HoursWorked model.&amp;nbsp; So in this particular case we&#39;re just lucky to be adhering to a SOLID principal!</description><link>http://blog.mystery.co.za/2017/12/writing-solid-laravel-code.html</link><author>noreply@blogger.com (Andrew Beak)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEisH9VegSRgPZ8mrxqQR6SIn6Cgefk7L1E1Z-p7Dlzlzwbk2guYs1l5gL-GecAcDPqfXu424Yhkte3K3X6st9QRQJNTZk_TJLwnCN5o2bnfW3mbHbZjgeqkGPnGLz9t0fDt7n7GClPYfYhF/s72-c/home-office-599475_1920.jpg" height="72" width="72"/><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-1356169494544655761.post-4492382048162375116</guid><pubDate>Fri, 23 Jun 2017 12:30:00 +0000</pubDate><atom:updated>2017-06-23T13:30:28.539+01:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">ansible</category><category domain="http://www.blogger.com/atom/ns#">devops</category><title>How to get Virtualbox VMs to talk to each other</title><description>&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://upload.wikimedia.org/wikipedia/commons/thumb/2/24/Ansible_logo.svg/2000px-Ansible_logo.svg.png&quot; imageanchor=&quot;1&quot; style=&quot;clear: right; float: right; margin-bottom: 1em; margin-left: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;800&quot; data-original-width=&quot;650&quot; height=&quot;320&quot; src=&quot;https://upload.wikimedia.org/wikipedia/commons/thumb/2/24/Ansible_logo.svg/2000px-Ansible_logo.svg.png&quot; width=&quot;260&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
I&#39;m busy writing an Ansible script and want to test it locally before trying to deploy it anywhere. &amp;nbsp;The easiest way to try and make my local environment as close to my deployment environment was to set up a network of Virtualbox VMs.&lt;br /&gt;
&lt;br /&gt;
The problem was that I&#39;ve always configured my VM&#39;s to use NAT networking. &amp;nbsp;I ssh onto them by setting port forwarding and have never really needed them to have their own address.&lt;br /&gt;
&lt;br /&gt;
The solution to this problem is pretty simple. &amp;nbsp;Just stop the machines and add a new network adapter of type &quot;Host Only&quot;. &amp;nbsp;This adapter will handle communication between the guest and host machines. &lt;br /&gt;
&lt;br /&gt;
The trick is that you need to configure the guest OS network interface too. &lt;br /&gt;
&lt;br /&gt;
To do this SSH onto your VM and run &quot;ip add&quot; to list your network adapters. &amp;nbsp;If you&#39;re like me and started with NAT before adding &quot;Host Only&quot; as your second adapter the output should look something like this:&lt;br /&gt;&lt;br /&gt;
&lt;script src=&quot;https://gist.github.com/andybeak/8bcad5da6e0b970135ab71c929b67229.js?file=ip_add&quot;&gt;&lt;/script&gt;

You need to identify the adapter that is your &quot;Host Only&quot; network. &amp;nbsp;You can do this by running &quot;ip add&quot; on your host machine and looking for the vboxnet0 network address (assuming you&#39;re using the defaults given to you by Virtualbox).&lt;br /&gt;
&lt;br /&gt;
Now you need to edit /etc/network/interfaces and tell Linux (I&#39;m using Ubuntu 16.04) to set up that interface. &amp;nbsp;Add lines like this snippet to your file:&lt;br /&gt;
&lt;br /&gt;
&lt;script src=&quot;https://gist.github.com/andybeak/8bcad5da6e0b970135ab71c929b67229.js?file=network_interfaces&quot;&gt;&lt;/script&gt;
&lt;br /&gt;
&lt;br/&gt;
Now your virtual machines will have an ip address (you can grab it with ifconfig) that you can set up in your Ansible inventory.</description><link>http://blog.mystery.co.za/2017/06/how-to-get-virtualbox-vms-to-talk-to.html</link><author>noreply@blogger.com (Andrew Beak)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-1356169494544655761.post-1000494984829059732</guid><pubDate>Thu, 13 Apr 2017 13:26:00 +0000</pubDate><atom:updated>2017-04-13T14:27:33.500+01:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">api</category><category domain="http://www.blogger.com/atom/ns#">PHP</category><title>Is PHP a good fit for an API server?</title><description>&lt;table cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;float: right; margin-left: 1em; text-align: right;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgpJ8lUKWu9nNc3_cYkAAIEj0CBcf3_rGUAntVeJStsK4bTGCRU2JHfohO_MXfV95ngM5i8rIDTI1TqeZ1kbOKBH19YZu5rxU8om4Yt19E-vy1B9uPWNZhzE_gmD-rUt7-Et4pf55gcqEo2/s1600/honey-bees-326334_1920.jpg&quot; imageanchor=&quot;1&quot; style=&quot;clear: right; margin-bottom: 1em; margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;213&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgpJ8lUKWu9nNc3_cYkAAIEj0CBcf3_rGUAntVeJStsK4bTGCRU2JHfohO_MXfV95ngM5i8rIDTI1TqeZ1kbOKBH19YZu5rxU8om4Yt19E-vy1B9uPWNZhzE_gmD-rUt7-Et4pf55gcqEo2/s320/honey-bees-326334_1920.jpg&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;Image: &lt;a href=&quot;https://pixabay.com/en/honey-bees-beehive-honey-bees-326334/&quot; target=&quot;_blank&quot;&gt;Pixabay&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;span class=&quot;TextRun SCX50176487&quot; lang=&quot;EN-US&quot; style=&quot;background-color: white; font-family: &amp;quot;calibri&amp;quot; , sans-serif; font-size: 11pt; line-height: 19px; margin: 0px; padding: 0px;&quot; xml:lang=&quot;EN-US&quot;&gt;&lt;span class=&quot;NormalTextRun SCX50176487&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; background-color: inherit; margin: 0px; padding: 0px; user-select: text;&quot;&gt;Calling PHP a double-claw hammer is a bit of an in-joke in the PHP community&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;TextRun SCX50176487&quot; lang=&quot;EN-US&quot; style=&quot;background-color: white; font-family: &amp;quot;calibri&amp;quot; , sans-serif; font-size: 11pt; line-height: 19px; margin: 0px; padding: 0px;&quot; xml:lang=&quot;EN-US&quot;&gt;&lt;span class=&quot;NormalTextRun SCX50176487&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; background-color: inherit; margin: 0px; padding: 0px; user-select: text;&quot;&gt;. &amp;nbsp;A lot of people bemoan PHP as a language – it&#39;s fashionable to do so and it seems to be a way to look clever. &amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;TextRun SCX50176487&quot; lang=&quot;EN-US&quot; style=&quot;background-color: white; font-family: &amp;quot;calibri&amp;quot; , sans-serif; font-size: 11pt; line-height: 19px; margin: 0px; padding: 0px;&quot; xml:lang=&quot;EN-US&quot;&gt;&lt;span class=&quot;NormalTextRun SCX50176487&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; background-color: inherit; margin: 0px; padding: 0px; user-select: text;&quot;&gt;The joke came about from a blog post where somebody pointed out all of the problems with PHP (here&#39;s a rebuttal -&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;TextRun SCX50176487&quot; lang=&quot;EN-US&quot; style=&quot;background-color: white; font-family: &amp;quot;calibri&amp;quot; , sans-serif; font-size: 11pt; line-height: 19px; margin: 0px; padding: 0px;&quot; xml:lang=&quot;EN-US&quot;&gt;&lt;span class=&quot;NormalTextRun SCX50176487&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; background-color: inherit; margin: 0px; padding: 0px; user-select: text;&quot;&gt;https://blog.codinghorror.com/the-php-singularity/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;TextRun SCX50176487&quot; lang=&quot;EN-US&quot; style=&quot;background-color: white; font-family: &amp;quot;calibri&amp;quot; , sans-serif; font-size: 11pt; line-height: 19px; margin: 0px; padding: 0px;&quot; xml:lang=&quot;EN-US&quot;&gt;&lt;span class=&quot;NormalTextRun SCX50176487&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; background-color: inherit; margin: 0px; padding: 0px; user-select: text;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;TextRun SCX50176487&quot; lang=&quot;EN-US&quot; style=&quot;background-color: white; font-family: &amp;quot;calibri&amp;quot; , sans-serif; font-size: 11pt; line-height: 19px; margin: 0px; padding: 0px;&quot; xml:lang=&quot;EN-US&quot;&gt;&lt;span class=&quot;NormalTextRun SCX50176487&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; background-color: inherit; margin: 0px; padding: 0px; user-select: text;&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span class=&quot;TextRun SCX50176487&quot; lang=&quot;EN-US&quot; style=&quot;background-color: white; font-family: &amp;quot;calibri&amp;quot; , sans-serif; font-size: 11pt; line-height: 19px; margin: 0px; padding: 0px;&quot; xml:lang=&quot;EN-US&quot;&gt;&lt;span class=&quot;NormalTextRun SCX50176487&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; background-color: inherit; margin: 0px; padding: 0px; user-select: text;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;TextRun SCX50176487&quot; lang=&quot;EN-US&quot; style=&quot;background-color: white; font-family: &amp;quot;calibri&amp;quot; , sans-serif; font-size: 11pt; line-height: 19px; margin: 0px; padding: 0px;&quot; xml:lang=&quot;EN-US&quot;&gt;&lt;span class=&quot;NormalTextRun SCX50176487&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; background-color: inherit; margin: 0px; padding: 0px; user-select: text;&quot;&gt;&lt;span class=&quot;TextRun SCX89657236&quot; lang=&quot;EN-US&quot; style=&quot;-webkit-app-region: no-drag; -webkit-appearance: none; -webkit-background-clip: border-box; -webkit-background-origin: padding-box; -webkit-border-image: none; -webkit-box-align: stretch; -webkit-box-decoration-break: slice; -webkit-box-direction: normal; -webkit-box-flex-group: 1; -webkit-box-flex: 0; -webkit-box-lines: single; -webkit-box-ordinal-group: 1; -webkit-box-orient: horizontal; -webkit-box-pack: start; -webkit-font-smoothing: auto; -webkit-highlight: none; -webkit-hyphenate-character: auto; -webkit-line-break: after-white-space; -webkit-locale: &amp;quot;en-US&amp;quot;; -webkit-margin-collapse: collapse collapse; -webkit-mask-box-image-outset: 0px; -webkit-mask-box-image-repeat: stretch; -webkit-mask-box-image-slice: 0 fill; -webkit-mask-box-image-source: none; -webkit-mask-box-image-width: auto; -webkit-mask-composite: source-over; -webkit-mask: none 0% 0% / auto repeat border-box border-box; -webkit-print-color-adjust: economy; -webkit-rtl-ordering: logical; -webkit-tap-highlight-color: rgba(0, 0, 0, 0); -webkit-text-combine: none; -webkit-text-emphasis-position: over; -webkit-text-emphasis: none rgb(0, 0, 0); -webkit-text-fill-color: rgb(0, 0, 0); -webkit-text-orientation: vertical-right; -webkit-text-security: none; -webkit-text-stroke-color: rgb(0, 0, 0); -webkit-user-drag: none; -webkit-user-modify: read-write; -webkit-writing-mode: horizontal-tb; align-content: stretch; align-items: stretch; align-self: stretch; alignment-baseline: auto; animation: none 0s ease 0s 1 normal none running; backface-visibility: visible; background-blend-mode: normal; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0); baseline-shift: 0px; border-collapse: separate; border-radius: 0px; border-spacing: 0px; border: 0px none rgb(0, 0, 0); bottom: auto; box-shadow: none; box-sizing: content-box; break-after: auto; break-before: auto; break-inside: auto; buffered-rendering: auto; caption-side: top; clear: none; clip-path: none; clip-rule: nonzero; clip: auto; color-interpolation-filters: linearRGB; color-interpolation: sRGB; color-rendering: auto; column-gap: normal; column-rule: 0px none rgb(0, 0, 0); column-span: none; columns: auto auto; cursor: text; cx: 0px; cy: 0px; d: none; direction: ltr; display: inline; dominant-baseline: auto; empty-cells: show; fill-opacity: 1; fill-rule: nonzero; fill: rgb(0, 0, 0); filter: none; flex-flow: row nowrap; flex: 0 1 auto; float: none; flood-color: rgb(0, 0, 0); flood-opacity: 1; font-kerning: auto; font-size: 14.6667px; font-stretch: normal; font-variant-ligatures: no-common-ligatures no-discretionary-ligatures no-historical-ligatures no-contextual; font-variant-numeric: normal; height: auto; hyphens: manual; image-rendering: auto; isolation: auto; justify-content: flex-start; left: auto; lighting-color: rgb(255, 255, 255); line-height: 19px; list-style: disc outside none; margin: 0px; marker: none; mask-type: luminance; mask: none; max-height: none; max-width: none; min-height: 0px; min-width: 0px; mix-blend-mode: normal; motion: none 0px auto 0deg; object-fit: fill; object-position: 50% 50%; offset-rotate: auto 0deg; opacity: 1; order: 0; outline-offset: 0px; outline: rgb(0, 0, 0) none 0px; overflow-anchor: auto; overflow-wrap: break-word; overflow: visible; padding: 0px; paint-order: fill; perspective-origin: 0px 0px; perspective: none; pointer-events: auto; position: static; r: 0px; resize: none; right: auto; rx: auto; ry: auto; shape-image-threshold: 0; shape-margin: 0px; shape-outside: none; shape-rendering: auto; speak: normal; stop-color: rgb(0, 0, 0); stop-opacity: 1; stroke-dasharray: none; stroke-dashoffset: 0px; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 4; stroke-opacity: 1; stroke-width: 1px; stroke: none; tab-size: 8; table-layout: auto; text-align-last: auto; text-anchor: start; text-overflow: clip; text-rendering: auto; text-shadow: none; text-size-adjust: auto; top: auto; touch-action: auto; transform-origin: 0px 0px 0px; transform-style: flat; transform: none; transition: all 0s ease 0s; unicode-bidi: normal; user-select: text; vector-effect: none; vertical-align: baseline; visibility: visible; width: auto; will-change: auto; word-break: normal; word-wrap: break-word; writing-mode: horizontal-tb; x: 0px; y: 0px; z-index: auto; zoom: 1;&quot; xml:lang=&quot;EN-US&quot;&gt;&lt;span class=&quot;NormalTextRun SCX89657236&quot; style=&quot;background-color: inherit;&quot;&gt;Anyway, PHP is a warty language that sucks in academic circles but it doesn&#39;t matter because it&#39;s really good at web stuff, there are lots of people who know it (so it&#39;s cheap to hire), there are lots of libraries and frameworks (so it&#39;s cheap and fast to develop in). &amp;nbsp;The commercial world is willing to overlook the academic warts.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span class=&quot;TextRun SCX50176487&quot; lang=&quot;EN-US&quot; style=&quot;background-color: white; font-family: &amp;quot;calibri&amp;quot; , sans-serif; font-size: 11pt; line-height: 19px; margin: 0px; padding: 0px;&quot; xml:lang=&quot;EN-US&quot;&gt;&lt;span class=&quot;NormalTextRun SCX50176487&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; background-color: inherit; margin: 0px; padding: 0px; user-select: text;&quot;&gt;&lt;span class=&quot;TextRun SCX89657236&quot; lang=&quot;EN-US&quot; style=&quot;-webkit-app-region: no-drag; -webkit-appearance: none; -webkit-background-clip: border-box; -webkit-background-origin: padding-box; -webkit-border-image: none; -webkit-box-align: stretch; -webkit-box-decoration-break: slice; -webkit-box-direction: normal; -webkit-box-flex-group: 1; -webkit-box-flex: 0; -webkit-box-lines: single; -webkit-box-ordinal-group: 1; -webkit-box-orient: horizontal; -webkit-box-pack: start; -webkit-font-smoothing: auto; -webkit-highlight: none; -webkit-hyphenate-character: auto; -webkit-line-break: after-white-space; -webkit-locale: &amp;quot;en-US&amp;quot;; -webkit-margin-collapse: collapse collapse; -webkit-mask-box-image-outset: 0px; -webkit-mask-box-image-repeat: stretch; -webkit-mask-box-image-slice: 0 fill; -webkit-mask-box-image-source: none; -webkit-mask-box-image-width: auto; -webkit-mask-composite: source-over; -webkit-mask: none 0% 0% / auto repeat border-box border-box; -webkit-print-color-adjust: economy; -webkit-rtl-ordering: logical; -webkit-tap-highlight-color: rgba(0, 0, 0, 0); -webkit-text-combine: none; -webkit-text-emphasis-position: over; -webkit-text-emphasis: none rgb(0, 0, 0); -webkit-text-fill-color: rgb(0, 0, 0); -webkit-text-orientation: vertical-right; -webkit-text-security: none; -webkit-text-stroke-color: rgb(0, 0, 0); -webkit-user-drag: none; -webkit-user-modify: read-write; -webkit-writing-mode: horizontal-tb; align-content: stretch; align-items: stretch; align-self: stretch; alignment-baseline: auto; animation: none 0s ease 0s 1 normal none running; backface-visibility: visible; background-blend-mode: normal; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0); baseline-shift: 0px; border-collapse: separate; border-radius: 0px; border-spacing: 0px; border: 0px none rgb(0, 0, 0); bottom: auto; box-shadow: none; box-sizing: content-box; break-after: auto; break-before: auto; break-inside: auto; buffered-rendering: auto; caption-side: top; clear: none; clip-path: none; clip-rule: nonzero; clip: auto; color-interpolation-filters: linearRGB; color-interpolation: sRGB; color-rendering: auto; column-gap: normal; column-rule: 0px none rgb(0, 0, 0); column-span: none; columns: auto auto; cursor: text; cx: 0px; cy: 0px; d: none; direction: ltr; display: inline; dominant-baseline: auto; empty-cells: show; fill-opacity: 1; fill-rule: nonzero; fill: rgb(0, 0, 0); filter: none; flex-flow: row nowrap; flex: 0 1 auto; float: none; flood-color: rgb(0, 0, 0); flood-opacity: 1; font-kerning: auto; font-size: 14.6667px; font-stretch: normal; font-variant-ligatures: no-common-ligatures no-discretionary-ligatures no-historical-ligatures no-contextual; font-variant-numeric: normal; height: auto; hyphens: manual; image-rendering: auto; isolation: auto; justify-content: flex-start; left: auto; lighting-color: rgb(255, 255, 255); line-height: 19px; list-style: disc outside none; margin: 0px; marker: none; mask-type: luminance; mask: none; max-height: none; max-width: none; min-height: 0px; min-width: 0px; mix-blend-mode: normal; motion: none 0px auto 0deg; object-fit: fill; object-position: 50% 50%; offset-rotate: auto 0deg; opacity: 1; order: 0; outline-offset: 0px; outline: rgb(0, 0, 0) none 0px; overflow-anchor: auto; overflow-wrap: break-word; overflow: visible; padding: 0px; paint-order: fill; perspective-origin: 0px 0px; perspective: none; pointer-events: auto; position: static; r: 0px; resize: none; right: auto; rx: auto; ry: auto; shape-image-threshold: 0; shape-margin: 0px; shape-outside: none; shape-rendering: auto; speak: normal; stop-color: rgb(0, 0, 0); stop-opacity: 1; stroke-dasharray: none; stroke-dashoffset: 0px; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 4; stroke-opacity: 1; stroke-width: 1px; stroke: none; tab-size: 8; table-layout: auto; text-align-last: auto; text-anchor: start; text-overflow: clip; text-rendering: auto; text-shadow: none; text-size-adjust: auto; top: auto; touch-action: auto; transform-origin: 0px 0px 0px; transform-style: flat; transform: none; transition: all 0s ease 0s; unicode-bidi: normal; user-select: text; vector-effect: none; vertical-align: baseline; visibility: visible; width: auto; will-change: auto; word-break: normal; word-wrap: break-word; writing-mode: horizontal-tb; x: 0px; y: 0px; z-index: auto; zoom: 1;&quot; xml:lang=&quot;EN-US&quot;&gt;&lt;span class=&quot;NormalTextRun SCX89657236&quot; style=&quot;background-color: inherit;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;TextRun SCX50176487&quot; lang=&quot;EN-US&quot; style=&quot;background-color: white; font-family: &amp;quot;calibri&amp;quot; , sans-serif; font-size: 11pt; line-height: 19px; margin: 0px; padding: 0px;&quot; xml:lang=&quot;EN-US&quot;&gt;&lt;span class=&quot;NormalTextRun SCX50176487&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; background-color: inherit; margin: 0px; padding: 0px; user-select: text;&quot;&gt;&lt;span class=&quot;TextRun SCX89657236&quot; lang=&quot;EN-US&quot; style=&quot;-webkit-app-region: no-drag; -webkit-appearance: none; -webkit-background-clip: border-box; -webkit-background-origin: padding-box; -webkit-border-image: none; -webkit-box-align: stretch; -webkit-box-decoration-break: slice; -webkit-box-direction: normal; -webkit-box-flex-group: 1; -webkit-box-flex: 0; -webkit-box-lines: single; -webkit-box-ordinal-group: 1; -webkit-box-orient: horizontal; -webkit-box-pack: start; -webkit-font-smoothing: auto; -webkit-highlight: none; -webkit-hyphenate-character: auto; -webkit-line-break: after-white-space; -webkit-locale: &amp;quot;en-US&amp;quot;; -webkit-margin-collapse: collapse collapse; -webkit-mask-box-image-outset: 0px; -webkit-mask-box-image-repeat: stretch; -webkit-mask-box-image-slice: 0 fill; -webkit-mask-box-image-source: none; -webkit-mask-box-image-width: auto; -webkit-mask-composite: source-over; -webkit-mask: none 0% 0% / auto repeat border-box border-box; -webkit-print-color-adjust: economy; -webkit-rtl-ordering: logical; -webkit-tap-highlight-color: rgba(0, 0, 0, 0); -webkit-text-combine: none; -webkit-text-emphasis-position: over; -webkit-text-emphasis: none rgb(0, 0, 0); -webkit-text-fill-color: rgb(0, 0, 0); -webkit-text-orientation: vertical-right; -webkit-text-security: none; -webkit-text-stroke-color: rgb(0, 0, 0); -webkit-user-drag: none; -webkit-user-modify: read-write; -webkit-writing-mode: horizontal-tb; align-content: stretch; align-items: stretch; align-self: stretch; alignment-baseline: auto; animation: none 0s ease 0s 1 normal none running; backface-visibility: visible; background-blend-mode: normal; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0); baseline-shift: 0px; border-collapse: separate; border-radius: 0px; border-spacing: 0px; border: 0px none rgb(0, 0, 0); bottom: auto; box-shadow: none; box-sizing: content-box; break-after: auto; break-before: auto; break-inside: auto; buffered-rendering: auto; caption-side: top; clear: none; clip-path: none; clip-rule: nonzero; clip: auto; color-interpolation-filters: linearRGB; color-interpolation: sRGB; color-rendering: auto; column-gap: normal; column-rule: 0px none rgb(0, 0, 0); column-span: none; columns: auto auto; cursor: text; cx: 0px; cy: 0px; d: none; direction: ltr; display: inline; dominant-baseline: auto; empty-cells: show; fill-opacity: 1; fill-rule: nonzero; fill: rgb(0, 0, 0); filter: none; flex-flow: row nowrap; flex: 0 1 auto; float: none; flood-color: rgb(0, 0, 0); flood-opacity: 1; font-kerning: auto; font-size: 14.6667px; font-stretch: normal; font-variant-ligatures: no-common-ligatures no-discretionary-ligatures no-historical-ligatures no-contextual; font-variant-numeric: normal; height: auto; hyphens: manual; image-rendering: auto; isolation: auto; justify-content: flex-start; left: auto; lighting-color: rgb(255, 255, 255); line-height: 19px; list-style: disc outside none; margin: 0px; marker: none; mask-type: luminance; mask: none; max-height: none; max-width: none; min-height: 0px; min-width: 0px; mix-blend-mode: normal; motion: none 0px auto 0deg; object-fit: fill; object-position: 50% 50%; offset-rotate: auto 0deg; opacity: 1; order: 0; outline-offset: 0px; outline: rgb(0, 0, 0) none 0px; overflow-anchor: auto; overflow-wrap: break-word; overflow: visible; padding: 0px; paint-order: fill; perspective-origin: 0px 0px; perspective: none; pointer-events: auto; position: static; r: 0px; resize: none; right: auto; rx: auto; ry: auto; shape-image-threshold: 0; shape-margin: 0px; shape-outside: none; shape-rendering: auto; speak: normal; stop-color: rgb(0, 0, 0); stop-opacity: 1; stroke-dasharray: none; stroke-dashoffset: 0px; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 4; stroke-opacity: 1; stroke-width: 1px; stroke: none; tab-size: 8; table-layout: auto; text-align-last: auto; text-anchor: start; text-overflow: clip; text-rendering: auto; text-shadow: none; text-size-adjust: auto; top: auto; touch-action: auto; transform-origin: 0px 0px 0px; transform-style: flat; transform: none; transition: all 0s ease 0s; unicode-bidi: normal; user-select: text; vector-effect: none; vertical-align: baseline; visibility: visible; width: auto; will-change: auto; word-break: normal; word-wrap: break-word; writing-mode: horizontal-tb; x: 0px; y: 0px; z-index: auto; zoom: 1;&quot; xml:lang=&quot;EN-US&quot;&gt;&lt;span class=&quot;NormalTextRun SCX89657236&quot; style=&quot;background-color: inherit;&quot;&gt;I&#39;m busy helping to improve the performance of an API server. &amp;nbsp;As part of my effort I&#39;m profiling calls to the endpoints. &amp;nbsp;I&#39;m using Blackfire to generate call graphs and also logging the sql queries that the ORM is producing so that I can check the indexes and joins.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span class=&quot;TextRun SCX50176487&quot; lang=&quot;EN-US&quot; style=&quot;background-color: white; font-family: &amp;quot;calibri&amp;quot; , sans-serif; font-size: 11pt; line-height: 19px; margin: 0px; padding: 0px;&quot; xml:lang=&quot;EN-US&quot;&gt;&lt;span class=&quot;NormalTextRun SCX50176487&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; background-color: inherit; margin: 0px; padding: 0px; user-select: text;&quot;&gt;&lt;span class=&quot;TextRun SCX89657236&quot; lang=&quot;EN-US&quot; style=&quot;-webkit-app-region: no-drag; -webkit-appearance: none; -webkit-background-clip: border-box; -webkit-background-origin: padding-box; -webkit-border-image: none; -webkit-box-align: stretch; -webkit-box-decoration-break: slice; -webkit-box-direction: normal; -webkit-box-flex-group: 1; -webkit-box-flex: 0; -webkit-box-lines: single; -webkit-box-ordinal-group: 1; -webkit-box-orient: horizontal; -webkit-box-pack: start; -webkit-font-smoothing: auto; -webkit-highlight: none; -webkit-hyphenate-character: auto; -webkit-line-break: after-white-space; -webkit-locale: &amp;quot;en-US&amp;quot;; -webkit-margin-collapse: collapse collapse; -webkit-mask-box-image-outset: 0px; -webkit-mask-box-image-repeat: stretch; -webkit-mask-box-image-slice: 0 fill; -webkit-mask-box-image-source: none; -webkit-mask-box-image-width: auto; -webkit-mask-composite: source-over; -webkit-mask: none 0% 0% / auto repeat border-box border-box; -webkit-print-color-adjust: economy; -webkit-rtl-ordering: logical; -webkit-tap-highlight-color: rgba(0, 0, 0, 0); -webkit-text-combine: none; -webkit-text-emphasis-position: over; -webkit-text-emphasis: none rgb(0, 0, 0); -webkit-text-fill-color: rgb(0, 0, 0); -webkit-text-orientation: vertical-right; -webkit-text-security: none; -webkit-text-stroke-color: rgb(0, 0, 0); -webkit-user-drag: none; -webkit-user-modify: read-write; -webkit-writing-mode: horizontal-tb; align-content: stretch; align-items: stretch; align-self: stretch; alignment-baseline: auto; animation: none 0s ease 0s 1 normal none running; backface-visibility: visible; background-blend-mode: normal; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0); baseline-shift: 0px; border-collapse: separate; border-radius: 0px; border-spacing: 0px; border: 0px none rgb(0, 0, 0); bottom: auto; box-shadow: none; box-sizing: content-box; break-after: auto; break-before: auto; break-inside: auto; buffered-rendering: auto; caption-side: top; clear: none; clip-path: none; clip-rule: nonzero; clip: auto; color-interpolation-filters: linearRGB; color-interpolation: sRGB; color-rendering: auto; column-gap: normal; column-rule: 0px none rgb(0, 0, 0); column-span: none; columns: auto auto; cursor: text; cx: 0px; cy: 0px; d: none; direction: ltr; display: inline; dominant-baseline: auto; empty-cells: show; fill-opacity: 1; fill-rule: nonzero; fill: rgb(0, 0, 0); filter: none; flex-flow: row nowrap; flex: 0 1 auto; float: none; flood-color: rgb(0, 0, 0); flood-opacity: 1; font-kerning: auto; font-size: 14.6667px; font-stretch: normal; font-variant-ligatures: no-common-ligatures no-discretionary-ligatures no-historical-ligatures no-contextual; font-variant-numeric: normal; height: auto; hyphens: manual; image-rendering: auto; isolation: auto; justify-content: flex-start; left: auto; lighting-color: rgb(255, 255, 255); line-height: 19px; list-style: disc outside none; margin: 0px; marker: none; mask-type: luminance; mask: none; max-height: none; max-width: none; min-height: 0px; min-width: 0px; mix-blend-mode: normal; motion: none 0px auto 0deg; object-fit: fill; object-position: 50% 50%; offset-rotate: auto 0deg; opacity: 1; order: 0; outline-offset: 0px; outline: rgb(0, 0, 0) none 0px; overflow-anchor: auto; overflow-wrap: break-word; overflow: visible; padding: 0px; paint-order: fill; perspective-origin: 0px 0px; perspective: none; pointer-events: auto; position: static; r: 0px; resize: none; right: auto; rx: auto; ry: auto; shape-image-threshold: 0; shape-margin: 0px; shape-outside: none; shape-rendering: auto; speak: normal; stop-color: rgb(0, 0, 0); stop-opacity: 1; stroke-dasharray: none; stroke-dashoffset: 0px; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 4; stroke-opacity: 1; stroke-width: 1px; stroke: none; tab-size: 8; table-layout: auto; text-align-last: auto; text-anchor: start; text-overflow: clip; text-rendering: auto; text-shadow: none; text-size-adjust: auto; top: auto; touch-action: auto; transform-origin: 0px 0px 0px; transform-style: flat; transform: none; transition: all 0s ease 0s; unicode-bidi: normal; user-select: text; vector-effect: none; vertical-align: baseline; visibility: visible; width: auto; will-change: auto; word-break: normal; word-wrap: break-word; writing-mode: horizontal-tb; x: 0px; y: 0px; z-index: auto; zoom: 1;&quot; xml:lang=&quot;EN-US&quot;&gt;&lt;span class=&quot;NormalTextRun SCX89657236&quot; style=&quot;background-color: inherit;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;TextRun SCX50176487&quot; lang=&quot;EN-US&quot; style=&quot;background-color: white; font-family: &amp;quot;calibri&amp;quot; , sans-serif; font-size: 11pt; line-height: 19px; margin: 0px; padding: 0px;&quot; xml:lang=&quot;EN-US&quot;&gt;&lt;span class=&quot;NormalTextRun SCX50176487&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; background-color: inherit; margin: 0px; padding: 0px; user-select: text;&quot;&gt;&lt;span class=&quot;TextRun SCX89657236&quot; lang=&quot;EN-US&quot; style=&quot;-webkit-app-region: no-drag; -webkit-appearance: none; -webkit-background-clip: border-box; -webkit-background-origin: padding-box; -webkit-border-image: none; -webkit-box-align: stretch; -webkit-box-decoration-break: slice; -webkit-box-direction: normal; -webkit-box-flex-group: 1; -webkit-box-flex: 0; -webkit-box-lines: single; -webkit-box-ordinal-group: 1; -webkit-box-orient: horizontal; -webkit-box-pack: start; -webkit-font-smoothing: auto; -webkit-highlight: none; -webkit-hyphenate-character: auto; -webkit-line-break: after-white-space; -webkit-locale: &amp;quot;en-US&amp;quot;; -webkit-margin-collapse: collapse collapse; -webkit-mask-box-image-outset: 0px; -webkit-mask-box-image-repeat: stretch; -webkit-mask-box-image-slice: 0 fill; -webkit-mask-box-image-source: none; -webkit-mask-box-image-width: auto; -webkit-mask-composite: source-over; -webkit-mask: none 0% 0% / auto repeat border-box border-box; -webkit-print-color-adjust: economy; -webkit-rtl-ordering: logical; -webkit-tap-highlight-color: rgba(0, 0, 0, 0); -webkit-text-combine: none; -webkit-text-emphasis-position: over; -webkit-text-emphasis: none rgb(0, 0, 0); -webkit-text-fill-color: rgb(0, 0, 0); -webkit-text-orientation: vertical-right; -webkit-text-security: none; -webkit-text-stroke-color: rgb(0, 0, 0); -webkit-user-drag: none; -webkit-user-modify: read-write; -webkit-writing-mode: horizontal-tb; align-content: stretch; align-items: stretch; align-self: stretch; alignment-baseline: auto; animation: none 0s ease 0s 1 normal none running; backface-visibility: visible; background-blend-mode: normal; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0); baseline-shift: 0px; border-collapse: separate; border-radius: 0px; border-spacing: 0px; border: 0px none rgb(0, 0, 0); bottom: auto; box-shadow: none; box-sizing: content-box; break-after: auto; break-before: auto; break-inside: auto; buffered-rendering: auto; caption-side: top; clear: none; clip-path: none; clip-rule: nonzero; clip: auto; color-interpolation-filters: linearRGB; color-interpolation: sRGB; color-rendering: auto; column-gap: normal; column-rule: 0px none rgb(0, 0, 0); column-span: none; columns: auto auto; cursor: text; cx: 0px; cy: 0px; d: none; direction: ltr; display: inline; dominant-baseline: auto; empty-cells: show; fill-opacity: 1; fill-rule: nonzero; fill: rgb(0, 0, 0); filter: none; flex-flow: row nowrap; flex: 0 1 auto; float: none; flood-color: rgb(0, 0, 0); flood-opacity: 1; font-kerning: auto; font-size: 14.6667px; font-stretch: normal; font-variant-ligatures: no-common-ligatures no-discretionary-ligatures no-historical-ligatures no-contextual; font-variant-numeric: normal; height: auto; hyphens: manual; image-rendering: auto; isolation: auto; justify-content: flex-start; left: auto; lighting-color: rgb(255, 255, 255); line-height: 19px; list-style: disc outside none; margin: 0px; marker: none; mask-type: luminance; mask: none; max-height: none; max-width: none; min-height: 0px; min-width: 0px; mix-blend-mode: normal; motion: none 0px auto 0deg; object-fit: fill; object-position: 50% 50%; offset-rotate: auto 0deg; opacity: 1; order: 0; outline-offset: 0px; outline: rgb(0, 0, 0) none 0px; overflow-anchor: auto; overflow-wrap: break-word; overflow: visible; padding: 0px; paint-order: fill; perspective-origin: 0px 0px; perspective: none; pointer-events: auto; position: static; r: 0px; resize: none; right: auto; rx: auto; ry: auto; shape-image-threshold: 0; shape-margin: 0px; shape-outside: none; shape-rendering: auto; speak: normal; stop-color: rgb(0, 0, 0); stop-opacity: 1; stroke-dasharray: none; stroke-dashoffset: 0px; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 4; stroke-opacity: 1; stroke-width: 1px; stroke: none; tab-size: 8; table-layout: auto; text-align-last: auto; text-anchor: start; text-overflow: clip; text-rendering: auto; text-shadow: none; text-size-adjust: auto; top: auto; touch-action: auto; transform-origin: 0px 0px 0px; transform-style: flat; transform: none; transition: all 0s ease 0s; unicode-bidi: normal; user-select: text; vector-effect: none; vertical-align: baseline; visibility: visible; width: auto; will-change: auto; word-break: normal; word-wrap: break-word; writing-mode: horizontal-tb; x: 0px; y: 0px; z-index: auto; zoom: 1;&quot; xml:lang=&quot;EN-US&quot;&gt;&lt;span class=&quot;NormalTextRun SCX89657236&quot; style=&quot;background-color: inherit;&quot;&gt;Here&#39;s a callgraph for a call to the endpoint where we are looking to run a paginated SQL query. &amp;nbsp;We&#39;re not applying any business logic or having any side-effects - all we&#39;re trying to do is query the database and return a JSON string to the frontend.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span class=&quot;TextRun SCX50176487&quot; lang=&quot;EN-US&quot; style=&quot;background-color: white; font-family: &amp;quot;calibri&amp;quot; , sans-serif; font-size: 11pt; line-height: 19px; margin: 0px; padding: 0px;&quot; xml:lang=&quot;EN-US&quot;&gt;&lt;span class=&quot;NormalTextRun SCX50176487&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; background-color: inherit; margin: 0px; padding: 0px; user-select: text;&quot;&gt;&lt;span class=&quot;TextRun SCX89657236&quot; lang=&quot;EN-US&quot; style=&quot;-webkit-app-region: no-drag; -webkit-appearance: none; -webkit-background-clip: border-box; -webkit-background-origin: padding-box; -webkit-border-image: none; -webkit-box-align: stretch; -webkit-box-decoration-break: slice; -webkit-box-direction: normal; -webkit-box-flex-group: 1; -webkit-box-flex: 0; -webkit-box-lines: single; -webkit-box-ordinal-group: 1; -webkit-box-orient: horizontal; -webkit-box-pack: start; -webkit-font-smoothing: auto; -webkit-highlight: none; -webkit-hyphenate-character: auto; -webkit-line-break: after-white-space; -webkit-locale: &amp;quot;en-US&amp;quot;; -webkit-margin-collapse: collapse collapse; -webkit-mask-box-image-outset: 0px; -webkit-mask-box-image-repeat: stretch; -webkit-mask-box-image-slice: 0 fill; -webkit-mask-box-image-source: none; -webkit-mask-box-image-width: auto; -webkit-mask-composite: source-over; -webkit-mask: none 0% 0% / auto repeat border-box border-box; -webkit-print-color-adjust: economy; -webkit-rtl-ordering: logical; -webkit-tap-highlight-color: rgba(0, 0, 0, 0); -webkit-text-combine: none; -webkit-text-emphasis-position: over; -webkit-text-emphasis: none rgb(0, 0, 0); -webkit-text-fill-color: rgb(0, 0, 0); -webkit-text-orientation: vertical-right; -webkit-text-security: none; -webkit-text-stroke-color: rgb(0, 0, 0); -webkit-user-drag: none; -webkit-user-modify: read-write; -webkit-writing-mode: horizontal-tb; align-content: stretch; align-items: stretch; align-self: stretch; alignment-baseline: auto; animation: none 0s ease 0s 1 normal none running; backface-visibility: visible; background-blend-mode: normal; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0); baseline-shift: 0px; border-collapse: separate; border-radius: 0px; border-spacing: 0px; border: 0px none rgb(0, 0, 0); bottom: auto; box-shadow: none; box-sizing: content-box; break-after: auto; break-before: auto; break-inside: auto; buffered-rendering: auto; caption-side: top; clear: none; clip-path: none; clip-rule: nonzero; clip: auto; color-interpolation-filters: linearRGB; color-interpolation: sRGB; color-rendering: auto; column-gap: normal; column-rule: 0px none rgb(0, 0, 0); column-span: none; columns: auto auto; cursor: text; cx: 0px; cy: 0px; d: none; direction: ltr; display: inline; dominant-baseline: auto; empty-cells: show; fill-opacity: 1; fill-rule: nonzero; fill: rgb(0, 0, 0); filter: none; flex-flow: row nowrap; flex: 0 1 auto; float: none; flood-color: rgb(0, 0, 0); flood-opacity: 1; font-kerning: auto; font-size: 14.6667px; font-stretch: normal; font-variant-ligatures: no-common-ligatures no-discretionary-ligatures no-historical-ligatures no-contextual; font-variant-numeric: normal; height: auto; hyphens: manual; image-rendering: auto; isolation: auto; justify-content: flex-start; left: auto; lighting-color: rgb(255, 255, 255); line-height: 19px; list-style: disc outside none; margin: 0px; marker: none; mask-type: luminance; mask: none; max-height: none; max-width: none; min-height: 0px; min-width: 0px; mix-blend-mode: normal; motion: none 0px auto 0deg; object-fit: fill; object-position: 50% 50%; offset-rotate: auto 0deg; opacity: 1; order: 0; outline-offset: 0px; outline: rgb(0, 0, 0) none 0px; overflow-anchor: auto; overflow-wrap: break-word; overflow: visible; padding: 0px; paint-order: fill; perspective-origin: 0px 0px; perspective: none; pointer-events: auto; position: static; r: 0px; resize: none; right: auto; rx: auto; ry: auto; shape-image-threshold: 0; shape-margin: 0px; shape-outside: none; shape-rendering: auto; speak: normal; stop-color: rgb(0, 0, 0); stop-opacity: 1; stroke-dasharray: none; stroke-dashoffset: 0px; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 4; stroke-opacity: 1; stroke-width: 1px; stroke: none; tab-size: 8; table-layout: auto; text-align-last: auto; text-anchor: start; text-overflow: clip; text-rendering: auto; text-shadow: none; text-size-adjust: auto; top: auto; touch-action: auto; transform-origin: 0px 0px 0px; transform-style: flat; transform: none; transition: all 0s ease 0s; unicode-bidi: normal; user-select: text; vector-effect: none; vertical-align: baseline; visibility: visible; width: auto; will-change: auto; word-break: normal; word-wrap: break-word; writing-mode: horizontal-tb; x: 0px; y: 0px; z-index: auto; zoom: 1;&quot; xml:lang=&quot;EN-US&quot;&gt;&lt;span class=&quot;NormalTextRun SCX89657236&quot; style=&quot;background-color: inherit;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;br /&gt;
&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto; text-align: center;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjMzxSQ9s8WqJHQASKOxhR8MBd53BXThcRSBVO6gXz1qFzMaV3YitraTRGUHllvcVXWTzXWmAaHNes0sEfbbOT3j8VmKoCKPaJGNnrgEhGwA58-FqB0OMRG54ZK9a27_FdNg5ZjTU_TG-lr/s1600/call-graph.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img alt=&quot;&quot; border=&quot;0&quot; height=&quot;170&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjMzxSQ9s8WqJHQASKOxhR8MBd53BXThcRSBVO6gXz1qFzMaV3YitraTRGUHllvcVXWTzXWmAaHNes0sEfbbOT3j8VmKoCKPaJGNnrgEhGwA58-FqB0OMRG54ZK9a27_FdNg5ZjTU_TG-lr/s320/call-graph.png&quot; title=&quot;&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;Blackfire call graph&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
That&#39;s a pretty substantial call graph for what sounds like a simple task right? &amp;nbsp;All I want to do is route the request to a controller, query the database, and send the results back.&lt;br /&gt;
&lt;br /&gt;
Blackfire tells me that 172 different MySQL queries are being run. &amp;nbsp;The PHP code responsible is using the ORM to build up the joins and so on. &amp;nbsp;I suspect that the problem is that there is pagination being applied and the ORM is not able to optimize the queries it needs to do in order to paginate efficiently.&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;
Okay, so what questions do I have?&lt;/h3&gt;
&lt;div&gt;
&lt;span class=&quot;TextRun SCX9252380&quot; lang=&quot;EN-US&quot; style=&quot;background-color: white; font-family: &amp;quot;calibri&amp;quot; , sans-serif; font-size: 11pt; line-height: 19px; margin: 0px; padding: 0px;&quot; xml:lang=&quot;EN-US&quot;&gt;&lt;span class=&quot;NormalTextRun SCX9252380&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; background-color: inherit; margin: 0px; padding: 0px; user-select: text;&quot;&gt;W&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;TextRun SCX9252380&quot; lang=&quot;EN-US&quot; style=&quot;background-color: white; font-family: &amp;quot;calibri&amp;quot; , sans-serif; font-size: 11pt; line-height: 19px; margin: 0px; padding: 0px;&quot; xml:lang=&quot;EN-US&quot;&gt;&lt;span class=&quot;NormalTextRun SCX9252380&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; background-color: inherit; margin: 0px; padding: 0px; user-select: text;&quot;&gt;hy are we not querying the database more directly? &amp;nbsp;I appreciate that developer productivity is a good reason to use ORM but is it a good reason in this case? &amp;nbsp;172 queries is an awful lot, especially when a lot of them are related to querying the schema so our ORM can run.&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;
&lt;span class=&quot;TextRun SCX9252380&quot; lang=&quot;EN-US&quot; style=&quot;background-color: white; font-family: &amp;quot;calibri&amp;quot; , sans-serif; font-size: 11pt; line-height: 19px; margin: 0px; padding: 0px;&quot; xml:lang=&quot;EN-US&quot;&gt;&lt;span class=&quot;NormalTextRun SCX9252380&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; background-color: inherit; margin: 0px; padding: 0px; user-select: text;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;
&lt;span class=&quot;TextRun SCX9252380&quot; lang=&quot;EN-US&quot; style=&quot;background-color: white; font-family: &amp;quot;calibri&amp;quot; , sans-serif; font-size: 11pt; line-height: 19px; margin: 0px; padding: 0px;&quot; xml:lang=&quot;EN-US&quot;&gt;&lt;span class=&quot;NormalTextRun SCX9252380&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; background-color: inherit; margin: 0px; padding: 0px; user-select: text;&quot;&gt;&lt;span class=&quot;TextRun SCX224025393&quot; lang=&quot;EN-US&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; font-size: 11pt; line-height: 19px; margin: 0px; padding: 0px; user-select: text;&quot; xml:lang=&quot;EN-US&quot;&gt;&lt;span class=&quot;NormalTextRun SCX224025393&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; background-color: inherit; margin: 0px; padding: 0px; user-select: text;&quot;&gt;Why on earth does PHP have to spend so much time in disk I/O reading all of those source files when really what we need is request routing, a database query, and a response handler? &amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;
&lt;span class=&quot;TextRun SCX9252380&quot; lang=&quot;EN-US&quot; style=&quot;background-color: white; font-family: &amp;quot;calibri&amp;quot; , sans-serif; font-size: 11pt; line-height: 19px; margin: 0px; padding: 0px;&quot; xml:lang=&quot;EN-US&quot;&gt;&lt;span class=&quot;NormalTextRun SCX9252380&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; background-color: inherit; margin: 0px; padding: 0px; user-select: text;&quot;&gt;&lt;span class=&quot;TextRun SCX224025393&quot; lang=&quot;EN-US&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; font-size: 11pt; line-height: 19px; margin: 0px; padding: 0px; user-select: text;&quot; xml:lang=&quot;EN-US&quot;&gt;&lt;span class=&quot;NormalTextRun SCX224025393&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; background-color: inherit; margin: 0px; padding: 0px; user-select: text;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;
&lt;span class=&quot;TextRun SCX9252380&quot; lang=&quot;EN-US&quot; style=&quot;background-color: white; font-family: &amp;quot;calibri&amp;quot; , sans-serif; font-size: 11pt; line-height: 19px; margin: 0px; padding: 0px;&quot; xml:lang=&quot;EN-US&quot;&gt;&lt;span class=&quot;NormalTextRun SCX9252380&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; background-color: inherit; margin: 0px; padding: 0px; user-select: text;&quot;&gt;&lt;span class=&quot;TextRun SCX224025393&quot; lang=&quot;EN-US&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; font-size: 11pt; line-height: 19px; margin: 0px; padding: 0px; user-select: text;&quot; xml:lang=&quot;EN-US&quot;&gt;&lt;span class=&quot;NormalTextRun SCX224025393&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; background-color: inherit; margin: 0px; padding: 0px; user-select: text;&quot;&gt;&lt;span class=&quot;TextRun SCX96217580&quot; lang=&quot;EN-US&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; font-size: 11pt; line-height: 19px; margin: 0px; padding: 0px; user-select: text;&quot; xml:lang=&quot;EN-US&quot;&gt;&lt;span class=&quot;SpellingError SCX96217580&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; background-color: inherit; background-image: url(&amp;quot;data:image/gif; background-position: left bottom; background-repeat: repeat-x; border-bottom: 1px solid transparent; margin: 0px; padding: 0px; user-select: text;&quot;&gt;Blackfire&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;TextRun SCX96217580&quot; lang=&quot;EN-US&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; font-size: 11pt; line-height: 19px; margin: 0px; padding: 0px; user-select: text;&quot; xml:lang=&quot;EN-US&quot;&gt;&lt;span class=&quot;NormalTextRun SCX96217580&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; background-color: inherit; margin: 0px; padding: 0px; user-select: text;&quot;&gt;&amp;nbsp;reports that 304kb of data was transmitted across the wire for this. &amp;nbsp;That seems like a lot of data for the five or six records that I&#39;m returning to the frontend.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;
&lt;span class=&quot;TextRun SCX9252380&quot; lang=&quot;EN-US&quot; style=&quot;background-color: white; font-family: &amp;quot;calibri&amp;quot; , sans-serif; font-size: 11pt; line-height: 19px; margin: 0px; padding: 0px;&quot; xml:lang=&quot;EN-US&quot;&gt;&lt;span class=&quot;NormalTextRun SCX9252380&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; background-color: inherit; margin: 0px; padding: 0px; user-select: text;&quot;&gt;&lt;span class=&quot;TextRun SCX224025393&quot; lang=&quot;EN-US&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; font-size: 11pt; line-height: 19px; margin: 0px; padding: 0px; user-select: text;&quot; xml:lang=&quot;EN-US&quot;&gt;&lt;span class=&quot;NormalTextRun SCX224025393&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; background-color: inherit; margin: 0px; padding: 0px; user-select: text;&quot;&gt;&lt;span class=&quot;TextRun SCX96217580&quot; lang=&quot;EN-US&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; font-size: 11pt; line-height: 19px; margin: 0px; padding: 0px; user-select: text;&quot; xml:lang=&quot;EN-US&quot;&gt;&lt;span class=&quot;NormalTextRun SCX96217580&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; background-color: inherit; margin: 0px; padding: 0px; user-select: text;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;
&lt;span class=&quot;TextRun SCX9252380&quot; lang=&quot;EN-US&quot; style=&quot;background-color: white; font-family: &amp;quot;calibri&amp;quot; , sans-serif; font-size: 11pt; line-height: 19px; margin: 0px; padding: 0px;&quot; xml:lang=&quot;EN-US&quot;&gt;&lt;span class=&quot;NormalTextRun SCX9252380&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; background-color: inherit; margin: 0px; padding: 0px; user-select: text;&quot;&gt;&lt;span class=&quot;TextRun SCX224025393&quot; lang=&quot;EN-US&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; font-size: 11pt; line-height: 19px; margin: 0px; padding: 0px; user-select: text;&quot; xml:lang=&quot;EN-US&quot;&gt;&lt;span class=&quot;NormalTextRun SCX224025393&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; background-color: inherit; margin: 0px; padding: 0px; user-select: text;&quot;&gt;&lt;span class=&quot;TextRun SCX96217580&quot; lang=&quot;EN-US&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; font-size: 11pt; line-height: 19px; margin: 0px; padding: 0px; user-select: text;&quot; xml:lang=&quot;EN-US&quot;&gt;&lt;span class=&quot;NormalTextRun SCX96217580&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; background-color: inherit; margin: 0px; padding: 0px; user-select: text;&quot;&gt;&lt;span class=&quot;TextRun SCX212034074&quot; lang=&quot;EN-US&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; font-size: 11pt; line-height: 19px; margin: 0px; padding: 0px; user-select: text;&quot; xml:lang=&quot;EN-US&quot;&gt;&lt;span class=&quot;NormalTextRun SCX212034074&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; background-color: inherit; margin: 0px; padding: 0px; user-select: text;&quot;&gt;The call graph is frustrating –&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;TextRun SCX212034074&quot; lang=&quot;EN-US&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; font-size: 11pt; line-height: 19px; margin: 0px; padding: 0px; user-select: text;&quot; xml:lang=&quot;EN-US&quot;&gt;&lt;span class=&quot;NormalTextRun SCX212034074&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; background-color: inherit; margin: 0px; padding: 0px; user-select: text;&quot;&gt;I&#39;m lumbered with a whole lot of black box code and I have no control over the SQL that is being run. &amp;nbsp;How can I improve the performance of this transaction?&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;
&lt;h3&gt;
&lt;span class=&quot;TextRun SCX9252380&quot; lang=&quot;EN-US&quot; style=&quot;background-color: white; font-family: &amp;quot;calibri&amp;quot; , sans-serif; font-size: 11pt; line-height: 19px; margin: 0px; padding: 0px;&quot; xml:lang=&quot;EN-US&quot;&gt;&lt;span class=&quot;NormalTextRun SCX9252380&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; background-color: inherit; margin: 0px; padding: 0px; user-select: text;&quot;&gt;&lt;span class=&quot;TextRun SCX224025393&quot; lang=&quot;EN-US&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; font-size: 11pt; line-height: 19px; margin: 0px; padding: 0px; user-select: text;&quot; xml:lang=&quot;EN-US&quot;&gt;&lt;span class=&quot;NormalTextRun SCX224025393&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; background-color: inherit; margin: 0px; padding: 0px; user-select: text;&quot;&gt;&lt;span class=&quot;TextRun SCX96217580&quot; lang=&quot;EN-US&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; font-size: 11pt; line-height: 19px; margin: 0px; padding: 0px; user-select: text;&quot; xml:lang=&quot;EN-US&quot;&gt;&lt;span class=&quot;NormalTextRun SCX96217580&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; background-color: inherit; margin: 0px; padding: 0px; user-select: text;&quot;&gt;So is PHP the best tool for this job?&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;div&gt;
&lt;span class=&quot;TextRun SCX9252380&quot; lang=&quot;EN-US&quot; style=&quot;background-color: white; font-family: &amp;quot;calibri&amp;quot; , sans-serif; font-size: 11pt; line-height: 19px; margin: 0px; padding: 0px;&quot; xml:lang=&quot;EN-US&quot;&gt;&lt;span class=&quot;NormalTextRun SCX9252380&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; background-color: inherit; margin: 0px; padding: 0px; user-select: text;&quot;&gt;&lt;span class=&quot;TextRun SCX224025393&quot; lang=&quot;EN-US&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; font-size: 11pt; line-height: 19px; margin: 0px; padding: 0px; user-select: text;&quot; xml:lang=&quot;EN-US&quot;&gt;&lt;span class=&quot;NormalTextRun SCX224025393&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; background-color: inherit; margin: 0px; padding: 0px; user-select: text;&quot;&gt;&lt;span class=&quot;TextRun SCX96217580&quot; lang=&quot;EN-US&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; font-size: 11pt; line-height: 19px; margin: 0px; padding: 0px; user-select: text;&quot; xml:lang=&quot;EN-US&quot;&gt;&lt;span class=&quot;NormalTextRun SCX96217580&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; background-color: inherit; margin: 0px; padding: 0px; user-select: text;&quot;&gt;&lt;span class=&quot;TextRun SCX212034074&quot; lang=&quot;EN-US&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; font-size: 11pt; line-height: 19px; margin: 0px; padding: 0px; user-select: text;&quot; xml:lang=&quot;EN-US&quot;&gt;&lt;span class=&quot;NormalTextRun SCX212034074&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; background-color: inherit; margin: 0px; padding: 0px; user-select: text;&quot;&gt;&lt;span class=&quot;TextRun SCX190447168&quot; lang=&quot;EN-US&quot; style=&quot;-webkit-app-region: no-drag; -webkit-appearance: none; -webkit-background-clip: border-box; -webkit-background-origin: padding-box; -webkit-border-image: none; -webkit-box-align: stretch; -webkit-box-decoration-break: slice; -webkit-box-direction: normal; -webkit-box-flex-group: 1; -webkit-box-flex: 0; -webkit-box-lines: single; -webkit-box-ordinal-group: 1; -webkit-box-orient: horizontal; -webkit-box-pack: start; -webkit-font-smoothing: auto; -webkit-highlight: none; -webkit-hyphenate-character: auto; -webkit-line-break: after-white-space; -webkit-locale: &amp;quot;en-US&amp;quot;; -webkit-margin-collapse: collapse collapse; -webkit-mask-box-image-outset: 0px; -webkit-mask-box-image-repeat: stretch; -webkit-mask-box-image-slice: 0 fill; -webkit-mask-box-image-source: none; -webkit-mask-box-image-width: auto; -webkit-mask-composite: source-over; -webkit-mask: none 0% 0% / auto repeat border-box border-box; -webkit-print-color-adjust: economy; -webkit-rtl-ordering: logical; -webkit-tap-highlight-color: rgba(0, 0, 0, 0); -webkit-text-combine: none; -webkit-text-emphasis-position: over; -webkit-text-emphasis: none rgb(0, 0, 0); -webkit-text-fill-color: rgb(0, 0, 0); -webkit-text-orientation: vertical-right; -webkit-text-security: none; -webkit-text-stroke-color: rgb(0, 0, 0); -webkit-user-drag: none; -webkit-user-modify: read-write; -webkit-writing-mode: horizontal-tb; align-content: stretch; align-items: stretch; align-self: stretch; alignment-baseline: auto; animation: none 0s ease 0s 1 normal none running; backface-visibility: visible; background-blend-mode: normal; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0); baseline-shift: 0px; border-collapse: separate; border-radius: 0px; border-spacing: 0px; border: 0px none rgb(0, 0, 0); bottom: auto; box-shadow: none; box-sizing: content-box; break-after: auto; break-before: auto; break-inside: auto; buffered-rendering: auto; caption-side: top; clear: none; clip-path: none; clip-rule: nonzero; clip: auto; color-interpolation-filters: linearRGB; color-interpolation: sRGB; color-rendering: auto; column-gap: normal; column-rule: 0px none rgb(0, 0, 0); column-span: none; columns: auto auto; cursor: text; cx: 0px; cy: 0px; d: none; direction: ltr; display: inline; dominant-baseline: auto; empty-cells: show; fill-opacity: 1; fill-rule: nonzero; fill: rgb(0, 0, 0); filter: none; flex-flow: row nowrap; flex: 0 1 auto; float: none; flood-color: rgb(0, 0, 0); flood-opacity: 1; font-kerning: auto; font-size: 14.6667px; font-stretch: normal; font-variant-ligatures: no-common-ligatures no-discretionary-ligatures no-historical-ligatures no-contextual; font-variant-numeric: normal; height: auto; hyphens: manual; image-rendering: auto; isolation: auto; justify-content: flex-start; left: auto; lighting-color: rgb(255, 255, 255); line-height: 19px; list-style: disc outside none; margin: 0px; marker: none; mask-type: luminance; mask: none; max-height: none; max-width: none; min-height: 0px; min-width: 0px; mix-blend-mode: normal; motion: none 0px auto 0deg; object-fit: fill; object-position: 50% 50%; offset-rotate: auto 0deg; opacity: 1; order: 0; outline-offset: 0px; outline: rgb(0, 0, 0) none 0px; overflow-anchor: auto; overflow-wrap: break-word; overflow: visible; padding: 0px; paint-order: fill; perspective-origin: 0px 0px; perspective: none; pointer-events: auto; position: static; r: 0px; resize: none; right: auto; rx: auto; ry: auto; shape-image-threshold: 0; shape-margin: 0px; shape-outside: none; shape-rendering: auto; speak: normal; stop-color: rgb(0, 0, 0); stop-opacity: 1; stroke-dasharray: none; stroke-dashoffset: 0px; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 4; stroke-opacity: 1; stroke-width: 1px; stroke: none; tab-size: 8; table-layout: auto; text-align-last: auto; text-anchor: start; text-overflow: clip; text-rendering: auto; text-shadow: none; text-size-adjust: auto; top: auto; touch-action: auto; transform-origin: 0px 0px 0px; transform-style: flat; transform: none; transition: all 0s ease 0s; unicode-bidi: normal; user-select: text; vector-effect: none; vertical-align: baseline; visibility: visible; width: auto; will-change: auto; word-break: normal; word-wrap: break-word; writing-mode: horizontal-tb; x: 0px; y: 0px; z-index: auto; zoom: 1;&quot; xml:lang=&quot;EN-US&quot;&gt;&lt;span class=&quot;NormalTextRun SCX190447168&quot; style=&quot;background-color: inherit;&quot;&gt;I have previously had intractable problems with PHP when it comes to memory management. &amp;nbsp;It&#39;s pretty complicated and it differs depending on the way that PHP is run but I do not have 100% confidence in PHP&#39;s garbage collection. &amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;
&lt;span class=&quot;TextRun SCX9252380&quot; lang=&quot;EN-US&quot; style=&quot;background-color: white; font-family: &amp;quot;calibri&amp;quot; , sans-serif; font-size: 11pt; line-height: 19px; margin: 0px; padding: 0px;&quot; xml:lang=&quot;EN-US&quot;&gt;&lt;span class=&quot;NormalTextRun SCX9252380&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; background-color: inherit; margin: 0px; padding: 0px; user-select: text;&quot;&gt;&lt;span class=&quot;TextRun SCX224025393&quot; lang=&quot;EN-US&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; font-size: 11pt; line-height: 19px; margin: 0px; padding: 0px; user-select: text;&quot; xml:lang=&quot;EN-US&quot;&gt;&lt;span class=&quot;NormalTextRun SCX224025393&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; background-color: inherit; margin: 0px; padding: 0px; user-select: text;&quot;&gt;&lt;span class=&quot;TextRun SCX96217580&quot; lang=&quot;EN-US&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; font-size: 11pt; line-height: 19px; margin: 0px; padding: 0px; user-select: text;&quot; xml:lang=&quot;EN-US&quot;&gt;&lt;span class=&quot;NormalTextRun SCX96217580&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; background-color: inherit; margin: 0px; padding: 0px; user-select: text;&quot;&gt;&lt;span class=&quot;TextRun SCX212034074&quot; lang=&quot;EN-US&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; font-size: 11pt; line-height: 19px; margin: 0px; padding: 0px; user-select: text;&quot; xml:lang=&quot;EN-US&quot;&gt;&lt;span class=&quot;NormalTextRun SCX212034074&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; background-color: inherit; margin: 0px; padding: 0px; user-select: text;&quot;&gt;&lt;span class=&quot;TextRun SCX190447168&quot; lang=&quot;EN-US&quot; style=&quot;-webkit-app-region: no-drag; -webkit-appearance: none; -webkit-background-clip: border-box; -webkit-background-origin: padding-box; -webkit-border-image: none; -webkit-box-align: stretch; -webkit-box-decoration-break: slice; -webkit-box-direction: normal; -webkit-box-flex-group: 1; -webkit-box-flex: 0; -webkit-box-lines: single; -webkit-box-ordinal-group: 1; -webkit-box-orient: horizontal; -webkit-box-pack: start; -webkit-font-smoothing: auto; -webkit-highlight: none; -webkit-hyphenate-character: auto; -webkit-line-break: after-white-space; -webkit-locale: &amp;quot;en-US&amp;quot;; -webkit-margin-collapse: collapse collapse; -webkit-mask-box-image-outset: 0px; -webkit-mask-box-image-repeat: stretch; -webkit-mask-box-image-slice: 0 fill; -webkit-mask-box-image-source: none; -webkit-mask-box-image-width: auto; -webkit-mask-composite: source-over; -webkit-mask: none 0% 0% / auto repeat border-box border-box; -webkit-print-color-adjust: economy; -webkit-rtl-ordering: logical; -webkit-tap-highlight-color: rgba(0, 0, 0, 0); -webkit-text-combine: none; -webkit-text-emphasis-position: over; -webkit-text-emphasis: none rgb(0, 0, 0); -webkit-text-fill-color: rgb(0, 0, 0); -webkit-text-orientation: vertical-right; -webkit-text-security: none; -webkit-text-stroke-color: rgb(0, 0, 0); -webkit-user-drag: none; -webkit-user-modify: read-write; -webkit-writing-mode: horizontal-tb; align-content: stretch; align-items: stretch; align-self: stretch; alignment-baseline: auto; animation: none 0s ease 0s 1 normal none running; backface-visibility: visible; background-blend-mode: normal; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0); baseline-shift: 0px; border-collapse: separate; border-radius: 0px; border-spacing: 0px; border: 0px none rgb(0, 0, 0); bottom: auto; box-shadow: none; box-sizing: content-box; break-after: auto; break-before: auto; break-inside: auto; buffered-rendering: auto; caption-side: top; clear: none; clip-path: none; clip-rule: nonzero; clip: auto; color-interpolation-filters: linearRGB; color-interpolation: sRGB; color-rendering: auto; column-gap: normal; column-rule: 0px none rgb(0, 0, 0); column-span: none; columns: auto auto; cursor: text; cx: 0px; cy: 0px; d: none; direction: ltr; display: inline; dominant-baseline: auto; empty-cells: show; fill-opacity: 1; fill-rule: nonzero; fill: rgb(0, 0, 0); filter: none; flex-flow: row nowrap; flex: 0 1 auto; float: none; flood-color: rgb(0, 0, 0); flood-opacity: 1; font-kerning: auto; font-size: 14.6667px; font-stretch: normal; font-variant-ligatures: no-common-ligatures no-discretionary-ligatures no-historical-ligatures no-contextual; font-variant-numeric: normal; height: auto; hyphens: manual; image-rendering: auto; isolation: auto; justify-content: flex-start; left: auto; lighting-color: rgb(255, 255, 255); line-height: 19px; list-style: disc outside none; margin: 0px; marker: none; mask-type: luminance; mask: none; max-height: none; max-width: none; min-height: 0px; min-width: 0px; mix-blend-mode: normal; motion: none 0px auto 0deg; object-fit: fill; object-position: 50% 50%; offset-rotate: auto 0deg; opacity: 1; order: 0; outline-offset: 0px; outline: rgb(0, 0, 0) none 0px; overflow-anchor: auto; overflow-wrap: break-word; overflow: visible; padding: 0px; paint-order: fill; perspective-origin: 0px 0px; perspective: none; pointer-events: auto; position: static; r: 0px; resize: none; right: auto; rx: auto; ry: auto; shape-image-threshold: 0; shape-margin: 0px; shape-outside: none; shape-rendering: auto; speak: normal; stop-color: rgb(0, 0, 0); stop-opacity: 1; stroke-dasharray: none; stroke-dashoffset: 0px; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 4; stroke-opacity: 1; stroke-width: 1px; stroke: none; tab-size: 8; table-layout: auto; text-align-last: auto; text-anchor: start; text-overflow: clip; text-rendering: auto; text-shadow: none; text-size-adjust: auto; top: auto; touch-action: auto; transform-origin: 0px 0px 0px; transform-style: flat; transform: none; transition: all 0s ease 0s; unicode-bidi: normal; user-select: text; vector-effect: none; vertical-align: baseline; visibility: visible; width: auto; will-change: auto; word-break: normal; word-wrap: break-word; writing-mode: horizontal-tb; x: 0px; y: 0px; z-index: auto; zoom: 1;&quot; xml:lang=&quot;EN-US&quot;&gt;&lt;span class=&quot;NormalTextRun SCX190447168&quot; style=&quot;background-color: inherit;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;
&lt;span class=&quot;TextRun SCX9252380&quot; lang=&quot;EN-US&quot; style=&quot;background-color: white; font-family: &amp;quot;calibri&amp;quot; , sans-serif; font-size: 11pt; line-height: 19px; margin: 0px; padding: 0px;&quot; xml:lang=&quot;EN-US&quot;&gt;&lt;span class=&quot;NormalTextRun SCX9252380&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; background-color: inherit; margin: 0px; padding: 0px; user-select: text;&quot;&gt;&lt;span class=&quot;TextRun SCX224025393&quot; lang=&quot;EN-US&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; font-size: 11pt; line-height: 19px; margin: 0px; padding: 0px; user-select: text;&quot; xml:lang=&quot;EN-US&quot;&gt;&lt;span class=&quot;NormalTextRun SCX224025393&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; background-color: inherit; margin: 0px; padding: 0px; user-select: text;&quot;&gt;&lt;span class=&quot;TextRun SCX96217580&quot; lang=&quot;EN-US&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; font-size: 11pt; line-height: 19px; margin: 0px; padding: 0px; user-select: text;&quot; xml:lang=&quot;EN-US&quot;&gt;&lt;span class=&quot;NormalTextRun SCX96217580&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; background-color: inherit; margin: 0px; padding: 0px; user-select: text;&quot;&gt;&lt;span class=&quot;TextRun SCX212034074&quot; lang=&quot;EN-US&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; font-size: 11pt; line-height: 19px; margin: 0px; padding: 0px; user-select: text;&quot; xml:lang=&quot;EN-US&quot;&gt;&lt;span class=&quot;NormalTextRun SCX212034074&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; background-color: inherit; margin: 0px; padding: 0px; user-select: text;&quot;&gt;&lt;span class=&quot;TextRun SCX190447168&quot; lang=&quot;EN-US&quot; style=&quot;-webkit-app-region: no-drag; -webkit-appearance: none; -webkit-background-clip: border-box; -webkit-background-origin: padding-box; -webkit-border-image: none; -webkit-box-align: stretch; -webkit-box-decoration-break: slice; -webkit-box-direction: normal; -webkit-box-flex-group: 1; -webkit-box-flex: 0; -webkit-box-lines: single; -webkit-box-ordinal-group: 1; -webkit-box-orient: horizontal; -webkit-box-pack: start; -webkit-font-smoothing: auto; -webkit-highlight: none; -webkit-hyphenate-character: auto; -webkit-line-break: after-white-space; -webkit-locale: &amp;quot;en-US&amp;quot;; -webkit-margin-collapse: collapse collapse; -webkit-mask-box-image-outset: 0px; -webkit-mask-box-image-repeat: stretch; -webkit-mask-box-image-slice: 0 fill; -webkit-mask-box-image-source: none; -webkit-mask-box-image-width: auto; -webkit-mask-composite: source-over; -webkit-mask: none 0% 0% / auto repeat border-box border-box; -webkit-print-color-adjust: economy; -webkit-rtl-ordering: logical; -webkit-tap-highlight-color: rgba(0, 0, 0, 0); -webkit-text-combine: none; -webkit-text-emphasis-position: over; -webkit-text-emphasis: none rgb(0, 0, 0); -webkit-text-fill-color: rgb(0, 0, 0); -webkit-text-orientation: vertical-right; -webkit-text-security: none; -webkit-text-stroke-color: rgb(0, 0, 0); -webkit-user-drag: none; -webkit-user-modify: read-write; -webkit-writing-mode: horizontal-tb; align-content: stretch; align-items: stretch; align-self: stretch; alignment-baseline: auto; animation: none 0s ease 0s 1 normal none running; backface-visibility: visible; background-blend-mode: normal; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0); baseline-shift: 0px; border-collapse: separate; border-radius: 0px; border-spacing: 0px; border: 0px none rgb(0, 0, 0); bottom: auto; box-shadow: none; box-sizing: content-box; break-after: auto; break-before: auto; break-inside: auto; buffered-rendering: auto; caption-side: top; clear: none; clip-path: none; clip-rule: nonzero; clip: auto; color-interpolation-filters: linearRGB; color-interpolation: sRGB; color-rendering: auto; column-gap: normal; column-rule: 0px none rgb(0, 0, 0); column-span: none; columns: auto auto; cursor: text; cx: 0px; cy: 0px; d: none; direction: ltr; display: inline; dominant-baseline: auto; empty-cells: show; fill-opacity: 1; fill-rule: nonzero; fill: rgb(0, 0, 0); filter: none; flex-flow: row nowrap; flex: 0 1 auto; float: none; flood-color: rgb(0, 0, 0); flood-opacity: 1; font-kerning: auto; font-size: 14.6667px; font-stretch: normal; font-variant-ligatures: no-common-ligatures no-discretionary-ligatures no-historical-ligatures no-contextual; font-variant-numeric: normal; height: auto; hyphens: manual; image-rendering: auto; isolation: auto; justify-content: flex-start; left: auto; lighting-color: rgb(255, 255, 255); line-height: 19px; list-style: disc outside none; margin: 0px; marker: none; mask-type: luminance; mask: none; max-height: none; max-width: none; min-height: 0px; min-width: 0px; mix-blend-mode: normal; motion: none 0px auto 0deg; object-fit: fill; object-position: 50% 50%; offset-rotate: auto 0deg; opacity: 1; order: 0; outline-offset: 0px; outline: rgb(0, 0, 0) none 0px; overflow-anchor: auto; overflow-wrap: break-word; overflow: visible; padding: 0px; paint-order: fill; perspective-origin: 0px 0px; perspective: none; pointer-events: auto; position: static; r: 0px; resize: none; right: auto; rx: auto; ry: auto; shape-image-threshold: 0; shape-margin: 0px; shape-outside: none; shape-rendering: auto; speak: normal; stop-color: rgb(0, 0, 0); stop-opacity: 1; stroke-dasharray: none; stroke-dashoffset: 0px; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 4; stroke-opacity: 1; stroke-width: 1px; stroke: none; tab-size: 8; table-layout: auto; text-align-last: auto; text-anchor: start; text-overflow: clip; text-rendering: auto; text-shadow: none; text-size-adjust: auto; top: auto; touch-action: auto; transform-origin: 0px 0px 0px; transform-style: flat; transform: none; transition: all 0s ease 0s; unicode-bidi: normal; user-select: text; vector-effect: none; vertical-align: baseline; visibility: visible; width: auto; will-change: auto; word-break: normal; word-wrap: break-word; writing-mode: horizontal-tb; x: 0px; y: 0px; z-index: auto; zoom: 1;&quot; xml:lang=&quot;EN-US&quot;&gt;&lt;span class=&quot;NormalTextRun SCX190447168&quot; style=&quot;background-color: inherit;&quot;&gt;&lt;span class=&quot;TextRun SCX44968697&quot; lang=&quot;EN-US&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; font-size: 11pt; line-height: 19px; margin: 0px; padding: 0px; user-select: text;&quot; xml:lang=&quot;EN-US&quot;&gt;&lt;span class=&quot;NormalTextRun SCX44968697&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; background-color: inherit; margin: 0px; padding: 0px; user-select: text;&quot;&gt;Circular object references (which I encountered while using an ORM&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;TextRun SCX44968697&quot; lang=&quot;EN-US&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; font-size: 11pt; line-height: 19px; margin: 0px; padding: 0px; user-select: text;&quot; xml:lang=&quot;EN-US&quot;&gt;&lt;span class=&quot;NormalTextRun SCX44968697&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; background-color: inherit; margin: 0px; padding: 0px; user-select: text;&quot;&gt;where a model referenced itself as a parent to form a hierarchy) cannot be completely collected by PHP&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;TextRun SCX44968697&quot; lang=&quot;EN-US&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; font-size: 11pt; line-height: 19px; margin: 0px; padding: 0px; user-select: text;&quot; xml:lang=&quot;EN-US&quot;&gt;&lt;span class=&quot;NormalTextRun SCX44968697&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; background-color: inherit; margin: 0px; padding: 0px; user-select: text;&quot;&gt;. &amp;nbsp;PHP actually relies on the container the machine runs in to collect this memory. &amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;
&lt;span class=&quot;TextRun SCX9252380&quot; lang=&quot;EN-US&quot; style=&quot;background-color: white; font-family: &amp;quot;calibri&amp;quot; , sans-serif; font-size: 11pt; line-height: 19px; margin: 0px; padding: 0px;&quot; xml:lang=&quot;EN-US&quot;&gt;&lt;span class=&quot;NormalTextRun SCX9252380&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; background-color: inherit; margin: 0px; padding: 0px; user-select: text;&quot;&gt;&lt;span class=&quot;TextRun SCX224025393&quot; lang=&quot;EN-US&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; font-size: 11pt; line-height: 19px; margin: 0px; padding: 0px; user-select: text;&quot; xml:lang=&quot;EN-US&quot;&gt;&lt;span class=&quot;NormalTextRun SCX224025393&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; background-color: inherit; margin: 0px; padding: 0px; user-select: text;&quot;&gt;&lt;span class=&quot;TextRun SCX96217580&quot; lang=&quot;EN-US&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; font-size: 11pt; line-height: 19px; margin: 0px; padding: 0px; user-select: text;&quot; xml:lang=&quot;EN-US&quot;&gt;&lt;span class=&quot;NormalTextRun SCX96217580&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; background-color: inherit; margin: 0px; padding: 0px; user-select: text;&quot;&gt;&lt;span class=&quot;TextRun SCX212034074&quot; lang=&quot;EN-US&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; font-size: 11pt; line-height: 19px; margin: 0px; padding: 0px; user-select: text;&quot; xml:lang=&quot;EN-US&quot;&gt;&lt;span class=&quot;NormalTextRun SCX212034074&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; background-color: inherit; margin: 0px; padding: 0px; user-select: text;&quot;&gt;&lt;span class=&quot;TextRun SCX190447168&quot; lang=&quot;EN-US&quot; style=&quot;-webkit-app-region: no-drag; -webkit-appearance: none; -webkit-background-clip: border-box; -webkit-background-origin: padding-box; -webkit-border-image: none; -webkit-box-align: stretch; -webkit-box-decoration-break: slice; -webkit-box-direction: normal; -webkit-box-flex-group: 1; -webkit-box-flex: 0; -webkit-box-lines: single; -webkit-box-ordinal-group: 1; -webkit-box-orient: horizontal; -webkit-box-pack: start; -webkit-font-smoothing: auto; -webkit-highlight: none; -webkit-hyphenate-character: auto; -webkit-line-break: after-white-space; -webkit-locale: &amp;quot;en-US&amp;quot;; -webkit-margin-collapse: collapse collapse; -webkit-mask-box-image-outset: 0px; -webkit-mask-box-image-repeat: stretch; -webkit-mask-box-image-slice: 0 fill; -webkit-mask-box-image-source: none; -webkit-mask-box-image-width: auto; -webkit-mask-composite: source-over; -webkit-mask: none 0% 0% / auto repeat border-box border-box; -webkit-print-color-adjust: economy; -webkit-rtl-ordering: logical; -webkit-tap-highlight-color: rgba(0, 0, 0, 0); -webkit-text-combine: none; -webkit-text-emphasis-position: over; -webkit-text-emphasis: none rgb(0, 0, 0); -webkit-text-fill-color: rgb(0, 0, 0); -webkit-text-orientation: vertical-right; -webkit-text-security: none; -webkit-text-stroke-color: rgb(0, 0, 0); -webkit-user-drag: none; -webkit-user-modify: read-write; -webkit-writing-mode: horizontal-tb; align-content: stretch; align-items: stretch; align-self: stretch; alignment-baseline: auto; animation: none 0s ease 0s 1 normal none running; backface-visibility: visible; background-blend-mode: normal; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0); baseline-shift: 0px; border-collapse: separate; border-radius: 0px; border-spacing: 0px; border: 0px none rgb(0, 0, 0); bottom: auto; box-shadow: none; box-sizing: content-box; break-after: auto; break-before: auto; break-inside: auto; buffered-rendering: auto; caption-side: top; clear: none; clip-path: none; clip-rule: nonzero; clip: auto; color-interpolation-filters: linearRGB; color-interpolation: sRGB; color-rendering: auto; column-gap: normal; column-rule: 0px none rgb(0, 0, 0); column-span: none; columns: auto auto; cursor: text; cx: 0px; cy: 0px; d: none; direction: ltr; display: inline; dominant-baseline: auto; empty-cells: show; fill-opacity: 1; fill-rule: nonzero; fill: rgb(0, 0, 0); filter: none; flex-flow: row nowrap; flex: 0 1 auto; float: none; flood-color: rgb(0, 0, 0); flood-opacity: 1; font-kerning: auto; font-size: 14.6667px; font-stretch: normal; font-variant-ligatures: no-common-ligatures no-discretionary-ligatures no-historical-ligatures no-contextual; font-variant-numeric: normal; height: auto; hyphens: manual; image-rendering: auto; isolation: auto; justify-content: flex-start; left: auto; lighting-color: rgb(255, 255, 255); line-height: 19px; list-style: disc outside none; margin: 0px; marker: none; mask-type: luminance; mask: none; max-height: none; max-width: none; min-height: 0px; min-width: 0px; mix-blend-mode: normal; motion: none 0px auto 0deg; object-fit: fill; object-position: 50% 50%; offset-rotate: auto 0deg; opacity: 1; order: 0; outline-offset: 0px; outline: rgb(0, 0, 0) none 0px; overflow-anchor: auto; overflow-wrap: break-word; overflow: visible; padding: 0px; paint-order: fill; perspective-origin: 0px 0px; perspective: none; pointer-events: auto; position: static; r: 0px; resize: none; right: auto; rx: auto; ry: auto; shape-image-threshold: 0; shape-margin: 0px; shape-outside: none; shape-rendering: auto; speak: normal; stop-color: rgb(0, 0, 0); stop-opacity: 1; stroke-dasharray: none; stroke-dashoffset: 0px; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 4; stroke-opacity: 1; stroke-width: 1px; stroke: none; tab-size: 8; table-layout: auto; text-align-last: auto; text-anchor: start; text-overflow: clip; text-rendering: auto; text-shadow: none; text-size-adjust: auto; top: auto; touch-action: auto; transform-origin: 0px 0px 0px; transform-style: flat; transform: none; transition: all 0s ease 0s; unicode-bidi: normal; user-select: text; vector-effect: none; vertical-align: baseline; visibility: visible; width: auto; will-change: auto; word-break: normal; word-wrap: break-word; writing-mode: horizontal-tb; x: 0px; y: 0px; z-index: auto; zoom: 1;&quot; xml:lang=&quot;EN-US&quot;&gt;&lt;span class=&quot;NormalTextRun SCX190447168&quot; style=&quot;background-color: inherit;&quot;&gt;&lt;span class=&quot;TextRun SCX44968697&quot; lang=&quot;EN-US&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; font-size: 11pt; line-height: 19px; margin: 0px; padding: 0px; user-select: text;&quot; xml:lang=&quot;EN-US&quot;&gt;&lt;span class=&quot;NormalTextRun SCX44968697&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; background-color: inherit; margin: 0px; padding: 0px; user-select: text;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;
&lt;span class=&quot;TextRun SCX9252380&quot; lang=&quot;EN-US&quot; style=&quot;background-color: white; font-family: &amp;quot;calibri&amp;quot; , sans-serif; font-size: 11pt; line-height: 19px; margin: 0px; padding: 0px;&quot; xml:lang=&quot;EN-US&quot;&gt;&lt;span class=&quot;NormalTextRun SCX9252380&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; background-color: inherit; margin: 0px; padding: 0px; user-select: text;&quot;&gt;&lt;span class=&quot;TextRun SCX224025393&quot; lang=&quot;EN-US&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; font-size: 11pt; line-height: 19px; margin: 0px; padding: 0px; user-select: text;&quot; xml:lang=&quot;EN-US&quot;&gt;&lt;span class=&quot;NormalTextRun SCX224025393&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; background-color: inherit; margin: 0px; padding: 0px; user-select: text;&quot;&gt;&lt;span class=&quot;TextRun SCX96217580&quot; lang=&quot;EN-US&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; font-size: 11pt; line-height: 19px; margin: 0px; padding: 0px; user-select: text;&quot; xml:lang=&quot;EN-US&quot;&gt;&lt;span class=&quot;NormalTextRun SCX96217580&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; background-color: inherit; margin: 0px; padding: 0px; user-select: text;&quot;&gt;&lt;span class=&quot;TextRun SCX212034074&quot; lang=&quot;EN-US&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; font-size: 11pt; line-height: 19px; margin: 0px; padding: 0px; user-select: text;&quot; xml:lang=&quot;EN-US&quot;&gt;&lt;span class=&quot;NormalTextRun SCX212034074&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; background-color: inherit; margin: 0px; padding: 0px; user-select: text;&quot;&gt;&lt;span class=&quot;TextRun SCX190447168&quot; lang=&quot;EN-US&quot; style=&quot;-webkit-app-region: no-drag; -webkit-appearance: none; -webkit-background-clip: border-box; -webkit-background-origin: padding-box; -webkit-border-image: none; -webkit-box-align: stretch; -webkit-box-decoration-break: slice; -webkit-box-direction: normal; -webkit-box-flex-group: 1; -webkit-box-flex: 0; -webkit-box-lines: single; -webkit-box-ordinal-group: 1; -webkit-box-orient: horizontal; -webkit-box-pack: start; -webkit-font-smoothing: auto; -webkit-highlight: none; -webkit-hyphenate-character: auto; -webkit-line-break: after-white-space; -webkit-locale: &amp;quot;en-US&amp;quot;; -webkit-margin-collapse: collapse collapse; -webkit-mask-box-image-outset: 0px; -webkit-mask-box-image-repeat: stretch; -webkit-mask-box-image-slice: 0 fill; -webkit-mask-box-image-source: none; -webkit-mask-box-image-width: auto; -webkit-mask-composite: source-over; -webkit-mask: none 0% 0% / auto repeat border-box border-box; -webkit-print-color-adjust: economy; -webkit-rtl-ordering: logical; -webkit-tap-highlight-color: rgba(0, 0, 0, 0); -webkit-text-combine: none; -webkit-text-emphasis-position: over; -webkit-text-emphasis: none rgb(0, 0, 0); -webkit-text-fill-color: rgb(0, 0, 0); -webkit-text-orientation: vertical-right; -webkit-text-security: none; -webkit-text-stroke-color: rgb(0, 0, 0); -webkit-user-drag: none; -webkit-user-modify: read-write; -webkit-writing-mode: horizontal-tb; align-content: stretch; align-items: stretch; align-self: stretch; alignment-baseline: auto; animation: none 0s ease 0s 1 normal none running; backface-visibility: visible; background-blend-mode: normal; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0); baseline-shift: 0px; border-collapse: separate; border-radius: 0px; border-spacing: 0px; border: 0px none rgb(0, 0, 0); bottom: auto; box-shadow: none; box-sizing: content-box; break-after: auto; break-before: auto; break-inside: auto; buffered-rendering: auto; caption-side: top; clear: none; clip-path: none; clip-rule: nonzero; clip: auto; color-interpolation-filters: linearRGB; color-interpolation: sRGB; color-rendering: auto; column-gap: normal; column-rule: 0px none rgb(0, 0, 0); column-span: none; columns: auto auto; cursor: text; cx: 0px; cy: 0px; d: none; direction: ltr; display: inline; dominant-baseline: auto; empty-cells: show; fill-opacity: 1; fill-rule: nonzero; fill: rgb(0, 0, 0); filter: none; flex-flow: row nowrap; flex: 0 1 auto; float: none; flood-color: rgb(0, 0, 0); flood-opacity: 1; font-kerning: auto; font-size: 14.6667px; font-stretch: normal; font-variant-numeric: normal; height: auto; hyphens: manual; image-rendering: auto; isolation: auto; justify-content: flex-start; left: auto; lighting-color: rgb(255, 255, 255); line-height: 19px; list-style: disc outside none; margin: 0px; marker: none; mask-type: luminance; mask: none; max-height: none; max-width: none; min-height: 0px; min-width: 0px; mix-blend-mode: normal; motion: none 0px auto 0deg; object-fit: fill; object-position: 50% 50%; offset-rotate: auto 0deg; opacity: 1; order: 0; outline-offset: 0px; outline: rgb(0, 0, 0) none 0px; overflow-anchor: auto; overflow-wrap: break-word; overflow: visible; padding: 0px; paint-order: fill; perspective-origin: 0px 0px; perspective: none; pointer-events: auto; position: static; r: 0px; resize: none; right: auto; rx: auto; ry: auto; shape-image-threshold: 0; shape-margin: 0px; shape-outside: none; shape-rendering: auto; speak: normal; stop-color: rgb(0, 0, 0); stop-opacity: 1; stroke-dasharray: none; stroke-dashoffset: 0px; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 4; stroke-opacity: 1; stroke-width: 1px; stroke: none; tab-size: 8; table-layout: auto; text-align-last: auto; text-anchor: start; text-overflow: clip; text-rendering: auto; text-shadow: none; text-size-adjust: auto; top: auto; touch-action: auto; transform-origin: 0px 0px 0px; transform-style: flat; transform: none; transition: all 0s ease 0s; unicode-bidi: normal; user-select: text; vector-effect: none; vertical-align: baseline; visibility: visible; width: auto; will-change: auto; word-break: normal; word-wrap: break-word; writing-mode: horizontal-tb; x: 0px; y: 0px; z-index: auto; zoom: 1;&quot; xml:lang=&quot;EN-US&quot;&gt;&lt;span class=&quot;NormalTextRun SCX190447168&quot; style=&quot;background-color: inherit;&quot;&gt;&lt;span class=&quot;TextRun SCX44968697&quot; lang=&quot;EN-US&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; font-size: 11pt; line-height: 19px; margin: 0px; padding: 0px; user-select: text;&quot; xml:lang=&quot;EN-US&quot;&gt;&lt;span class=&quot;NormalTextRun SCX44968697&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; background-color: inherit; margin: 0px; padding: 0px; user-select: text;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;div class=&quot;OutlineElement Ltr SCX106645496&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; clear: both; cursor: text; direction: ltr; font-family: &amp;quot;Segoe UI&amp;quot;, Tahoma, Verdana, sans-serif; font-size: 8px; font-variant-ligatures: normal; margin: 0px; overflow: visible; padding: 0px; position: relative; user-select: text;&quot;&gt;
&lt;div class=&quot;Paragraph SCX106645496&quot; lang=&quot;EN-US&quot; paraeid=&quot;{3a6a8fb6-c855-46ad-bf77-ee1e04db3770}{69}&quot; paraid=&quot;1940148154&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; background-color: transparent; color: windowtext; padding: 0px; user-select: text; vertical-align: baseline; word-wrap: break-word;&quot; xml:lang=&quot;EN-US&quot;&gt;
&lt;span class=&quot;TextRun SCX9252380&quot; lang=&quot;EN-US&quot; style=&quot;background-color: white; font-family: &amp;quot;calibri&amp;quot; , sans-serif; font-size: 11pt; line-height: 19px; margin: 0px; padding: 0px;&quot; xml:lang=&quot;EN-US&quot;&gt;&lt;span class=&quot;NormalTextRun SCX9252380&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; background-color: inherit; margin: 0px; padding: 0px; user-select: text;&quot;&gt;&lt;span class=&quot;TextRun SCX224025393&quot; lang=&quot;EN-US&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; font-size: 11pt; line-height: 19px; margin: 0px; padding: 0px; user-select: text;&quot; xml:lang=&quot;EN-US&quot;&gt;&lt;span class=&quot;NormalTextRun SCX224025393&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; background-color: inherit; margin: 0px; padding: 0px; user-select: text;&quot;&gt;&lt;span class=&quot;TextRun SCX96217580&quot; lang=&quot;EN-US&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; font-size: 11pt; line-height: 19px; margin: 0px; padding: 0px; user-select: text;&quot; xml:lang=&quot;EN-US&quot;&gt;&lt;span class=&quot;NormalTextRun SCX96217580&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; background-color: inherit; margin: 0px; padding: 0px; user-select: text;&quot;&gt;&lt;span class=&quot;TextRun SCX212034074&quot; lang=&quot;EN-US&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; font-size: 11pt; line-height: 19px; margin: 0px; padding: 0px; user-select: text;&quot; xml:lang=&quot;EN-US&quot;&gt;&lt;span class=&quot;NormalTextRun SCX212034074&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; background-color: inherit; margin: 0px; padding: 0px; user-select: text;&quot;&gt;&lt;span class=&quot;TextRun SCX190447168&quot; lang=&quot;EN-US&quot; style=&quot;-webkit-app-region: no-drag; -webkit-appearance: none; -webkit-background-clip: border-box; -webkit-background-origin: padding-box; -webkit-border-image: none; -webkit-box-align: stretch; -webkit-box-decoration-break: slice; -webkit-box-direction: normal; -webkit-box-flex-group: 1; -webkit-box-flex: 0; -webkit-box-lines: single; -webkit-box-ordinal-group: 1; -webkit-box-orient: horizontal; -webkit-box-pack: start; -webkit-font-smoothing: auto; -webkit-highlight: none; -webkit-hyphenate-character: auto; -webkit-line-break: after-white-space; -webkit-locale: &amp;quot;en-US&amp;quot;; -webkit-margin-collapse: collapse collapse; -webkit-mask-box-image-outset: 0px; -webkit-mask-box-image-repeat: stretch; -webkit-mask-box-image-slice: 0 fill; -webkit-mask-box-image-source: none; -webkit-mask-box-image-width: auto; -webkit-mask-composite: source-over; -webkit-mask: none 0% 0% / auto repeat border-box border-box; -webkit-print-color-adjust: economy; -webkit-rtl-ordering: logical; -webkit-tap-highlight-color: rgba(0, 0, 0, 0); -webkit-text-combine: none; -webkit-text-emphasis-position: over; -webkit-text-emphasis: none rgb(0, 0, 0); -webkit-text-fill-color: rgb(0, 0, 0); -webkit-text-orientation: vertical-right; -webkit-text-security: none; -webkit-text-stroke-color: rgb(0, 0, 0); -webkit-user-drag: none; -webkit-user-modify: read-write; -webkit-writing-mode: horizontal-tb; align-content: stretch; align-items: stretch; align-self: stretch; alignment-baseline: auto; animation: none 0s ease 0s 1 normal none running; backface-visibility: visible; background-blend-mode: normal; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0); baseline-shift: 0px; border-collapse: separate; border-radius: 0px; border-spacing: 0px; border: 0px none rgb(0, 0, 0); bottom: auto; box-shadow: none; box-sizing: content-box; break-after: auto; break-before: auto; break-inside: auto; buffered-rendering: auto; caption-side: top; clear: none; clip-path: none; clip-rule: nonzero; clip: auto; color-interpolation-filters: linearRGB; color-interpolation: sRGB; color-rendering: auto; column-gap: normal; column-rule: 0px none rgb(0, 0, 0); column-span: none; columns: auto auto; cursor: text; cx: 0px; cy: 0px; d: none; direction: ltr; display: inline; dominant-baseline: auto; empty-cells: show; fill-opacity: 1; fill-rule: nonzero; fill: rgb(0, 0, 0); filter: none; flex-flow: row nowrap; flex: 0 1 auto; float: none; flood-color: rgb(0, 0, 0); flood-opacity: 1; font-kerning: auto; font-size: 14.6667px; font-stretch: normal; font-variant-numeric: normal; height: auto; hyphens: manual; image-rendering: auto; isolation: auto; justify-content: flex-start; left: auto; lighting-color: rgb(255, 255, 255); line-height: 19px; list-style: disc outside none; margin: 0px; marker: none; mask-type: luminance; mask: none; max-height: none; max-width: none; min-height: 0px; min-width: 0px; mix-blend-mode: normal; motion: none 0px auto 0deg; object-fit: fill; object-position: 50% 50%; offset-rotate: auto 0deg; opacity: 1; order: 0; outline-offset: 0px; outline: rgb(0, 0, 0) none 0px; overflow-anchor: auto; overflow-wrap: break-word; overflow: visible; padding: 0px; paint-order: fill; perspective-origin: 0px 0px; perspective: none; pointer-events: auto; position: static; r: 0px; resize: none; right: auto; rx: auto; ry: auto; shape-image-threshold: 0; shape-margin: 0px; shape-outside: none; shape-rendering: auto; speak: normal; stop-color: rgb(0, 0, 0); stop-opacity: 1; stroke-dasharray: none; stroke-dashoffset: 0px; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 4; stroke-opacity: 1; stroke-width: 1px; stroke: none; tab-size: 8; table-layout: auto; text-align-last: auto; text-anchor: start; text-overflow: clip; text-rendering: auto; text-shadow: none; text-size-adjust: auto; top: auto; touch-action: auto; transform-origin: 0px 0px 0px; transform-style: flat; transform: none; transition: all 0s ease 0s; unicode-bidi: normal; user-select: text; vector-effect: none; vertical-align: baseline; visibility: visible; width: auto; will-change: auto; word-break: normal; word-wrap: break-word; writing-mode: horizontal-tb; x: 0px; y: 0px; z-index: auto; zoom: 1;&quot; xml:lang=&quot;EN-US&quot;&gt;&lt;span class=&quot;NormalTextRun SCX190447168&quot; style=&quot;background-color: inherit;&quot;&gt;&lt;span class=&quot;TextRun SCX44968697&quot; lang=&quot;EN-US&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; font-size: 11pt; line-height: 19px; margin: 0px; padding: 0px; user-select: text;&quot; xml:lang=&quot;EN-US&quot;&gt;&lt;span class=&quot;NormalTextRun SCX44968697&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; background-color: inherit; margin: 0px; padding: 0px; user-select: text;&quot;&gt;&lt;span class=&quot;TextRun SCX106645496&quot; lang=&quot;EN-US&quot; style=&quot;font-family: &amp;quot;calibri&amp;quot; , sans-serif; font-size: 11pt; line-height: 19px; margin: 0px; padding: 0px;&quot; xml:lang=&quot;EN-US&quot;&gt;PHP is not built for being a long-running program. &amp;nbsp;It was never designed for this and it should never be used for this. &amp;nbsp;It was built to handle a request for a page and then terminate.&lt;/span&gt;&lt;span class=&quot;EOP SCX106645496&quot; style=&quot;font-family: &amp;quot;calibri&amp;quot; , sans-serif; font-size: 11pt; line-height: 19px; margin: 0px; padding: 0px;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div class=&quot;Paragraph SCX106645496&quot; lang=&quot;EN-US&quot; paraeid=&quot;{3a6a8fb6-c855-46ad-bf77-ee1e04db3770}{69}&quot; paraid=&quot;1940148154&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; background-color: transparent; color: windowtext; padding: 0px; user-select: text; vertical-align: baseline; word-wrap: break-word;&quot; xml:lang=&quot;EN-US&quot;&gt;
&lt;span class=&quot;TextRun SCX9252380&quot; lang=&quot;EN-US&quot; style=&quot;background-color: white; font-family: &amp;quot;calibri&amp;quot; , sans-serif; font-size: 11pt; line-height: 19px; margin: 0px; padding: 0px;&quot; xml:lang=&quot;EN-US&quot;&gt;&lt;span class=&quot;NormalTextRun SCX9252380&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; background-color: inherit; margin: 0px; padding: 0px; user-select: text;&quot;&gt;&lt;span class=&quot;TextRun SCX224025393&quot; lang=&quot;EN-US&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; font-size: 11pt; line-height: 19px; margin: 0px; padding: 0px; user-select: text;&quot; xml:lang=&quot;EN-US&quot;&gt;&lt;span class=&quot;NormalTextRun SCX224025393&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; background-color: inherit; margin: 0px; padding: 0px; user-select: text;&quot;&gt;&lt;span class=&quot;TextRun SCX96217580&quot; lang=&quot;EN-US&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; font-size: 11pt; line-height: 19px; margin: 0px; padding: 0px; user-select: text;&quot; xml:lang=&quot;EN-US&quot;&gt;&lt;span class=&quot;NormalTextRun SCX96217580&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; background-color: inherit; margin: 0px; padding: 0px; user-select: text;&quot;&gt;&lt;span class=&quot;TextRun SCX212034074&quot; lang=&quot;EN-US&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; font-size: 11pt; line-height: 19px; margin: 0px; padding: 0px; user-select: text;&quot; xml:lang=&quot;EN-US&quot;&gt;&lt;span class=&quot;NormalTextRun SCX212034074&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; background-color: inherit; margin: 0px; padding: 0px; user-select: text;&quot;&gt;&lt;span class=&quot;TextRun SCX190447168&quot; lang=&quot;EN-US&quot; style=&quot;-webkit-app-region: no-drag; -webkit-appearance: none; -webkit-background-clip: border-box; -webkit-background-origin: padding-box; -webkit-border-image: none; -webkit-box-align: stretch; -webkit-box-decoration-break: slice; -webkit-box-direction: normal; -webkit-box-flex-group: 1; -webkit-box-flex: 0; -webkit-box-lines: single; -webkit-box-ordinal-group: 1; -webkit-box-orient: horizontal; -webkit-box-pack: start; -webkit-font-smoothing: auto; -webkit-highlight: none; -webkit-hyphenate-character: auto; -webkit-line-break: after-white-space; -webkit-locale: &amp;quot;en-US&amp;quot;; -webkit-margin-collapse: collapse collapse; -webkit-mask-box-image-outset: 0px; -webkit-mask-box-image-repeat: stretch; -webkit-mask-box-image-slice: 0 fill; -webkit-mask-box-image-source: none; -webkit-mask-box-image-width: auto; -webkit-mask-composite: source-over; -webkit-mask: none 0% 0% / auto repeat border-box border-box; -webkit-print-color-adjust: economy; -webkit-rtl-ordering: logical; -webkit-tap-highlight-color: rgba(0, 0, 0, 0); -webkit-text-combine: none; -webkit-text-emphasis-position: over; -webkit-text-emphasis: none rgb(0, 0, 0); -webkit-text-fill-color: rgb(0, 0, 0); -webkit-text-orientation: vertical-right; -webkit-text-security: none; -webkit-text-stroke-color: rgb(0, 0, 0); -webkit-user-drag: none; -webkit-user-modify: read-write; -webkit-writing-mode: horizontal-tb; align-content: stretch; align-items: stretch; align-self: stretch; alignment-baseline: auto; animation: none 0s ease 0s 1 normal none running; backface-visibility: visible; background-blend-mode: normal; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0); baseline-shift: 0px; border-collapse: separate; border-radius: 0px; border-spacing: 0px; border: 0px none rgb(0, 0, 0); bottom: auto; box-shadow: none; box-sizing: content-box; break-after: auto; break-before: auto; break-inside: auto; buffered-rendering: auto; caption-side: top; clear: none; clip-path: none; clip-rule: nonzero; clip: auto; color-interpolation-filters: linearRGB; color-interpolation: sRGB; color-rendering: auto; column-gap: normal; column-rule: 0px none rgb(0, 0, 0); column-span: none; columns: auto auto; cursor: text; cx: 0px; cy: 0px; d: none; direction: ltr; display: inline; dominant-baseline: auto; empty-cells: show; fill-opacity: 1; fill-rule: nonzero; fill: rgb(0, 0, 0); filter: none; flex-flow: row nowrap; flex: 0 1 auto; float: none; flood-color: rgb(0, 0, 0); flood-opacity: 1; font-kerning: auto; font-size: 14.6667px; font-stretch: normal; font-variant-numeric: normal; height: auto; hyphens: manual; image-rendering: auto; isolation: auto; justify-content: flex-start; left: auto; lighting-color: rgb(255, 255, 255); line-height: 19px; list-style: disc outside none; margin: 0px; marker: none; mask-type: luminance; mask: none; max-height: none; max-width: none; min-height: 0px; min-width: 0px; mix-blend-mode: normal; motion: none 0px auto 0deg; object-fit: fill; object-position: 50% 50%; offset-rotate: auto 0deg; opacity: 1; order: 0; outline-offset: 0px; outline: rgb(0, 0, 0) none 0px; overflow-anchor: auto; overflow-wrap: break-word; overflow: visible; padding: 0px; paint-order: fill; perspective-origin: 0px 0px; perspective: none; pointer-events: auto; position: static; r: 0px; resize: none; right: auto; rx: auto; ry: auto; shape-image-threshold: 0; shape-margin: 0px; shape-outside: none; shape-rendering: auto; speak: normal; stop-color: rgb(0, 0, 0); stop-opacity: 1; stroke-dasharray: none; stroke-dashoffset: 0px; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 4; stroke-opacity: 1; stroke-width: 1px; stroke: none; tab-size: 8; table-layout: auto; text-align-last: auto; text-anchor: start; text-overflow: clip; text-rendering: auto; text-shadow: none; text-size-adjust: auto; top: auto; touch-action: auto; transform-origin: 0px 0px 0px; transform-style: flat; transform: none; transition: all 0s ease 0s; unicode-bidi: normal; user-select: text; vector-effect: none; vertical-align: baseline; visibility: visible; width: auto; will-change: auto; word-break: normal; word-wrap: break-word; writing-mode: horizontal-tb; x: 0px; y: 0px; z-index: auto; zoom: 1;&quot; xml:lang=&quot;EN-US&quot;&gt;&lt;span class=&quot;NormalTextRun SCX190447168&quot; style=&quot;background-color: inherit;&quot;&gt;&lt;span class=&quot;TextRun SCX44968697&quot; lang=&quot;EN-US&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; font-size: 11pt; line-height: 19px; margin: 0px; padding: 0px; user-select: text;&quot; xml:lang=&quot;EN-US&quot;&gt;&lt;span class=&quot;NormalTextRun SCX44968697&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; background-color: inherit; margin: 0px; padding: 0px; user-select: text;&quot;&gt;&lt;span class=&quot;EOP SCX106645496&quot; style=&quot;font-family: &amp;quot;calibri&amp;quot; , sans-serif; font-size: 11pt; line-height: 19px; margin: 0px; padding: 0px;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;span class=&quot;TextRun SCX9252380&quot; lang=&quot;EN-US&quot; style=&quot;background-color: white; font-family: &amp;quot;calibri&amp;quot; , sans-serif; font-size: 11pt; line-height: 19px; margin: 0px; padding: 0px;&quot; xml:lang=&quot;EN-US&quot;&gt;&lt;span class=&quot;NormalTextRun SCX9252380&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; background-color: inherit; margin: 0px; padding: 0px; user-select: text;&quot;&gt;&lt;span class=&quot;TextRun SCX224025393&quot; lang=&quot;EN-US&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; font-size: 11pt; line-height: 19px; margin: 0px; padding: 0px; user-select: text;&quot; xml:lang=&quot;EN-US&quot;&gt;&lt;span class=&quot;NormalTextRun SCX224025393&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; background-color: inherit; margin: 0px; padding: 0px; user-select: text;&quot;&gt;&lt;span class=&quot;TextRun SCX96217580&quot; lang=&quot;EN-US&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; font-size: 11pt; line-height: 19px; margin: 0px; padding: 0px; user-select: text;&quot; xml:lang=&quot;EN-US&quot;&gt;&lt;span class=&quot;NormalTextRun SCX96217580&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; background-color: inherit; margin: 0px; padding: 0px; user-select: text;&quot;&gt;&lt;span class=&quot;TextRun SCX212034074&quot; lang=&quot;EN-US&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; font-size: 11pt; line-height: 19px; margin: 0px; padding: 0px; user-select: text;&quot; xml:lang=&quot;EN-US&quot;&gt;&lt;span class=&quot;NormalTextRun SCX212034074&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; background-color: inherit; margin: 0px; padding: 0px; user-select: text;&quot;&gt;&lt;span class=&quot;TextRun SCX190447168&quot; lang=&quot;EN-US&quot; style=&quot;-webkit-app-region: no-drag; -webkit-appearance: none; -webkit-background-clip: border-box; -webkit-background-origin: padding-box; -webkit-border-image: none; -webkit-box-align: stretch; -webkit-box-decoration-break: slice; -webkit-box-direction: normal; -webkit-box-flex-group: 1; -webkit-box-flex: 0; -webkit-box-lines: single; -webkit-box-ordinal-group: 1; -webkit-box-orient: horizontal; -webkit-box-pack: start; -webkit-font-smoothing: auto; -webkit-highlight: none; -webkit-hyphenate-character: auto; -webkit-line-break: after-white-space; -webkit-locale: &amp;quot;en-US&amp;quot;; -webkit-margin-collapse: collapse collapse; -webkit-mask-box-image-outset: 0px; -webkit-mask-box-image-repeat: stretch; -webkit-mask-box-image-slice: 0 fill; -webkit-mask-box-image-source: none; -webkit-mask-box-image-width: auto; -webkit-mask-composite: source-over; -webkit-mask: none 0% 0% / auto repeat border-box border-box; -webkit-print-color-adjust: economy; -webkit-rtl-ordering: logical; -webkit-tap-highlight-color: rgba(0, 0, 0, 0); -webkit-text-combine: none; -webkit-text-emphasis-position: over; -webkit-text-emphasis: none rgb(0, 0, 0); -webkit-text-fill-color: rgb(0, 0, 0); -webkit-text-orientation: vertical-right; -webkit-text-security: none; -webkit-text-stroke-color: rgb(0, 0, 0); -webkit-user-drag: none; -webkit-user-modify: read-write; -webkit-writing-mode: horizontal-tb; align-content: stretch; align-items: stretch; align-self: stretch; alignment-baseline: auto; animation: none 0s ease 0s 1 normal none running; backface-visibility: visible; background-blend-mode: normal; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0); baseline-shift: 0px; border-collapse: separate; border-radius: 0px; border-spacing: 0px; border: 0px none rgb(0, 0, 0); bottom: auto; box-shadow: none; box-sizing: content-box; break-after: auto; break-before: auto; break-inside: auto; buffered-rendering: auto; caption-side: top; clear: none; clip-path: none; clip-rule: nonzero; clip: auto; color-interpolation-filters: linearRGB; color-interpolation: sRGB; color-rendering: auto; column-gap: normal; column-rule: 0px none rgb(0, 0, 0); column-span: none; columns: auto auto; cursor: text; cx: 0px; cy: 0px; d: none; direction: ltr; display: inline; dominant-baseline: auto; empty-cells: show; fill-opacity: 1; fill-rule: nonzero; fill: rgb(0, 0, 0); filter: none; flex-flow: row nowrap; flex: 0 1 auto; float: none; flood-color: rgb(0, 0, 0); flood-opacity: 1; font-kerning: auto; font-size: 14.6667px; font-stretch: normal; font-variant-numeric: normal; height: auto; hyphens: manual; image-rendering: auto; isolation: auto; justify-content: flex-start; left: auto; lighting-color: rgb(255, 255, 255); line-height: 19px; list-style: disc outside none; margin: 0px; marker: none; mask-type: luminance; mask: none; max-height: none; max-width: none; min-height: 0px; min-width: 0px; mix-blend-mode: normal; motion: none 0px auto 0deg; object-fit: fill; object-position: 50% 50%; offset-rotate: auto 0deg; opacity: 1; order: 0; outline-offset: 0px; outline: rgb(0, 0, 0) none 0px; overflow-anchor: auto; overflow-wrap: break-word; overflow: visible; padding: 0px; paint-order: fill; perspective-origin: 0px 0px; perspective: none; pointer-events: auto; position: static; r: 0px; resize: none; right: auto; rx: auto; ry: auto; shape-image-threshold: 0; shape-margin: 0px; shape-outside: none; shape-rendering: auto; speak: normal; stop-color: rgb(0, 0, 0); stop-opacity: 1; stroke-dasharray: none; stroke-dashoffset: 0px; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 4; stroke-opacity: 1; stroke-width: 1px; stroke: none; tab-size: 8; table-layout: auto; text-align-last: auto; text-anchor: start; text-overflow: clip; text-rendering: auto; text-shadow: none; text-size-adjust: auto; top: auto; touch-action: auto; transform-origin: 0px 0px 0px; transform-style: flat; transform: none; transition: all 0s ease 0s; unicode-bidi: normal; user-select: text; vector-effect: none; vertical-align: baseline; visibility: visible; width: auto; will-change: auto; word-break: normal; word-wrap: break-word; writing-mode: horizontal-tb; x: 0px; y: 0px; z-index: auto; zoom: 1;&quot; xml:lang=&quot;EN-US&quot;&gt;&lt;span class=&quot;NormalTextRun SCX190447168&quot; style=&quot;background-color: inherit;&quot;&gt;&lt;span class=&quot;TextRun SCX44968697&quot; lang=&quot;EN-US&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; font-size: 11pt; line-height: 19px; margin: 0px; padding: 0px; user-select: text;&quot; xml:lang=&quot;EN-US&quot;&gt;&lt;span class=&quot;NormalTextRun SCX44968697&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; background-color: inherit; margin: 0px; padding: 0px; user-select: text;&quot;&gt;
&lt;div class=&quot;OutlineElement Ltr SCX106645496&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; clear: both; cursor: text; direction: ltr; font-family: &amp;quot;Segoe UI&amp;quot;, Tahoma, Verdana, sans-serif; font-size: 8px; margin: 0px; overflow: visible; padding: 0px; position: relative; user-select: text;&quot;&gt;
&lt;div class=&quot;Paragraph SCX106645496&quot; lang=&quot;EN-US&quot; paraeid=&quot;{e4d6cafb-1a50-4b94-b949-feff86dad606}{169}&quot; paraid=&quot;548314187&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; background-color: transparent; color: windowtext; font-variant-ligatures: normal; padding: 0px; user-select: text; vertical-align: baseline; word-wrap: break-word;&quot; xml:lang=&quot;EN-US&quot;&gt;
&lt;span class=&quot;TextRun SCX106645496&quot; lang=&quot;EN-US&quot; style=&quot;font-family: &amp;quot;calibri&amp;quot; , sans-serif; font-size: 11pt; line-height: 19px; margin: 0px; padding: 0px;&quot; xml:lang=&quot;EN-US&quot;&gt;The application is bootstrapped for every request. &amp;nbsp;&lt;/span&gt;&lt;span class=&quot;TextRun SCX106645496&quot; lang=&quot;EN-US&quot; style=&quot;font-family: &amp;quot;calibri&amp;quot; , sans-serif; font-size: 11pt; line-height: 19px; margin: 0px; padding: 0px;&quot; xml:lang=&quot;EN-US&quot;&gt;How much overhead does this add? &amp;nbsp;Well there&#39;s a question that&amp;nbsp;&lt;/span&gt;&lt;span class=&quot;TextRun SCX106645496&quot; lang=&quot;EN-US&quot; style=&quot;font-family: &amp;quot;calibri&amp;quot; , sans-serif; font-size: 11pt; line-height: 19px; margin: 0px; padding: 0px;&quot; xml:lang=&quot;EN-US&quot;&gt;&lt;span class=&quot;SpellingError SCX106645496&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; background-color: inherit; background-image: url(&amp;quot;data:image/gif; background-position: left bottom; background-repeat: repeat-x; border-bottom: 1px solid transparent; margin: 0px; padding: 0px; user-select: text;&quot;&gt;Blackfire&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;TextRun SCX106645496&quot; lang=&quot;EN-US&quot; style=&quot;font-family: &amp;quot;calibri&amp;quot; , sans-serif; font-size: 11pt; line-height: 19px; margin: 0px; padding: 0px;&quot; xml:lang=&quot;EN-US&quot;&gt;&amp;nbsp;is raising for me. &amp;nbsp;Take a look at the timeline for&amp;nbsp;&lt;/span&gt;&lt;span class=&quot;TextRun SCX106645496&quot; lang=&quot;EN-US&quot; style=&quot;font-family: &amp;quot;calibri&amp;quot; , sans-serif; font-size: 11pt; line-height: 19px; margin: 0px; padding: 0px;&quot; xml:lang=&quot;EN-US&quot;&gt;the&lt;/span&gt;&lt;span class=&quot;TextRun SCX106645496&quot; lang=&quot;EN-US&quot; style=&quot;font-family: &amp;quot;calibri&amp;quot; , sans-serif; font-size: 11pt; line-height: 19px; margin: 0px; padding: 0px;&quot; xml:lang=&quot;EN-US&quot;&gt;&amp;nbsp;transaction from before:&lt;/span&gt;&lt;/div&gt;
&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;font-variant-ligatures: normal; margin-left: auto; margin-right: auto; text-align: center;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh7Vq6LLKBkT93wa_X7TGv-ycQZ5VyHpqvMSNWOJAQms6zaqySokDQO2y2HVrrpCQEUUEuimgOkVokU33sz48lrSr__VyVmbP4hB7zJN7HBk74dMWk_JuBS9VzEJR0NhjLqMZrd1SlnONRp/s1600/timeline.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;212&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh7Vq6LLKBkT93wa_X7TGv-ycQZ5VyHpqvMSNWOJAQms6zaqySokDQO2y2HVrrpCQEUUEuimgOkVokU33sz48lrSr__VyVmbP4hB7zJN7HBk74dMWk_JuBS9VzEJR0NhjLqMZrd1SlnONRp/s400/timeline.png&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;Blackfire call timeline&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;div class=&quot;OutlineElement Ltr SCX240501246&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; clear: both; cursor: text; direction: ltr; font-variant-ligatures: normal; margin: 0px; overflow: visible; padding: 0px; position: relative; user-select: text;&quot;&gt;
&lt;div class=&quot;Paragraph SCX240501246&quot; lang=&quot;EN-US&quot; paraeid=&quot;{f8d4304a-0c1d-4f28-a381-222675f28b40}{26}&quot; paraid=&quot;1205383312&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; background-color: transparent; color: windowtext; padding: 0px; user-select: text; vertical-align: baseline; word-wrap: break-word;&quot; xml:lang=&quot;EN-US&quot;&gt;
&lt;span class=&quot;TextRun SCX240501246&quot; lang=&quot;EN-US&quot; style=&quot;font-family: &amp;quot;calibri&amp;quot; , sans-serif; font-size: 11pt; line-height: 19px; margin: 0px; padding: 0px;&quot; xml:lang=&quot;EN-US&quot;&gt;The timeline shows when a PHP function was called in relation to the time taken to generate the response.&lt;/span&gt;&lt;span class=&quot;EOP SCX240501246&quot; style=&quot;font-family: &amp;quot;calibri&amp;quot; , sans-serif; font-size: 11pt; line-height: 19px; margin: 0px; padding: 0px;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/div&gt;
&lt;div class=&quot;Paragraph SCX240501246&quot; lang=&quot;EN-US&quot; paraeid=&quot;{f8d4304a-0c1d-4f28-a381-222675f28b40}{26}&quot; paraid=&quot;1205383312&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; background-color: transparent; color: windowtext; padding: 0px; user-select: text; vertical-align: baseline; word-wrap: break-word;&quot; xml:lang=&quot;EN-US&quot;&gt;
&lt;span class=&quot;EOP SCX240501246&quot; style=&quot;font-family: &amp;quot;calibri&amp;quot; , sans-serif; font-size: 11pt; line-height: 19px; margin: 0px; padding: 0px;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;OutlineElement Ltr SCX240501246&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; clear: both; cursor: text; direction: ltr; font-variant-ligatures: normal; margin: 0px; overflow: visible; padding: 0px; position: relative; user-select: text;&quot;&gt;
&lt;div class=&quot;Paragraph SCX240501246&quot; lang=&quot;EN-US&quot; paraeid=&quot;{baa59781-cab4-4302-b3bf-8fca00e70254}{124}&quot; paraid=&quot;1788414390&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; background-color: transparent; color: windowtext; padding: 0px; user-select: text; vertical-align: baseline; word-wrap: break-word;&quot; xml:lang=&quot;EN-US&quot;&gt;
&lt;span class=&quot;TextRun SCX240501246&quot; lang=&quot;EN-US&quot; style=&quot;font-family: &amp;quot;calibri&amp;quot; , sans-serif; font-size: 11pt; line-height: 19px; margin: 0px; padding: 0px;&quot; xml:lang=&quot;EN-US&quot;&gt;My controller function starts at around 750ms into the transaction. &amp;nbsp;The actual time is irrelevant as a benchmark, but the fact that the first time *&lt;/span&gt;&lt;span class=&quot;TextRun SCX240501246&quot; lang=&quot;EN-US&quot; style=&quot;font-family: &amp;quot;calibri&amp;quot; , sans-serif; font-size: 11pt; font-weight: bold; line-height: 19px; margin: 0px; padding: 0px;&quot; xml:lang=&quot;EN-US&quot;&gt;my&lt;/span&gt;&lt;span class=&quot;TextRun SCX240501246&quot; lang=&quot;EN-US&quot; style=&quot;font-family: &amp;quot;calibri&amp;quot; , sans-serif; font-size: 11pt; line-height: 19px; margin: 0px; padding: 0px;&quot; xml:lang=&quot;EN-US&quot;&gt;* code runs is half-way into the transaction is what is relevant. &amp;nbsp;&lt;/span&gt;&lt;span class=&quot;EOP SCX240501246&quot; style=&quot;font-family: &amp;quot;calibri&amp;quot; , sans-serif; font-size: 11pt; line-height: 19px; margin: 0px; padding: 0px;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/div&gt;
&lt;div class=&quot;Paragraph SCX240501246&quot; lang=&quot;EN-US&quot; paraeid=&quot;{baa59781-cab4-4302-b3bf-8fca00e70254}{124}&quot; paraid=&quot;1788414390&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; background-color: transparent; color: windowtext; padding: 0px; user-select: text; vertical-align: baseline; word-wrap: break-word;&quot; xml:lang=&quot;EN-US&quot;&gt;
&lt;span class=&quot;EOP SCX240501246&quot; style=&quot;font-family: &amp;quot;calibri&amp;quot; , sans-serif; font-size: 11pt; line-height: 19px; margin: 0px; padding: 0px;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;OutlineElement Ltr SCX240501246&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; clear: both; cursor: text; direction: ltr; font-variant-ligatures: normal; margin: 0px; overflow: visible; padding: 0px; position: relative; user-select: text;&quot;&gt;
&lt;div class=&quot;Paragraph SCX240501246&quot; lang=&quot;EN-US&quot; paraeid=&quot;{1c494fcb-784e-4b98-b8db-39c33986df9a}{79}&quot; paraid=&quot;1851285450&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; background-color: transparent; color: windowtext; padding: 0px; user-select: text; vertical-align: baseline; word-wrap: break-word;&quot; xml:lang=&quot;EN-US&quot;&gt;
&lt;span class=&quot;TextRun SCX240501246&quot; lang=&quot;EN-US&quot; style=&quot;font-family: &amp;quot;calibri&amp;quot; , sans-serif; font-size: 11pt; line-height: 19px; margin: 0px; padding: 0px;&quot; xml:lang=&quot;EN-US&quot;&gt;Until halfway into my application&amp;nbsp;&lt;/span&gt;&lt;span class=&quot;TextRun SCX240501246&quot; lang=&quot;EN-US&quot; style=&quot;font-family: &amp;quot;calibri&amp;quot; , sans-serif; font-size: 11pt; line-height: 19px; margin: 0px; padding: 0px;&quot; xml:lang=&quot;EN-US&quot;&gt;I&#39;ve been waiting for PHP to bootstrap my application. &amp;nbsp;You could argue that this is because of the PHP framework I&#39;m using, but actually it is the limitation of PHP not being able to maintain state that requires us to continuously bootstrap the application.&lt;/span&gt;&lt;/div&gt;
&lt;div class=&quot;Paragraph SCX240501246&quot; lang=&quot;EN-US&quot; paraeid=&quot;{1c494fcb-784e-4b98-b8db-39c33986df9a}{79}&quot; paraid=&quot;1851285450&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; background-color: transparent; color: windowtext; padding: 0px; user-select: text; vertical-align: baseline; word-wrap: break-word;&quot; xml:lang=&quot;EN-US&quot;&gt;
&lt;span class=&quot;EOP SCX240501246&quot; style=&quot;font-family: &amp;quot;calibri&amp;quot; , sans-serif; font-size: 11pt; line-height: 19px; margin: 0px; padding: 0px;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;OutlineElement Ltr SCX240501246&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; clear: both; cursor: text; direction: ltr; margin: 0px; overflow: visible; padding: 0px; position: relative; user-select: text;&quot;&gt;
&lt;div class=&quot;Paragraph SCX240501246&quot; lang=&quot;EN-US&quot; paraeid=&quot;{27bd6110-0830-4a8c-90ca-5ee96e37c575}{161}&quot; paraid=&quot;480231366&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; background-color: transparent; color: windowtext; padding: 0px; user-select: text; vertical-align: baseline; word-wrap: break-word;&quot; xml:lang=&quot;EN-US&quot;&gt;
&lt;span class=&quot;TextRun SCX240501246&quot; lang=&quot;EN-US&quot; style=&quot;font-family: &amp;quot;calibri&amp;quot; , sans-serif; font-size: 11pt; line-height: 19px; margin: 0px; padding: 0px;&quot; xml:lang=&quot;EN-US&quot;&gt;Bootstrapping our application might involve&lt;/span&gt;&lt;span class=&quot;TextRun SCX240501246&quot; lang=&quot;EN-US&quot; style=&quot;font-family: &amp;quot;calibri&amp;quot; , sans-serif; font-size: 11pt; line-height: 19px; margin: 0px; padding: 0px;&quot; xml:lang=&quot;EN-US&quot;&gt;&amp;nbsp;disk I/O (depending on OpCache). &amp;nbsp;&lt;/span&gt;&lt;span class=&quot;TextRun SCX240501246&quot; lang=&quot;EN-US&quot; style=&quot;font-family: &amp;quot;calibri&amp;quot; , sans-serif; font-size: 11pt; line-height: 19px; margin: 0px; padding: 0px;&quot; xml:lang=&quot;EN-US&quot;&gt;It definitely involves network I/O because we have to connect to MySQL and wait for it to authenticate us.&lt;/span&gt;&lt;span class=&quot;TextRun SCX240501246&quot; lang=&quot;EN-US&quot; style=&quot;font-family: &amp;quot;calibri&amp;quot; , sans-serif; font-size: 11pt; line-height: 19px; margin: 0px; padding: 0px;&quot; xml:lang=&quot;EN-US&quot;&gt;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;span class=&quot;TextRun SCX240501246&quot; lang=&quot;EN-US&quot; style=&quot;background-color: transparent; font-family: &amp;quot;calibri&amp;quot; , sans-serif; font-size: 11pt; line-height: 19px; margin: 0px; padding: 0px;&quot; xml:lang=&quot;EN-US&quot;&gt;I know that there are ways to improve this, like by not using a framework an&lt;/span&gt;&lt;span class=&quot;TextRun SCX240501246&quot; lang=&quot;EN-US&quot; style=&quot;background-color: transparent; font-family: &amp;quot;calibri&amp;quot; , sans-serif; font-size: 11pt; line-height: 19px; margin: 0px; padding: 0px;&quot; xml:lang=&quot;EN-US&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;TextRun SCX240501246&quot; lang=&quot;EN-US&quot; style=&quot;background-color: transparent; font-family: &amp;quot;calibri&amp;quot; , sans-serif; font-size: 11pt; line-height: 19px; margin: 0px; padding: 0px;&quot; xml:lang=&quot;EN-US&quot;&gt;&amp;nbsp;by tuning&amp;nbsp;&lt;/span&gt;&lt;span class=&quot;TextRun SCX240501246&quot; lang=&quot;EN-US&quot; style=&quot;background-color: transparent; font-family: &amp;quot;calibri&amp;quot; , sans-serif; font-size: 11pt; line-height: 19px; margin: 0px; padding: 0px;&quot; xml:lang=&quot;EN-US&quot;&gt;&lt;span class=&quot;SpellingError SCX240501246&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; background-color: inherit; background-image: url(&amp;quot;data:image/gif; background-position: left bottom; background-repeat: repeat-x; border-bottom: 1px solid transparent; margin: 0px; padding: 0px; user-select: text;&quot;&gt;OpCache&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;TextRun SCX240501246&quot; lang=&quot;EN-US&quot; style=&quot;background-color: transparent; font-family: &amp;quot;calibri&amp;quot; , sans-serif; font-size: 11pt; line-height: 19px; margin: 0px; padding: 0px;&quot; xml:lang=&quot;EN-US&quot;&gt;&amp;nbsp;to improve compile time.&lt;/span&gt;&lt;/div&gt;
&lt;div class=&quot;Paragraph SCX240501246&quot; lang=&quot;EN-US&quot; paraeid=&quot;{27bd6110-0830-4a8c-90ca-5ee96e37c575}{161}&quot; paraid=&quot;480231366&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; background-color: transparent; color: windowtext; padding: 0px; user-select: text; vertical-align: baseline; word-wrap: break-word;&quot; xml:lang=&quot;EN-US&quot;&gt;
&lt;span class=&quot;TextRun SCX240501246&quot; lang=&quot;EN-US&quot; style=&quot;background-color: transparent; font-family: &amp;quot;calibri&amp;quot; , sans-serif; font-size: 11pt; line-height: 19px; margin: 0px; padding: 0px;&quot; xml:lang=&quot;EN-US&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div class=&quot;Paragraph SCX240501246&quot; lang=&quot;EN-US&quot; paraeid=&quot;{27bd6110-0830-4a8c-90ca-5ee96e37c575}{161}&quot; paraid=&quot;480231366&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; background-color: transparent; color: windowtext; padding: 0px; user-select: text; vertical-align: baseline; word-wrap: break-word;&quot; xml:lang=&quot;EN-US&quot;&gt;
&lt;span class=&quot;TextRun SCX240501246&quot; lang=&quot;EN-US&quot; style=&quot;background-color: transparent; font-family: &amp;quot;calibri&amp;quot; , sans-serif; font-size: 11pt; line-height: 19px; margin: 0px; padding: 0px;&quot; xml:lang=&quot;EN-US&quot;&gt;I&#39;m concerned about what will happen when the application has 50,000 concurrent users. &amp;nbsp;How much of a strain will it place on my database server to be constantly connecting (and authenticating)?&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;Paragraph SCX106645496&quot; lang=&quot;EN-US&quot; paraeid=&quot;{e4d6cafb-1a50-4b94-b949-feff86dad606}{169}&quot; paraid=&quot;548314187&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; background-color: transparent; color: windowtext; font-variant-ligatures: normal; padding: 0px; user-select: text; vertical-align: baseline; word-wrap: break-word;&quot; xml:lang=&quot;EN-US&quot;&gt;
&lt;span class=&quot;TextRun SCX106645496&quot; lang=&quot;EN-US&quot; style=&quot;font-family: &amp;quot;calibri&amp;quot; , sans-serif; font-size: 11pt; line-height: 19px; margin: 0px; padding: 0px;&quot; xml:lang=&quot;EN-US&quot;&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div class=&quot;OutlineElement Ltr SCX240501246&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; clear: both; cursor: text; direction: ltr; font-variant-ligatures: normal; margin: 0px; overflow: visible; padding: 0px; position: relative; user-select: text;&quot;&gt;
&lt;div class=&quot;Paragraph SCX240501246&quot; lang=&quot;EN-US&quot; paraeid=&quot;{54b1d195-ed67-42b6-9f51-efb0c3473b53}{57}&quot; paraid=&quot;716632979&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; background-color: transparent; color: windowtext; padding: 0px; user-select: text; vertical-align: baseline; word-wrap: break-word;&quot; xml:lang=&quot;EN-US&quot;&gt;
&lt;span class=&quot;TextRun SCX240501246&quot; lang=&quot;EN-US&quot; style=&quot;font-family: &amp;quot;calibri&amp;quot; , sans-serif; font-size: 11pt; line-height: 19px; margin: 0px; padding: 0px;&quot; xml:lang=&quot;EN-US&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div class=&quot;Paragraph SCX240501246&quot; lang=&quot;EN-US&quot; paraeid=&quot;{54b1d195-ed67-42b6-9f51-efb0c3473b53}{57}&quot; paraid=&quot;716632979&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; background-color: transparent; color: windowtext; padding: 0px; user-select: text; vertical-align: baseline; word-wrap: break-word;&quot; xml:lang=&quot;EN-US&quot;&gt;
&lt;span class=&quot;TextRun SCX240501246&quot; lang=&quot;EN-US&quot; style=&quot;font-family: &amp;quot;calibri&amp;quot; , sans-serif; font-size: 11pt; line-height: 19px; margin: 0px; padding: 0px;&quot; xml:lang=&quot;EN-US&quot;&gt;I think PHP is brilliant at web pages and not so good at being a long-running application that is capable of reusing resources. &amp;nbsp;I&#39;m a huge PHP fan but as an architect I do not want it to be my only tool. &amp;nbsp;&lt;/span&gt;&lt;/div&gt;
&lt;div class=&quot;Paragraph SCX240501246&quot; lang=&quot;EN-US&quot; paraeid=&quot;{54b1d195-ed67-42b6-9f51-efb0c3473b53}{57}&quot; paraid=&quot;716632979&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; background-color: transparent; color: windowtext; padding: 0px; user-select: text; vertical-align: baseline; word-wrap: break-word;&quot; xml:lang=&quot;EN-US&quot;&gt;
&lt;span class=&quot;TextRun SCX240501246&quot; lang=&quot;EN-US&quot; style=&quot;font-family: &amp;quot;calibri&amp;quot; , sans-serif; font-size: 11pt; line-height: 19px; margin: 0px; padding: 0px;&quot; xml:lang=&quot;EN-US&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div class=&quot;Paragraph SCX240501246&quot; lang=&quot;EN-US&quot; paraeid=&quot;{54b1d195-ed67-42b6-9f51-efb0c3473b53}{57}&quot; paraid=&quot;716632979&quot; style=&quot;-webkit-tap-highlight-color: transparent; -webkit-user-drag: none; background-color: transparent; color: windowtext; padding: 0px; user-select: text; vertical-align: baseline; word-wrap: break-word;&quot; xml:lang=&quot;EN-US&quot;&gt;
&lt;span class=&quot;TextRun SCX240501246&quot; lang=&quot;EN-US&quot; style=&quot;font-family: &amp;quot;calibri&amp;quot; , sans-serif; font-size: 11pt; line-height: 19px; margin: 0px; padding: 0px;&quot; xml:lang=&quot;EN-US&quot;&gt;I&#39;m busy learning Elixir and the Phoenix framework (again with the frameworks!) response in microseconds (not milliseconds). &amp;nbsp;I don&#39;t think we should using PHP like the hammer we use for everything.&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;
</description><link>http://blog.mystery.co.za/2017/04/is-php-good-fit-for-api-server.html</link><author>noreply@blogger.com (Andrew Beak)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgpJ8lUKWu9nNc3_cYkAAIEj0CBcf3_rGUAntVeJStsK4bTGCRU2JHfohO_MXfV95ngM5i8rIDTI1TqeZ1kbOKBH19YZu5rxU8om4Yt19E-vy1B9uPWNZhzE_gmD-rUt7-Et4pf55gcqEo2/s72-c/honey-bees-326334_1920.jpg" height="72" width="72"/><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-1356169494544655761.post-685168721718150911</guid><pubDate>Tue, 21 Mar 2017 12:41:00 +0000</pubDate><atom:updated>2017-03-21T12:51:27.336+00:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">PHP</category><category domain="http://www.blogger.com/atom/ns#">programming</category><title>Why is it important to understand how PHP matches a catch block?  </title><description>&lt;table cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;float: right; text-align: right;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgusbR6GSXpitEEYs3Lry5yHJ1LkIaA2goyl1URa5y5kQUCMvLS_PqznkeGQcfTDGCVEkiex0kzpzwzQIR9R0kUxLVxxPvE28NAtwkylloTutIaVh6GwLT2QWOOOGtOhIbIiElKBfjjzz3P/s1600/bubble-83758_1280.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;212&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgusbR6GSXpitEEYs3Lry5yHJ1LkIaA2goyl1URa5y5kQUCMvLS_PqznkeGQcfTDGCVEkiex0kzpzwzQIR9R0kUxLVxxPvE28NAtwkylloTutIaVh6GwLT2QWOOOGtOhIbIiElKBfjjzz3P/s320/bubble-83758_1280.jpg&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;Image: &lt;a href=&quot;https://pixabay.com/en/bubble-fun-colors-game-flight-83758/&quot;&gt;Pixabay&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
PHP allows you to have multiple catch blocks. &amp;nbsp;The &lt;a href=&quot;http://php.net/manual/en/language.exceptions.php&quot; target=&quot;_blank&quot;&gt;manual&lt;/a&gt; says that when an exception occurs PHP will try to find the &lt;b&gt;first&lt;/b&gt; matching exception block, but it isn&#39;t entirely clear on exactly how &amp;nbsp;PHP matches &quot;catch&quot; blocks when handling exceptions.&lt;br /&gt;
&lt;br /&gt;
Why is it important to understand how PHP matches a catch block? &lt;br /&gt;
&lt;br /&gt;
It really relates to the fact that you are able to extend the base &quot;Exception&quot; class in PHP to make your own classes. &amp;nbsp;Throwing exceptions that are specific is good practice because it allows you to write catch blocks that are focused on solving just one sort of problem at a time. &amp;nbsp;The &lt;a href=&quot;https://secure.php.net/manual/en/spl.exceptions.php&quot; target=&quot;_blank&quot;&gt;SPL exceptions&lt;/a&gt; are a good example of hierarchical exception inheritance in case you&#39;re wanting to see some practical usage.&lt;br /&gt;
&lt;br /&gt;
In PHP a catch block is a match for an exception if the exception class is either the same as &lt;i&gt;or inherits from&lt;/i&gt; the class in the catch clause. &lt;br /&gt;
&lt;br /&gt;
Here&#39;s a trivial example:&lt;br /&gt;
&lt;br /&gt;
&lt;script src=&quot;https://gist.github.com/andybeak/30e25d4fc46f9b24d4e76d9516025720.js&quot;&gt;&lt;/script&gt;

In this example I&#39;ve set up an exception class that extends the base exception class. &amp;nbsp;I&#39;m throwing an instance of this child exception. &amp;nbsp;If I didn&#39;t know that PHP will match it&#39;s parent class I would expect the message &quot;Child exception&quot; to be output. &lt;br /&gt;
&lt;br /&gt;
In actual fact if you run this code (&lt;a href=&quot;https://3v4l.org/IvPO2&quot; rel=&quot;nofollow&quot; target=&quot;_blank&quot;&gt;here&lt;/a&gt;) you will see that the message &quot;Parent exception&quot; is output because PHP has matched MyException with its parent class.&lt;br /&gt;
&lt;br /&gt;
The PHP manual doesn&#39;t make this explicit or obvious when discussing catch blocks or exception inheritance but it&#39;s one of those things that&#39;s always handy to know. &lt;br /&gt;
&lt;br /&gt;
This implies that when we&#39;re coding multiple catch blocks we should place the more general parent classes at the bottom of the list. &amp;nbsp;This prevents them from catching exceptions that are meant for their children. &lt;br /&gt;
&lt;br /&gt;
I discuss this and other gotchas &amp;nbsp;in my&amp;nbsp;&lt;a href=&quot;https://leanpub.com/phpforprogrammers/&quot; target=&quot;_blank&quot;&gt;study guide for the Zend certification exam&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;</description><link>http://blog.mystery.co.za/2017/03/why-is-it-important-to-understand-how.html</link><author>noreply@blogger.com (Andrew Beak)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgusbR6GSXpitEEYs3Lry5yHJ1LkIaA2goyl1URa5y5kQUCMvLS_PqznkeGQcfTDGCVEkiex0kzpzwzQIR9R0kUxLVxxPvE28NAtwkylloTutIaVh6GwLT2QWOOOGtOhIbIiElKBfjjzz3P/s72-c/bubble-83758_1280.jpg" height="72" width="72"/><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-1356169494544655761.post-6158770127173264121</guid><pubDate>Mon, 23 Jan 2017 16:03:00 +0000</pubDate><atom:updated>2017-01-23T16:03:33.846+00:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">database</category><category domain="http://www.blogger.com/atom/ns#">Laravel</category><category domain="http://www.blogger.com/atom/ns#">pci</category><category domain="http://www.blogger.com/atom/ns#">PHP</category><category domain="http://www.blogger.com/atom/ns#">security</category><title>Complying with PCI database requirements in Laravel on AWS</title><description>&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;float: right; margin-left: 1em; text-align: right;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhv2-cq19oEMH5WWyFK_BrhFPg5ZJnV5RNYHjX8e1PUANzOB-UYd540VMU_PsQIdqtPF5puF1m35lBIvorm-cIQbuElEHKmXSd1CQ1td8Ph2tC6NlPnMyexXvcvBwOeEiBnRuT7nHl7nkm9/s1600/padlock-lock-chain-key-39624.jpeg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;213&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhv2-cq19oEMH5WWyFK_BrhFPg5ZJnV5RNYHjX8e1PUANzOB-UYd540VMU_PsQIdqtPF5puF1m35lBIvorm-cIQbuElEHKmXSd1CQ1td8Ph2tC6NlPnMyexXvcvBwOeEiBnRuT7nHl7nkm9/s320/padlock-lock-chain-key-39624.jpeg&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;Image: &lt;a href=&quot;https://www.pexels.com/photo/strong-lock-locked-padlock-39624/&quot;&gt;pexels.com&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
I&#39;m busy with the self assessment questionnaire for PCI compliance. &amp;nbsp;Part of the database requirements are that cardholder data are encrypted at rest as well as in transit.&lt;br /&gt;
&lt;br /&gt;
I host with Amazon RDS and use Laravel so my life is made pretty easy. &lt;br /&gt;
&lt;br /&gt;
Amazon RDS natively supports encrypted connections and also lets you create a database that is stored on an encrypted backing instance. &amp;nbsp;If you&#39;ve enabled this option then all that you need to do is make sure that you connect to the database using an encrypted connection.&lt;br /&gt;
&lt;br /&gt;
I&#39;m not getting paid anything for saying that I really enjoy using RDS, but today is another occasion when I&#39;m really happy that I didn&#39;t have to sit and install certificates and fiddle with a cluster configuration to enable SSL connections. &amp;nbsp;The &quot;zero config&quot; that comes with RDS saves time and money.&lt;br /&gt;
&lt;br /&gt;
Laravel was really easy to configure to use SSL. &amp;nbsp;All that you need to do is download the RDS certificate chain from&amp;nbsp;&lt;a href=&quot;https://s3.amazonaws.com/rds-downloads/rds-combined-ca-bundle.pem&quot;&gt;https://s3.amazonaws.com/rds-downloads/rds-combined-ca-bundle.pem&lt;/a&gt;&amp;nbsp;and amend your database configuration to use it when connecting.&lt;br /&gt;
&lt;br /&gt;
I saved the pem file into storage/app/mysql_ssl_certificate and then amended my app/database.php to look like this:&lt;br /&gt;
&lt;br /&gt;
&lt;script src=&quot;https://gist.github.com/andybeak/47e89385c305b21966b22d193d132500.js&quot;&gt;&lt;/script&gt;

Now I can use a command like&amp;nbsp;&lt;span class=&quot;nb&quot; style=&quot;color: #999999; font-family: &amp;quot;consolas&amp;quot; , &amp;quot;menlo&amp;quot; , &amp;quot;liberation mono&amp;quot; , &amp;quot;courier&amp;quot; , monospace; font-size: 12px;&quot;&gt;var_dump&lt;/span&gt;&lt;span class=&quot;p&quot; style=&quot;color: #333333; font-family: &amp;quot;consolas&amp;quot; , &amp;quot;menlo&amp;quot; , &amp;quot;liberation mono&amp;quot; , &amp;quot;courier&amp;quot; , monospace; font-size: 12px;&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot; style=&quot;color: #333333; font-family: &amp;quot;consolas&amp;quot; , &amp;quot;menlo&amp;quot; , &amp;quot;liberation mono&amp;quot; , &amp;quot;courier&amp;quot; , monospace; font-size: 12px;&quot;&gt;DB&lt;/span&gt;&lt;span class=&quot;o&quot; style=&quot;color: #333333; font-family: &amp;quot;consolas&amp;quot; , &amp;quot;menlo&amp;quot; , &amp;quot;liberation mono&amp;quot; , &amp;quot;courier&amp;quot; , monospace; font-size: 12px; font-weight: 700;&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;na&quot; style=&quot;color: teal; font-family: &amp;quot;consolas&amp;quot; , &amp;quot;menlo&amp;quot; , &amp;quot;liberation mono&amp;quot; , &amp;quot;courier&amp;quot; , monospace; font-size: 12px;&quot;&gt;select&lt;/span&gt;&lt;span class=&quot;p&quot; style=&quot;color: #333333; font-family: &amp;quot;consolas&amp;quot; , &amp;quot;menlo&amp;quot; , &amp;quot;liberation mono&amp;quot; , &amp;quot;courier&amp;quot; , monospace; font-size: 12px;&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot; style=&quot;color: #333333; font-family: &amp;quot;consolas&amp;quot; , &amp;quot;menlo&amp;quot; , &amp;quot;liberation mono&amp;quot; , &amp;quot;courier&amp;quot; , monospace; font-size: 12px;&quot;&gt;DB&lt;/span&gt;&lt;span class=&quot;o&quot; style=&quot;color: #333333; font-family: &amp;quot;consolas&amp;quot; , &amp;quot;menlo&amp;quot; , &amp;quot;liberation mono&amp;quot; , &amp;quot;courier&amp;quot; , monospace; font-size: 12px; font-weight: 700;&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;na&quot; style=&quot;color: teal; font-family: &amp;quot;consolas&amp;quot; , &amp;quot;menlo&amp;quot; , &amp;quot;liberation mono&amp;quot; , &amp;quot;courier&amp;quot; , monospace; font-size: 12px;&quot;&gt;raw&lt;/span&gt;&lt;span class=&quot;p&quot; style=&quot;color: #333333; font-family: &amp;quot;consolas&amp;quot; , &amp;quot;menlo&amp;quot; , &amp;quot;liberation mono&amp;quot; , &amp;quot;courier&amp;quot; , monospace; font-size: 12px;&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot; style=&quot;color: #bb8844; font-family: &amp;quot;consolas&amp;quot; , &amp;quot;menlo&amp;quot; , &amp;quot;liberation mono&amp;quot; , &amp;quot;courier&amp;quot; , monospace; font-size: 12px;&quot;&gt;&quot;SHOW STATUS LIKE &#39;SSL_CIPHER&#39;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot; style=&quot;color: #333333; font-family: &amp;quot;consolas&amp;quot; , &amp;quot;menlo&amp;quot; , &amp;quot;liberation mono&amp;quot; , &amp;quot;courier&amp;quot; , monospace; font-size: 12px;&quot;&gt;)));&amp;nbsp;&lt;/span&gt;to check that I&#39;m connected with SSL.&lt;br /&gt;
&lt;br /&gt;
I also decided that although PCI compliance doesn&#39;t actually demand that you encrypt the data in the database I would rather take the performance hit and protect my user details. &amp;nbsp;I don&#39;t use them terribly often so the overhead of decrypting them when I do seems worth the effort to avoid the reputational damage my company would suffer if it leaked user details. &lt;br /&gt;
&lt;br /&gt;
Again this is very easy to accomplish with Laravel. &amp;nbsp;I just implemented a trait which I can selectively apply to any of my entities that I want to protect. &amp;nbsp;So now if the application is breached and nasty people run off with my database at least they&#39;ll need to have the application key if they hope to read my users details.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;script src=&quot;https://gist.github.com/andybeak/64dc056b774a6eb2c1bbb1cb3b6029f6.js&quot;&gt;&lt;/script&gt;
</description><link>http://blog.mystery.co.za/2017/01/complying-with-pci-database.html</link><author>noreply@blogger.com (Andrew Beak)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhv2-cq19oEMH5WWyFK_BrhFPg5ZJnV5RNYHjX8e1PUANzOB-UYd540VMU_PsQIdqtPF5puF1m35lBIvorm-cIQbuElEHKmXSd1CQ1td8Ph2tC6NlPnMyexXvcvBwOeEiBnRuT7nHl7nkm9/s72-c/padlock-lock-chain-key-39624.jpeg" height="72" width="72"/><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-1356169494544655761.post-4647442373621523215</guid><pubDate>Fri, 20 Jan 2017 15:05:00 +0000</pubDate><atom:updated>2017-01-20T15:05:32.917+00:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">PHP</category><title>Email injection attacks in PHP</title><description>&lt;table cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;float: right; margin-left: 1em; text-align: right;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgZCknVZ7Wgn9XFfMsF4eX6AOTTXvHjm8pG-TAj79DAPi-P1vZG-k1Z-sNz9e_5rY2Mb15MbGdb2yQZF9wOfBNZ7L9424D6rGK2YdkXxOB047GSacLk0bR73m3ur622irkBTcWlvoeLdy-g/s1600/apple-691323_1280.jpg&quot; imageanchor=&quot;1&quot; style=&quot;clear: right; margin-bottom: 1em; margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;213&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgZCknVZ7Wgn9XFfMsF4eX6AOTTXvHjm8pG-TAj79DAPi-P1vZG-k1Z-sNz9e_5rY2Mb15MbGdb2yQZF9wOfBNZ7L9424D6rGK2YdkXxOB047GSacLk0bR73m3ur622irkBTcWlvoeLdy-g/s320/apple-691323_1280.jpg&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;Image: Pixabay&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
Email injection is one of the topics I cover in my Zend certification guide. &amp;nbsp;You can grab a copy on Leanpub - &lt;a href=&quot;https://leanpub.com/phpforprogrammers/&quot;&gt;https://leanpub.com/phpforprogrammers/&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
It is possible for a user to supply hexadecimal control characters that allow them to change the message body or recipient list. &lt;br /&gt;
&lt;br /&gt;
For example, if your form allows the person to enter their email address as a “from” field for the email then the following string will cause additional recipients to be included as cc and blind carbon copy recipients of the message:&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-family: Courier New, Courier, monospace;&quot;&gt;sender@example.com%0ACc:target@email.com%0ABcc:anotherperson@emailexample.com,stranger@shouldhavefiltered.com&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
It is also possible for the attacker to provide their own body, and even to change the MIME type of the message being sent. &amp;nbsp;This means that your form could be used by spammers to send mail from.&lt;br /&gt;
&lt;br /&gt;
You can protect against this in a couple of ways.&lt;br /&gt;
&lt;br /&gt;
Make sure that you properly filter input that you use when sending mails. &amp;nbsp;The `filter_var()` function provides a number of flags that you can use to make sure that your input data conforms to a desired pattern.&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-family: Courier New, Courier, monospace;&quot;&gt;&lt;span class=&quot;Apple-tab-span&quot; style=&quot;white-space: pre;&quot;&gt; &lt;/span&gt;&amp;lt;?php&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: Courier New, Courier, monospace;&quot;&gt;&lt;span class=&quot;Apple-tab-span&quot; style=&quot;white-space: pre;&quot;&gt; &lt;/span&gt;$from = $_POST[&quot;sender&quot;];&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: Courier New, Courier, monospace;&quot;&gt;&lt;span class=&quot;Apple-tab-span&quot; style=&quot;white-space: pre;&quot;&gt; &lt;/span&gt;$from = filter_var($from, FILTER_SANITIZE_EMAIL);&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: Courier New, Courier, monospace;&quot;&gt;&lt;span class=&quot;Apple-tab-span&quot; style=&quot;white-space: pre;&quot;&gt; &lt;/span&gt;// send the email&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
You could also install and use the Suhosin PHP extension. &amp;nbsp;It provides the `suhosin.mail.protect` directive that will guard against this.&lt;br /&gt;
&lt;br /&gt;
You could implement a tarpit to slow bots down or trap them indefinitely. &amp;nbsp;Take a look at &lt;a href=&quot;https://github.com/msigley/PHP-HTTP-Tarpit&quot; target=&quot;_blank&quot;&gt;msigley/PHP-HTTP-Tarpit&lt;/a&gt; on Github &amp;nbsp;as an example of a tarpit.&lt;br /&gt;
&lt;br /&gt;
Just be careful that when setting up your mail server you must make sure it is not configured as an open relay that allows anybody on the Internet to use it to send mail. &amp;nbsp;You should also consider closing port 25 (SMTP) on your firewall so that outside hosts are unable to reach your server.&lt;br /&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
</description><link>http://blog.mystery.co.za/2017/01/email-injection-attacks-in-php.html</link><author>noreply@blogger.com (Andrew Beak)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgZCknVZ7Wgn9XFfMsF4eX6AOTTXvHjm8pG-TAj79DAPi-P1vZG-k1Z-sNz9e_5rY2Mb15MbGdb2yQZF9wOfBNZ7L9424D6rGK2YdkXxOB047GSacLk0bR73m3ur622irkBTcWlvoeLdy-g/s72-c/apple-691323_1280.jpg" height="72" width="72"/><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-1356169494544655761.post-3159944097609759284</guid><pubDate>Thu, 12 Jan 2017 11:14:00 +0000</pubDate><atom:updated>2017-01-20T14:59:41.537+00:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">PHP</category><title>Traits in PHP</title><description>&lt;h3&gt;
What are traits?&lt;/h3&gt;
&lt;div&gt;
&lt;div&gt;
&lt;table cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;float: right; margin-left: 1em; text-align: right;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg7SlLuOIlXXPeXOxrV6yQtEArK98kRWwVKtCzo8JOAKPLfHtCVyCRsXm1YZiHLkX7AbkEVt32kUdUUOC8v9NxVdAxmoGpCNdos8BizGjKonQG0pDpLOpUcaNDipYfb_FvYr62d7MAzWVCL/s1600/unknown-1769656_1280.png&quot; imageanchor=&quot;1&quot; style=&quot;clear: right; margin-bottom: 1em; margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;320&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg7SlLuOIlXXPeXOxrV6yQtEArK98kRWwVKtCzo8JOAKPLfHtCVyCRsXm1YZiHLkX7AbkEVt32kUdUUOC8v9NxVdAxmoGpCNdos8BizGjKonQG0pDpLOpUcaNDipYfb_FvYr62d7MAzWVCL/s320/unknown-1769656_1280.png&quot; width=&quot;290&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;Image: Pixabay&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
Let&#39;s start by understanding what traits are and how they&#39;re useful. &amp;nbsp;We&#39;ll move on to code examples straight after that.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
Traits are not unique to PHP and are available in other languages too. &amp;nbsp;They provide a way to extend the functionality of a class. &amp;nbsp;A trait will have methods to implement this functionality and make these available as if they had been defined in the class itself.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
In other words traits are flattened into a class and it doesn’t matter if a method is defined in the trait or in the class that uses the trait. You could copy and paste the code from the trait into the class and it would be used in the same manner.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
The code that is included into a trait is intended to encapsulate reusable properties and methods that&lt;/div&gt;
&lt;div&gt;
can be applied to multiple classes. &amp;nbsp;Traits group functionality in a fine-grained and consistent way and allow you to reuse this functionality without requiring inheritance.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
I mentioned before in the lecture on inheritance that PHP does not support inheriting from multiple classes. &amp;nbsp;Traits offer a way to provide similar functionality and offer a way to avoid the so-called &quot;&lt;a href=&quot;https://www.google.co.uk/url?sa=t&amp;amp;rct=j&amp;amp;q=&amp;amp;esrc=s&amp;amp;source=web&amp;amp;cd=1&amp;amp;cad=rja&amp;amp;uact=8&amp;amp;ved=0ahUKEwj38pzZurzRAhULJsAKHSMOAdMQygQIITAA&amp;amp;url=https%3A%2F%2Fen.wikipedia.org%2Fwiki%2FMultiple_inheritance%23The_diamond_problem&amp;amp;usg=AFQjCNEC5xzkbo5uuHjrmm54ye3Ytf3QVA&quot; target=&quot;_blank&quot;&gt;diamond problem&lt;/a&gt;&quot; that is introduced with multiple inheritance.&lt;br /&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;h3&gt;
Defining and using traits&lt;/h3&gt;
&lt;div&gt;
&lt;div&gt;
Traits are very easy to use and there are lots of places that they can be useful. &amp;nbsp;I&#39;m going to run through an example that defines a trait that implements singleton functionality.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;script src=&quot;https://gist.github.com/andybeak/97e08f8905c04fba48f0fa5e4eb53f4c.js&quot;&gt;&lt;/script&gt;

&lt;br /&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
We define the trait using the `trait` keyword followed by the name we want to use. &amp;nbsp;We can declare variables in the trait as well as functions. &amp;nbsp;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
If you look at the code example you&#39;ll see that my Singleton trait has got all of the implementation requirements for the singleton pattern.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
I&#39;ve defined a private static variable called $instance in the trait. &amp;nbsp;Although it&#39;s private in the trait remember that the code gets flattened when a class uses the trait and so the class would be able to access it. &amp;nbsp;If that&#39;s not clear just imagine copying and pasting the code in the trait straight into your class.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
The trait also includes a static function called getInstance that returns a singleton instance of the class.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
I&#39;m using the trait in the class called MyClass. &amp;nbsp;The `use` keyword indicates that we&#39;re using the trait. &amp;nbsp;You might already be familiar with the `use` keyword as a way to import namespaces. &amp;nbsp;When we import a namespace the `use` keyword is outside of the class, if it&#39;s inside the class it indicates that we&#39;re using a trait.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
Now we&#39;re able to call the `getInstance()` static method on the class. &amp;nbsp;This method is defined in the trait but we&#39;re using the trait in the class so it&#39;s available to be used as part of the class.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
We&#39;re able to overwrite the constructor method in the trait. &amp;nbsp;In this example I&#39;ve set the constructor to private so that you are not able to instantiate the class but rather have to use the singleton method to get an instance. &amp;nbsp;Trying to create a new instance of MyClass generates a fatal error because `__construct()` is public.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
Now we&#39;re able to easily give a class all of the functionality it needs to be turned into a singleton without interfering with the inheritance hierarchy.&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;
Namespacing Traits&lt;/h3&gt;
So far we&#39;ve seen how to declare and use a trait. &amp;nbsp;What happens if we want to use a name for our trait that has already been using elsewhere in our code? &amp;nbsp;It might happen for example that one of the libraries we use has already defined a trait named Singleton.&lt;br /&gt;
&lt;br /&gt;
Traits, just like classes, can be namespaced. &amp;nbsp;The name of a trait must be unique within its namespace otherwise PHP will throw a fatal error.&lt;br /&gt;
&lt;br /&gt;
Just as with classes if you&#39;re wanting to use a trait in a namespace other than your own then you&#39;ll need to provide a full reference to it.&lt;br /&gt;
&lt;h3&gt;
Inheritance and Method precedence&lt;/h3&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
Traits may not inherit from other traits or classes but you can nest one trait inside another.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
Okay so inheritance is simple, but what about situations where a class and trait have a method with the same name? &amp;nbsp;Which method will take precedence and be run when we try to call it?&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
Methods declared in a class using a trait take precedence over methods declared in the trait. However, methods in a trait will override methods inherited by a class.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
Expressed more simply, precedence in traits and classes is as follows:&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
CLASS METHODS first, then TRAIT METHODS, and lastly INHERITED METHODS.&lt;/div&gt;
&lt;/div&gt;
&lt;h3&gt;
&lt;/h3&gt;
&lt;h3&gt;
Conflict resolution in traits&lt;/h3&gt;
&lt;div&gt;
&lt;div&gt;
What happens if your class is using two traits and they both declare a method with the same name? &amp;nbsp;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
PHP requires you to explicitly resolve these sort of conflicts and will throw a fatal error if you try to use two traits that each define a method of the same name.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
There are two approaches to resolving name conflicts between traits. &amp;nbsp;You can either tell PHP not to use a method that is defined in one of the traits or you can tell PHP to use an alias for a method in one of the traits. &amp;nbsp;Put shorter, you can exclude a method or alias a method.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
In order to exclude a method you use the `insteadof` operator which tells PHP to use a method from one trait instead of the method from the other.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
In order to alias a method you use the `as` operator to provide an alias to use. &amp;nbsp;The only trick here is that it&#39;s not enough to just specify an alias, you must *also* exclude the original method from being used.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
Right, so we&#39;ve got that we can either exclude or alias a method to resolve naming conflicts between traits. &amp;nbsp;Lets look at some code to see how it works.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;script src=&quot;https://gist.github.com/andybeak/cc120822368bb2cc43f4a6950ef1d970.js&quot;&gt;&lt;/script&gt;

&lt;br /&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
First we&#39;re going to create two traits. &amp;nbsp;We&#39;re going to define two methods in each trait and name them such that they&#39;ll create naming conflicts.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
We&#39;re going to use the traits in a class which we&#39;re calling DomesticPet. &amp;nbsp;Because the methods in the traits have conflicting names we&#39;re going to have to resolve these conflicts.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
We resolve the conflicts in a code block that follows the `use` keyword. &amp;nbsp;We have to resolve every conflict so lets step through this now.&lt;br /&gt;
&lt;h4&gt;
&lt;/h4&gt;
&lt;h4&gt;
Replacing methods with &quot;insteadof&quot;&lt;/h4&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
For the first method, `makeNoise()`, we&#39;re going to simply exclude the method from the Dog class. &amp;nbsp;We would do this if we only ever want to use the Cat method and expect never to use the Dog method.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
In order to do this we&#39;re going to tell the class to use the makeNoise function declared in the Cat trait instead of the one in the Dog trait. &amp;nbsp;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
We use the `insteadof` keyword to tell PHP to use the method declared in the Cat trait instead of the one in the dog class.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
Whenever the `makeNoise()` function is called PHP will know that you mean the one defined in the Cat trait and you won&#39;t be able to access the one from the Dog trait.&lt;/div&gt;
&lt;/div&gt;
&lt;h4&gt;
&lt;/h4&gt;
&lt;h4&gt;
Aliasing methods with &quot;as&quot;&lt;/h4&gt;
&lt;div&gt;
&lt;div&gt;
For the second method, `wantWalkies()`, we&#39;re going to rather alias the method in Cat. &amp;nbsp;We would do this when we want to be able to use both methods in our class.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
You may have already noticed in the code sample that there are three statements in the conflict resolution block but only two functions that need to be resolved. &amp;nbsp;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
Remember that earlier I said the trick with aliasing methods is that it&#39;s not enough to alias by itself and that you must also exclude the method you&#39;re aliasing? &amp;nbsp;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
We&#39;re going to need two statements to alias our method.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
First we&#39;re going to tell PHP to give the method an alias that we can use to reference it. &amp;nbsp;Then we&#39;re going to tell PHP to exclude the original method.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
In order to alias the method we use the `as` keyword. &amp;nbsp; We&#39;re going to give the `wantWalkies()` method an alias of `kittyWalk()`. &amp;nbsp;By doing this you will be able to call the `kittyWalk()` function in your class. &amp;nbsp;This isn&#39;t a new function, it&#39;s just an alias for the function in the Cat trait.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
Right after we alias the function we go ahead and tell PHP to use the Dog `wantWalkies()` method instead of the Cat trait. &amp;nbsp;If we left out this step we would still have a naming conflict.&lt;/div&gt;
&lt;/div&gt;
&lt;h4&gt;
&lt;/h4&gt;
&lt;h4&gt;
The end result&lt;/h4&gt;
&lt;div&gt;
&lt;div&gt;
We used the Cat `makeNoise()` function instead of the Dog one. &amp;nbsp;When we call the `makeNoise()` method on the object we are referencing the cat function and so the output is &quot;Purr&quot;.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
We aliased the Cat `wantWalkies()` method as `kittyWalk()` and so when we call the `kittyWalk()` method on the object we are referencing the aliased Cat method and the output is &quot;No Thanks&quot;.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
Because we are aliasing the Cat `wantWalkies` method we also had to use the Dog method instead of the Cat method. &amp;nbsp;Therefore when we call the `wantWalkies` method on the object we are referencing the Dog method and the output is &quot;Yes Please&quot;.&lt;/div&gt;
&lt;/div&gt;
&lt;h4&gt;
&lt;/h4&gt;
&lt;h4&gt;
Visibility&lt;/h4&gt;
&lt;div&gt;
&lt;div&gt;
You might want to change the visibility of a method that you&#39;re using from a trait. &amp;nbsp;This is done by using the `as` keyword. &amp;nbsp;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
Lets look at a simple example before we take a look at the complications.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
We can set an aliased method as protected by inserting the keyword `protected` between `as` and the name we&#39;re aliasing it as. &amp;nbsp;For example we can say,&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;span class=&quot;Apple-tab-span&quot; style=&quot;white-space: pre;&quot;&gt; &lt;/span&gt;Dog::wantWalkies as protected doggyWalk;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
Changing the visibility of a replaced method is slightly more complicated.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
In order to change the visibility of a replaced method we need to use two steps. &amp;nbsp;First we use `insteadof` to replace the method and then in the next statement we stipulate that it must be protected.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
Lets look at this in code. &amp;nbsp;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;script src=&quot;https://gist.github.com/andybeak/1d9c19c0f52b4e08607f79eeffcfbb46.js&quot;&gt;&lt;/script&gt;

&lt;br /&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
In the first line inside the trait use block I&#39;m aliasing a function and also changing its visibility. &amp;nbsp;The keyword `protected` appears between `as` and the aliased name. &amp;nbsp;If I were to try and call `doggyWalk()` from outside the class I would receive an error because it is protected.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
I&#39;ve commented out the second line because it&#39;s an example of what doesn&#39;t work. &amp;nbsp;The rule of thumb seems to be that you can&#39;t use both `as` and `insteadof` in one single statement. &amp;nbsp;You need to split them into separate statements as I do in the last two lines of the use block.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;/div&gt;
</description><link>http://blog.mystery.co.za/2017/01/traits-in-php.html</link><author>noreply@blogger.com (Andrew Beak)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg7SlLuOIlXXPeXOxrV6yQtEArK98kRWwVKtCzo8JOAKPLfHtCVyCRsXm1YZiHLkX7AbkEVt32kUdUUOC8v9NxVdAxmoGpCNdos8BizGjKonQG0pDpLOpUcaNDipYfb_FvYr62d7MAzWVCL/s72-c/unknown-1769656_1280.png" height="72" width="72"/><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-1356169494544655761.post-732454276860242832</guid><pubDate>Sat, 24 Sep 2016 13:36:00 +0000</pubDate><atom:updated>2016-09-24T14:40:53.035+01:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">angular 2</category><category domain="http://www.blogger.com/atom/ns#">javascript</category><category domain="http://www.blogger.com/atom/ns#">phoenix</category><title>Connecting to Elixir web channels from the Angular 2 quickstart application</title><description>&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgRWwHITr9W2T2hKyAG94n5zbADvUzbJWavdLKhAkA9jOo7VjVZGp8zsOdtGvJIKDPANqtTvgd2t24N4_NGIurfk36b_wufCXgq1obljzmfesOZXmsLMnNlwqtmler26JqRNEUGEmRi-Lyp/s1600/angular.png&quot; imageanchor=&quot;1&quot; style=&quot;clear: right; float: right; margin-bottom: 1em; margin-left: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgRWwHITr9W2T2hKyAG94n5zbADvUzbJWavdLKhAkA9jOo7VjVZGp8zsOdtGvJIKDPANqtTvgd2t24N4_NGIurfk36b_wufCXgq1obljzmfesOZXmsLMnNlwqtmler26JqRNEUGEmRi-Lyp/s1600/angular.png&quot; /&gt;&lt;/a&gt;I am busy learning &lt;a href=&quot;http://elixir-lang.org/&quot; target=&quot;_blank&quot;&gt;Elixir&lt;/a&gt;, a language that adds syntactic sugar to the awesomely scalable and concurrent Erlang language. &amp;nbsp;The &quot;go to&quot; framework in Elixir is &lt;a href=&quot;http://www.phoenixframework.org/&quot; target=&quot;_blank&quot;&gt;Phoenix&lt;/a&gt;&amp;nbsp;and I&#39;m busy writing my &quot;hello world&quot; application which will serve up data across a web channel.&lt;br /&gt;
&lt;br /&gt;
I followed the Typescript version of the Quickstart guide for Angular 2 (&lt;a href=&quot;https://angular.io/docs/ts/latest/quickstart.html&quot; target=&quot;_blank&quot;&gt;here&lt;/a&gt;). &amp;nbsp;I really like what I&#39;ve seen of Typescript so far. &amp;nbsp;Dependencies are easy to manage and the ability to define interfaces is a good sign of how well structured the language is. &amp;nbsp;I think Satya Nadella should be made an open source hero, if such an award exists.&lt;br /&gt;
&lt;br /&gt;
Anyway, what I wanted to do was get my Angular 2 application to be able to connect to the Elixir server channel and send a request to listen to a particular stream. &amp;nbsp;The idea is to use the Actor concurrency model (explained brilliantly in &quot;&lt;a href=&quot;https://www.manning.com/books/the-little-elixir-and-otp-guidebook&quot; target=&quot;_blank&quot;&gt;The Little Elixir &amp;amp; OTP Book&lt;/a&gt;&quot;) to start up a new process whenever a request for a stream arrives. &amp;nbsp;This article focuses on setting up the Angular 2 connection. &lt;br /&gt;
&lt;br /&gt;
The official &lt;a href=&quot;https://www.npmjs.com/package/phoenix&quot; target=&quot;_blank&quot;&gt;Phoenix javascript client&lt;/a&gt; is packaged up and can be installed with &quot;npm install --save phoenix&quot;. &amp;nbsp;Once it&#39;s installed we need to tell SystemJS where to find it so we amend systemjs.config.js and include it in our map array. &lt;br /&gt;
&lt;br /&gt;
&lt;script src=&quot;https://gist.github.com/andybeak/43c67a4108e35dabc45ad10abc11ddce.js?file=systemjs.config.js&quot;&gt;&lt;/script&gt;

Now we&#39;ll be able to import the Phoenix class from it wherever we need to.  We&#39;ll need it in the service that we&#39;re going to use to wrap the Phoenix channel support.  Lets take a look…&lt;br /&gt;
&lt;br /&gt;
&lt;script src=&quot;https://gist.github.com/andybeak/43c67a4108e35dabc45ad10abc11ddce.js?file=app.phoenix_channels.service.ts&quot;&gt;&lt;/script&gt;

We import the library using the map name that we set up in our systemjs config to make it available to our class. &amp;nbsp;We then copy the code that Phoenix shows on the &lt;a href=&quot;http://www.phoenixframework.org/docs/channels&quot; target=&quot;_blank&quot;&gt;channels manual page&lt;/a&gt;&amp;nbsp;to actually handle the connection.&lt;br /&gt;
&lt;br /&gt;
This gives us a channel service that we&#39;ll be able to inject into components.&lt;br /&gt;
&lt;br /&gt;
Angular 2 uses constructor injection so we&#39;ll be passing a channel service into the constructor function.  Before we do that though we need to let the component know that our channel service is a provider so that we can properly typecast the injected variable.

&lt;script src=&quot;https://gist.github.com/andybeak/43c67a4108e35dabc45ad10abc11ddce.js?file=app.component.ts&quot;&gt;&lt;/script&gt;

Once all this is saved run your app with &#39;npm start&#39; and if you pop over to your browser window you should see error messages in your console log saying that the channel connection was refused, unless of course you&#39;re already running your Phoenix server.</description><link>http://blog.mystery.co.za/2016/09/connecting-to-elixir-web-channels-from.html</link><author>noreply@blogger.com (Andrew Beak)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgRWwHITr9W2T2hKyAG94n5zbADvUzbJWavdLKhAkA9jOo7VjVZGp8zsOdtGvJIKDPANqtTvgd2t24N4_NGIurfk36b_wufCXgq1obljzmfesOZXmsLMnNlwqtmler26JqRNEUGEmRi-Lyp/s72-c/angular.png" height="72" width="72"/><thr:total>0</thr:total><georss:featurename>Cotswold District, UK</georss:featurename><georss:point>51.893444036983986 -2.11761474609375</georss:point><georss:box>51.736461036983989 -2.44033824609375 52.050427036983983 -1.79489124609375</georss:box></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-1356169494544655761.post-6900572261169744920</guid><pubDate>Tue, 06 Sep 2016 08:12:00 +0000</pubDate><atom:updated>2016-09-06T09:26:03.340+01:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Laravel</category><category domain="http://www.blogger.com/atom/ns#">PHP</category><title>Laravel refusing to start up</title><description>&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjiJV1psP79tVu9iPmRjSqoDP9YYTRWLmpfgmf_dpYDws9KznwcZ_GuFZnUui-zajSVEkCPtoTFFkBwRvQiyCC7FkQDLZkdgVgiSBhyphenhyphencPvG7SkKaHK9IRGcIedww1jf2W7OYtmyMoGxY4K-/s1600/laravel.png&quot; imageanchor=&quot;1&quot; style=&quot;clear: right; float: right; margin-bottom: 1em; margin-left: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;320&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjiJV1psP79tVu9iPmRjSqoDP9YYTRWLmpfgmf_dpYDws9KznwcZ_GuFZnUui-zajSVEkCPtoTFFkBwRvQiyCC7FkQDLZkdgVgiSBhyphenhyphencPvG7SkKaHK9IRGcIedww1jf2W7OYtmyMoGxY4K-/s320/laravel.png&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;I&#39;m very much a fan of the clean implementation of Laravel but really dislike the fact that if there is something wrong with the .env file it refuses to give any meaningful information.&lt;br /&gt;
&lt;br /&gt;
Laravel uses the &lt;a href=&quot;https://github.com/vlucas/phpdotenv&quot; target=&quot;_blank&quot;&gt;vlucas/phpdotenv package&lt;/a&gt; to manage its environment.&lt;br /&gt;
&lt;br /&gt;
It&#39;s pretty well known that if you have a space on either side the = sign in a key value pair then the .env file is invalid, but I had checked for this (and checked again).&lt;br /&gt;
&lt;br /&gt;
Laravel will try to use its standard logging methods before they have actually had a chance to be booted up with the result that you&#39;re left with a &quot;reflection error&quot; exception message on the CLI rather than the actual cause of the problem in the dotenv package.&lt;br /&gt;
&lt;br /&gt;
Debugging this is not trivial and I resorted to using strace to try and determine exactly what was going on. &amp;nbsp;Don&#39;t do this at home kids! &amp;nbsp;The easier solution is at the end of the article.&lt;br /&gt;
&lt;br /&gt;
I used the following command to generate a trace of the system calls being made by PHP while trying (and failing) to run artisan optimize.&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;pre style=&quot;background: #f0f0f0; border: 1px dashed #cccccc; color: black; font-family: &amp;quot;arial&amp;quot;; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;&quot;&gt;&lt;code style=&quot;color: black; word-wrap: normal;&quot;&gt; strace php artisan optimize &amp;amp;&amp;gt; /tmp/strace.txt  
&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
That let me walk through the calls and eventually confirm that the first PHP exception was thrown in the package that deals with reading the environment file.&lt;br /&gt;
&lt;br /&gt;
&lt;pre style=&quot;background: #f0f0f0; border: 1px dashed #cccccc; color: black; font-family: &amp;quot;arial&amp;quot;; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;&quot;&gt;&lt;code style=&quot;color: black; word-wrap: normal;&quot;&gt; access(&quot;/var/www/raffle.bhf.org.uk.new/vendor/vlucas/phpdotenv/src/Dotenv.php&quot;, F_OK) = 0  
 ... more lines loading up more of the package and showing us processing it ....  
 access(&quot;/var/www/raffle.bhf.org.uk.new/vendor/vlucas/phpdotenv/src/Exception/InvalidFileException.php&quot;, F_OK) = 0  
&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
But sadly there was no indication of exactly what the problem is with the file!&lt;br /&gt;
&lt;br /&gt;
I decided that creating a minimal project dedicated to debugging my .env file was going to be faster than anything else. &lt;br /&gt;
&lt;br /&gt;
I created a temporary directory and ran &quot;composer require vlucas/phpdotenv&quot;. &amp;nbsp;Then I placed my faulty .env file into the directory and ran the following PHP file:&lt;br /&gt;
&lt;br /&gt;
&lt;pre style=&quot;background: #f0f0f0; border: 1px dashed #cccccc; color: black; font-family: &amp;quot;arial&amp;quot;; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;&quot;&gt;&lt;code style=&quot;color: black; word-wrap: normal;&quot;&gt; &amp;lt;?php  
 require(&#39;vendor/autoload.php&#39;);  
 $dotenv = new Dotenv\Dotenv(__DIR__);  
 $dotenv-&amp;gt;load();  
&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
This gave me the actual exception in DotEnv which was that &quot;Dotenv values containing spaces must be surrounded by quotes&quot;. &amp;nbsp;So it wasn&#39;t a space around the = sign but rather a space in one of my values, which made my life a lot easier! &amp;nbsp;As an extra bonus the first line in the stack showed exactly which key was problematic.</description><link>http://blog.mystery.co.za/2016/09/laravel-refusing-to-start-up.html</link><author>noreply@blogger.com (Andrew Beak)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjiJV1psP79tVu9iPmRjSqoDP9YYTRWLmpfgmf_dpYDws9KznwcZ_GuFZnUui-zajSVEkCPtoTFFkBwRvQiyCC7FkQDLZkdgVgiSBhyphenhyphencPvG7SkKaHK9IRGcIedww1jf2W7OYtmyMoGxY4K-/s72-c/laravel.png" height="72" width="72"/><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-1356169494544655761.post-7370949690318341152</guid><pubDate>Sun, 21 Aug 2016 15:43:00 +0000</pubDate><atom:updated>2016-08-21T16:43:39.490+01:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">devops</category><category domain="http://www.blogger.com/atom/ns#">docker</category><category domain="http://www.blogger.com/atom/ns#">microsoft</category><category domain="http://www.blogger.com/atom/ns#">servers</category><title>Solving a Docker in VirtualBox DNS issue</title><description>&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: right;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgyKXG-vkueymUjdjIJlqgTzBYntt3gpBmyTN-dyyKqBMSYxyjhubhOAaS40XlHDIZEwg4lknDLCcI9YY1jByLCyytNQhYWzGfsIs6QbZonMW7gIyhQnNEL0STyWY5cLouLJJk0FsljbEaf/s1600/docker.png&quot; imageanchor=&quot;1&quot; style=&quot;clear: right; float: right; margin-bottom: 1em; margin-left: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;285&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgyKXG-vkueymUjdjIJlqgTzBYntt3gpBmyTN-dyyKqBMSYxyjhubhOAaS40XlHDIZEwg4lknDLCcI9YY1jByLCyytNQhYWzGfsIs6QbZonMW7gIyhQnNEL0STyWY5cLouLJJk0FsljbEaf/s320/docker.png&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div style=&quot;text-align: left;&quot;&gt;
I&#39;ve recently been playing with Docker on Windows in conjunction with &lt;a href=&quot;http://www.howtogeek.com/249966/how-to-install-and-use-the-linux-bash-shell-on-windows-10/&quot; target=&quot;_blank&quot;&gt;Linux on Windows&lt;/a&gt;. &amp;nbsp;I&#39;m really amazed at how cool the stuff coming out of Microsoft is under Satya Nadella. &amp;nbsp;&lt;/div&gt;
&lt;div style=&quot;text-align: left;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div style=&quot;text-align: left;&quot;&gt;
When I was using &lt;a href=&quot;https://www.docker.com/products/docker-toolbox&quot; target=&quot;_blank&quot;&gt;Docker Toolbox&lt;/a&gt; on Windows my Dockerfiles would build correctly but as soon as I tried to run them in a Virtualbox host they would fail with the error &quot;&lt;i&gt;Something wicked happened resolving &#39;archive.ubuntu.com:http&#39; (-5 - No address associated with hostname)&lt;/i&gt;&quot;&lt;/div&gt;
&lt;br /&gt;
&lt;br /&gt;
Of course this error wasn&#39;t distribution specific and none of the distros I tried were working.&lt;br /&gt;
&lt;br /&gt;
The stack that I am using is Windows Home hosting Ubuntu on VirtualBox which is running Docker. &amp;nbsp;I&#39;m using bash on Linux for Windows because it&#39;s easier to do stuff like ssh but it&#39;s not relevant to this setup.&lt;br /&gt;
&lt;br /&gt;
I tried setting the DNS in the Docker setup by using RUN steps to update /etc/resolv. &amp;nbsp;This felt a bit hacky and didn&#39;t work anyway. &lt;br /&gt;
&lt;br /&gt;
In the end the fix was to go to my Windows shell and run ipconfig to get its DNS servers. &amp;nbsp;I then went and edited /etc/default/docker in my VirtualBox guest server and set the Docker options to use this DNS. &amp;nbsp;After restarting the docker service it was able to resolve DNS properly.&lt;br /&gt;
&lt;span style=&quot;font-family: Courier New, Courier, monospace;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;span style=&quot;font-family: Courier New, Courier, monospace;&quot;&gt;# Use DOCKER_OPTS to modify the daemon startup options.&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: Courier New, Courier, monospace;&quot;&gt;DOCKER_OPTS=&quot;--dns 192.168.1.254&quot;&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
I had installed Docker with the package but I think if you install with binary you would set the DNS when you start up the daemon.&lt;br /&gt;
&lt;br /&gt;
Ironically Docker was easier to use with the Windows toolkit but I&#39;ll be deploying to Linux machines so I wanted to get this working under Ubuntu.</description><link>http://blog.mystery.co.za/2016/08/solving-docker-in-virtualbox-dns-issue.html</link><author>noreply@blogger.com (Andrew Beak)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgyKXG-vkueymUjdjIJlqgTzBYntt3gpBmyTN-dyyKqBMSYxyjhubhOAaS40XlHDIZEwg4lknDLCcI9YY1jByLCyytNQhYWzGfsIs6QbZonMW7gIyhQnNEL0STyWY5cLouLJJk0FsljbEaf/s72-c/docker.png" height="72" width="72"/><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-1356169494544655761.post-6382839555026548316</guid><pubDate>Tue, 12 Jul 2016 15:05:00 +0000</pubDate><atom:updated>2016-07-12T16:08:18.613+01:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Laravel</category><category domain="http://www.blogger.com/atom/ns#">PHP</category><category domain="http://www.blogger.com/atom/ns#">testing</category><title>Limiting how often you run seeds and migrations in Laravel 5.2 testing</title><description>&lt;table cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;float: right; margin-left: 1em; text-align: right;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhRU8eJsinwf-zH-SGXwTWB-BdKa6jc_xkyXGGBNgS4yVrXoP_MM548Z_mGl-vgBYwLGnPum9Z-qL6f3Gd9AYS3EWBahyDk6blI1pGAPkphao3yKHb_uvt7tFyUSLQ448xnoRc64cLM4OAr/s1600/sun-flower-179010_1920.jpg&quot; imageanchor=&quot;1&quot; style=&quot;clear: right; margin-bottom: 1em; margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;213&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhRU8eJsinwf-zH-SGXwTWB-BdKa6jc_xkyXGGBNgS4yVrXoP_MM548Z_mGl-vgBYwLGnPum9Z-qL6f3Gd9AYS3EWBahyDk6blI1pGAPkphao3yKHb_uvt7tFyUSLQ448xnoRc64cLM4OAr/s320/sun-flower-179010_1920.jpg&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;Image: &lt;a href=&quot;https://pixabay.com/en/sun-flower-sunflower-flowers-summer-179010/&quot; target=&quot;_blank&quot;&gt;Pixabay&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
If integration tests take too long to run then we stop running them because they&#39;re just an annoyance. &amp;nbsp;This is obviously not ideal and so getting my tests to run quickly is important.&lt;br /&gt;
&lt;br /&gt;
I&#39;m busy writing a test suite for Laravel and wanted more flexibility than the built-in options that Laravel offers for working with databases between tests.&lt;br /&gt;
&lt;br /&gt;
Using the DataMigrations trait meant that my seed data would be lost after every test. &amp;nbsp;Migrating and seeding my entire database for every test in my suite is very time consuming.&lt;br /&gt;
&lt;br /&gt;
Even when using an in-memory sqlite database doing a complete migration and seed was taking several seconds on my dev box.&lt;br /&gt;
&lt;br /&gt;
If I used the DataTransactions trait then my migrations and seeds would never run and so my tests would fail because the fixture data is missing.&lt;br /&gt;
&lt;br /&gt;
My solution was to use a static variable in the base TestCase class that Laravel supplies. &amp;nbsp;It has to be static so that it retains its values between tests by the way. &amp;nbsp;This variable is a boolean flag and tracks whether we need to run the migrations and seeds. &amp;nbsp;We initialise the class with it set on and so the migrations and seeds will run the first time that any test run.&lt;br /&gt;
&lt;br /&gt;
Now the fact that I&#39;m using Laravel DataTransactions should spare me from affecting the database between tests but if I wanted to be 100% certain I could set the flag and have my database refreshed when the next test runs.&lt;br /&gt;
&lt;br /&gt;
This also means that if the DataTransactions trait is not sufficient (for example I&#39;m using multiple database connections) then I can manually refresh when I want to.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;script src=&quot;https://gist.github.com/andybeak/375441b7c8a1f2c3cc572de26ab9a7c7.js&quot;&gt;&lt;/script&gt;</description><link>http://blog.mystery.co.za/2016/07/limiting-seeds-and-migrations-in.html</link><author>noreply@blogger.com (Andrew Beak)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhRU8eJsinwf-zH-SGXwTWB-BdKa6jc_xkyXGGBNgS4yVrXoP_MM548Z_mGl-vgBYwLGnPum9Z-qL6f3Gd9AYS3EWBahyDk6blI1pGAPkphao3yKHb_uvt7tFyUSLQ448xnoRc64cLM4OAr/s72-c/sun-flower-179010_1920.jpg" height="72" width="72"/><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-1356169494544655761.post-7487388893945180204</guid><pubDate>Wed, 06 Jul 2016 13:33:00 +0000</pubDate><atom:updated>2016-07-06T14:33:52.906+01:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">html</category><category domain="http://www.blogger.com/atom/ns#">javascript</category><title>Implementing a very simple &quot;all of the above&quot; checkbox</title><description>&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;float: right; margin-left: 1em; text-align: right;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjIdYlSyQBxhVaEhDGU3DxbUHU2Q36YaZMgHX8zl7tPC1s-nB0pVMVWjSPlChggGjvA_KNwFTgqfozWjKpi1Z4C_auIkOVu8o6N1oezWtht0sXTlTCGcv9SMptTxho6PzasY1GFaK_o3LOK/s1600/school-153561_1280.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;100&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjIdYlSyQBxhVaEhDGU3DxbUHU2Q36YaZMgHX8zl7tPC1s-nB0pVMVWjSPlChggGjvA_KNwFTgqfozWjKpi1Z4C_auIkOVu8o6N1oezWtht0sXTlTCGcv9SMptTxho6PzasY1GFaK_o3LOK/s200/school-153561_1280.png&quot; width=&quot;200&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;Image:&lt;a href=&quot;https://pixabay.com/en/school-pencil-pen-write-sketch-153561/&quot; target=&quot;_blank&quot;&gt;Pixabay&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;br /&gt;
Implementing a checkbox that lets the user &quot;select all of the above&quot; is trivial with jQuery.&lt;br /&gt;
&lt;br /&gt;
The idea is that when the user selects the &quot;all&quot; checkbox then all of the other checkboxes are set to the same value.  Similarly if they deselect any of the other options we need to turn off the &quot;all&quot; checkbox.
&lt;br /&gt;
&lt;br /&gt;
&lt;script src=&quot;https://gist.github.com/andybeak/e1073a3f1ea530a7e7aa0e2077b6b93e.js&quot;&gt;&lt;/script&gt;</description><link>http://blog.mystery.co.za/2016/07/implementing-very-simple-all-of-above.html</link><author>noreply@blogger.com (Andrew Beak)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjIdYlSyQBxhVaEhDGU3DxbUHU2Q36YaZMgHX8zl7tPC1s-nB0pVMVWjSPlChggGjvA_KNwFTgqfozWjKpi1Z4C_auIkOVu8o6N1oezWtht0sXTlTCGcv9SMptTxho6PzasY1GFaK_o3LOK/s72-c/school-153561_1280.png" height="72" width="72"/><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-1356169494544655761.post-6901563997640060641</guid><pubDate>Tue, 28 Jun 2016 09:52:00 +0000</pubDate><atom:updated>2016-06-28T15:29:43.219+01:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">bitcoin</category><title>Why am I so late on the Bitcoin train?</title><description>&lt;table cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;float: right; margin-left: 1em; text-align: right;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjD9KBkEHQJ5F5QbA7XF0Md2HKPLc0GjQbPZhpM-60paSXIBT31cbYRmUotnGmBlj7iG0diNZ7ZcEhwZNHzvzGC8CyUebcFtEjQukt1-3HPTWemUs8mUXJiyjoVZWonIKIIKK5aQW9BOzCc/s1600/bitcoin-503581_640.jpg&quot; imageanchor=&quot;1&quot; style=&quot;clear: right; margin-bottom: 1em; margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;226&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjD9KBkEHQJ5F5QbA7XF0Md2HKPLc0GjQbPZhpM-60paSXIBT31cbYRmUotnGmBlj7iG0diNZ7ZcEhwZNHzvzGC8CyUebcFtEjQukt1-3HPTWemUs8mUXJiyjoVZWonIKIIKK5aQW9BOzCc/s320/bitcoin-503581_640.jpg&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;Image: &lt;a href=&quot;https://pixabay.com/en/bitcoin-binary-one-null-coin-503581/&quot; target=&quot;_blank&quot;&gt;Pixabay&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
I&#39;ve been somewhat of a Bitcoin sceptic for quite some time. &amp;nbsp;When it first became a thing I was worried that governments would legislate it out of existence. &lt;br /&gt;
&lt;br /&gt;
It has had a pretty bad rap of being associated with the dark web and it is definitely the choice of currency for malware authors. &lt;br /&gt;
&lt;br /&gt;
In its normal usage Bitcoin is more transparent than cash. &amp;nbsp;If I give you a cash note there is no permanent record of the transaction and the tax man can&#39;t get a sniff into our business.&lt;br /&gt;
&lt;br /&gt;
Governments hate transactions they can&#39;t tax or police and so in the beginning there was a concern that Bitcoin would be outlawed. &lt;br /&gt;
&lt;br /&gt;
In contrast to cash, if I transfer you Bitcoin then there is a record of the transaction that anybody in the world can inspect. &amp;nbsp;It&#39;s possible to trace the coins in your Bitcoin wallet back through the various people who owned them. &amp;nbsp;Anybody in the world can watch the contents of your wallet and see where you spend your money. &lt;br /&gt;
&lt;br /&gt;
This is exactly the sort of thing that governments love.&lt;br /&gt;
&lt;br /&gt;
Of course not everybody wants to share their transactions with the world and so there are Bitcoin laundering services that attempt to anonymise the coins in your wallet. &amp;nbsp;This puts us back to square one with Bitcoin being very convenient for criminals to use in order to evade financial intelligence controls.&lt;br /&gt;
&lt;br /&gt;
I suspect that the process of banning Bitcoin transactions would impinge too much on the freedom of citizens. &amp;nbsp;Some governments are talking about banning cryptography in order to maintain surveillance on their citizens so it&#39;s not a stretch to imagine them being displeased with Bitcoin laundering services. &amp;nbsp;*sigh*.&lt;br /&gt;
&lt;br /&gt;
Anyway, back to my killer app for Bitcoin... I&#39;ve recently emigrated and am still paying debt in South Africa. &amp;nbsp;Sending money back to South Africa costs about £30 and takes 2-4 days if I use the banking system. &amp;nbsp;If I use Bitcoin the process costs around £ 3 and I can have the money in my South African bank account on the very same day.&lt;br /&gt;
&lt;br /&gt;
Bitcoin costs one tenth the price of using banks and is at least twice as fast. &lt;br /&gt;
&lt;br /&gt;
My transaction is not at all anonymous and a government can trace the funds to me on either end of the transaction where copies of my passport are stored with the exchanges. &amp;nbsp;If I wanted to hide this from thieves, the government, spear-phishers, and other people who want to take my money without giving anything in return then I would use a Bitcoin laundry service.</description><link>http://blog.mystery.co.za/2016/06/my-killer-app-for-bitcoin.html</link><author>noreply@blogger.com (Andrew Beak)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjD9KBkEHQJ5F5QbA7XF0Md2HKPLc0GjQbPZhpM-60paSXIBT31cbYRmUotnGmBlj7iG0diNZ7ZcEhwZNHzvzGC8CyUebcFtEjQukt1-3HPTWemUs8mUXJiyjoVZWonIKIIKK5aQW9BOzCc/s72-c/bitcoin-503581_640.jpg" height="72" width="72"/><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-1356169494544655761.post-2069223809834149683</guid><pubDate>Fri, 10 Jun 2016 08:21:00 +0000</pubDate><atom:updated>2016-06-17T14:15:44.608+01:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">devops</category><category domain="http://www.blogger.com/atom/ns#">servers</category><title>Restarting BOINC automatically</title><description>&lt;table cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;float: right; margin-left: 1em; text-align: right;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://boinc.berkeley.edu/logo/boinc_logo.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;133&quot; src=&quot;https://boinc.berkeley.edu/logo/boinc_logo.png&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;Image:&amp;nbsp;https://boinc.berkeley.edu, fair use&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;div style=&quot;text-align: left;&quot;&gt;
BOINC is a program curated by the University of Berkeley that allows people around the world to contribute to science projects. &amp;nbsp;&lt;/div&gt;
&lt;div style=&quot;text-align: left;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div style=&quot;text-align: left;&quot;&gt;
It works by using spare cycles from your computer to perform calculations that help do things like folding proteins to find candidates for cancer treatment, mapping the milky way galaxy, searching for pulsar stars, and improving our understanding of climate change and its effects.&lt;/div&gt;
&lt;div style=&quot;text-align: left;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div style=&quot;text-align: left;&quot;&gt;
It runs as a background process and is easily configured to only run in certain conditions - like when you haven&#39;t used your computer for 10 minutes for example.&lt;/div&gt;
&lt;div style=&quot;text-align: left;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div style=&quot;text-align: left;&quot;&gt;
It comes with a nifty GUI manager and for most people using it on their desktop this post is not going to be at all relevant. &amp;nbsp;This post deals with the case where a person is running it on a server without the GUI manager.&lt;/div&gt;
&lt;div style=&quot;text-align: left;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div style=&quot;text-align: left;&quot;&gt;
Anyway, the easiest solution I found to restarting BOINC on a headless server was to use &lt;a href=&quot;http://supervisord.org/&quot; target=&quot;_blank&quot;&gt;supervisord&lt;/a&gt;. &amp;nbsp;It&#39;s pretty much the &quot;go to&quot; tool for simple process management and adding the BOINC program was as easy as would be expected:&lt;/div&gt;
&lt;div style=&quot;text-align: left;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div style=&quot;text-align: left;&quot;&gt;
Here&#39;s the program definition from my /etc/supervisord.conf file:&lt;br /&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;pre style=&quot;background: #f0f0f0; border: 1px dashed #cccccc; color: black; font-family: &amp;quot;arial&amp;quot;; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;&quot;&gt;&lt;code style=&quot;color: black; word-wrap: normal;&quot;&gt; [program:BOINC]  
 command=sh /root/boinc/startup.sh  
 process_name=boinc  
 exitcodes=0,1,2
 autostart=true  
 autorestart=unexpected  
&lt;/code&gt;&lt;/pre&gt;
&lt;span style=&quot;font-size: x-small;&quot;&gt;&lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;
I use a script to restart BOINC because I want to make sure that I get reconnected to my account manager in case something goes wrong.&lt;br /&gt;
&lt;br /&gt;
Here&#39;s what /root/boinc/startup.sh script looks like:&lt;br /&gt;
&lt;br /&gt;
&lt;pre style=&quot;background: #f0f0f0; border: 1px dashed #cccccc; color: black; font-family: &amp;quot;arial&amp;quot;; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;&quot;&gt;&lt;code style=&quot;color: black; word-wrap: normal;&quot;&gt; /etc/init.d/boinc-client start  
 sleep 10  
 boinccmd --join_acct_mgr http://bam.boincstats.com &amp;lt;user&amp;gt; &amp;lt;pass&amp;gt;  
&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
If BOINC crashes it will automatically get restarted and reconnected to my account manager. &amp;nbsp;This means I don&#39;t need to monitor that process on all the servers I install it on.</description><link>http://blog.mystery.co.za/2016/06/restarting-boinc-automatically.html</link><author>noreply@blogger.com (Andrew Beak)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-1356169494544655761.post-7093018226935965524</guid><pubDate>Wed, 01 Jun 2016 12:37:00 +0000</pubDate><atom:updated>2016-06-01T13:38:21.284+01:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">servers</category><category domain="http://www.blogger.com/atom/ns#">virtual</category><category domain="http://www.blogger.com/atom/ns#">vm</category><title>Associating Vagrant 1.7.2 with an existing VM</title><description>&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgiVSlgGYlvyA-1iSAxl-Tn0lLEs6J_mnl_AAEeVIVdUCD5DRQDeuvR3RV9lZep44unOuZl2TDG9MsfSE3AHX0Txn4GVruyu1b10uSs62e5iZE3DgBywDL3oLE9QdGiqz3Xmb62rVtRZT0c/s1600/Vagrant.png&quot; imageanchor=&quot;1&quot; style=&quot;clear: right; float: right; margin-bottom: 1em; margin-left: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgiVSlgGYlvyA-1iSAxl-Tn0lLEs6J_mnl_AAEeVIVdUCD5DRQDeuvR3RV9lZep44unOuZl2TDG9MsfSE3AHX0Txn4GVruyu1b10uSs62e5iZE3DgBywDL3oLE9QdGiqz3Xmb62rVtRZT0c/s320/Vagrant.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
My Vagrant 1.7.2 machine bugged out and when I tried to `vagrant up` it spawned a new box instead of bringing up my existing machine.&lt;br /&gt;
&lt;br /&gt;
Naturally this was a problem because I had made some manual changes to the config that I hadn&#39;t had a chance to persist to my puppet config files yet. &lt;br /&gt;
&lt;br /&gt;
To fix the problem I found used the command `&lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;VBoxManage list vms&lt;/span&gt;` in the directory where my Vagrantfile is. &amp;nbsp;This provided me a list of the machine images it could find.&lt;br /&gt;
&lt;br /&gt;
I then went and edited the file at&amp;nbsp;.vagrant/machines/default/virtualbox/id and replaced the UUID that was in there with the one that the VBoxManage command had output.&lt;br /&gt;
&lt;br /&gt;
Now when I run &#39;vagrant up&#39; it spins up the correct VM. &amp;nbsp;Happy days.</description><link>http://blog.mystery.co.za/2016/06/associating-vagrant-172-with-existing-vm.html</link><author>noreply@blogger.com (Andrew Beak)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgiVSlgGYlvyA-1iSAxl-Tn0lLEs6J_mnl_AAEeVIVdUCD5DRQDeuvR3RV9lZep44unOuZl2TDG9MsfSE3AHX0Txn4GVruyu1b10uSs62e5iZE3DgBywDL3oLE9QdGiqz3Xmb62rVtRZT0c/s72-c/Vagrant.png" height="72" width="72"/><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-1356169494544655761.post-903450898714224035</guid><pubDate>Fri, 27 May 2016 12:03:00 +0000</pubDate><atom:updated>2016-05-27T13:05:39.685+01:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">nginx</category><category domain="http://www.blogger.com/atom/ns#">servers</category><title>Redirecting non-www urls to www and http to https in Nginx web server</title><description>&lt;table cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;float: right; margin-left: 1em; text-align: right;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiq6BBhcl3rfu7mIeohpwhY9Kt2Gjphl3IKTZyGUl67bzFoYGZxf_8u6DX3YQyhvraf8Dll3iLvGe6iOCXdjj-gLB81kjpWUrBxX8fobDV4ehDo-kKP7CD8oMp5FcLtWnl2k8m_V4GXtYSC/s1600/arrow-35405_640.png&quot; imageanchor=&quot;1&quot; style=&quot;clear: right; margin-bottom: 1em; margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;320&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiq6BBhcl3rfu7mIeohpwhY9Kt2Gjphl3IKTZyGUl67bzFoYGZxf_8u6DX3YQyhvraf8Dll3iLvGe6iOCXdjj-gLB81kjpWUrBxX8fobDV4ehDo-kKP7CD8oMp5FcLtWnl2k8m_V4GXtYSC/s320/arrow-35405_640.png&quot; width=&quot;319&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;Image: Pixabay&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
Although I&#39;m currently playing with Elixir and its HTTP servers like Cowboy at the moment Nginx is still my go-to server for production PHP.&lt;br /&gt;
&lt;br /&gt;
If you haven&#39;t already swapped your web-server from Apache then you really should consider installing Nginx on a test server and running some stress tests on it. &amp;nbsp;I wrote about stress testing in my &lt;a href=&quot;https://www.scaleyourphp.site/&quot; target=&quot;_blank&quot;&gt;book on scaling PHP&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
Redirecting non-www traffic to www in nginx is best accomplished by using the &quot;return&quot; verb. &amp;nbsp;You could use a rewrite but the Nginx manual suggests that a return is better in the section on &quot;&lt;a href=&quot;https://www.nginx.com/resources/wiki/start/topics/tutorials/config_pitfalls/#taxing-rewrites&quot; rel=&quot;nofollow&quot; target=&quot;_blank&quot;&gt;Taxing Rewrites&lt;/a&gt;&quot;.&lt;br /&gt;
&lt;br /&gt;
Server blocks are cheap in Nginx and I find it&#39;s simplest to have two redirects for the person who arrives on the non-secure non-canonical form of my link. &amp;nbsp;I wouldn&#39;t expect many people to reach this link because obviously every link that I create will be properly formatted so being redirected twice will only affect a small minority of people.&lt;br /&gt;
&lt;br /&gt;
Anyway, here&#39;s the config:&lt;br /&gt;
&lt;br /&gt;
&lt;script src=&quot;https://gist.github.com/andybeak/06a091fd817fe3230745a1362d4e5bbf.js&quot;&gt;&lt;/script&gt;</description><link>http://blog.mystery.co.za/2016/05/handling-redirects-in-nginx.html</link><author>noreply@blogger.com (Andrew Beak)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiq6BBhcl3rfu7mIeohpwhY9Kt2Gjphl3IKTZyGUl67bzFoYGZxf_8u6DX3YQyhvraf8Dll3iLvGe6iOCXdjj-gLB81kjpWUrBxX8fobDV4ehDo-kKP7CD8oMp5FcLtWnl2k8m_V4GXtYSC/s72-c/arrow-35405_640.png" height="72" width="72"/><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-1356169494544655761.post-4326619081167026832</guid><pubDate>Wed, 11 May 2016 09:25:00 +0000</pubDate><atom:updated>2016-05-11T12:36:46.385+01:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">logging</category><category domain="http://www.blogger.com/atom/ns#">PHP</category><title>Logging as a debugging tool</title><description>&lt;table cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;float: right; margin-left: 1em; text-align: right;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgPOdeyRu5FlLv20gCCVXT0W2VNURmB5fIrle0m6J6zEHNkFSizxLNZQd9G18y9sS9Qeo7hVppDHWwvPhp8WoRt-ac7I1gaQJdINVRUcZeaIt4tuzZJB-M4dxpFPReeG9XyjAnZfKuu4_49/s1600/wood-sun-forest-evening-7557.jpg&quot; imageanchor=&quot;1&quot; style=&quot;clear: right; margin-bottom: 1em; margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;214&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgPOdeyRu5FlLv20gCCVXT0W2VNURmB5fIrle0m6J6zEHNkFSizxLNZQd9G18y9sS9Qeo7hVppDHWwvPhp8WoRt-ac7I1gaQJdINVRUcZeaIt4tuzZJB-M4dxpFPReeG9XyjAnZfKuu4_49/s320/wood-sun-forest-evening-7557.jpg&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;Image: &lt;a href=&quot;https://www.pexels.com/photo/wood-forest-evening-chopped-wood-7557/&quot;&gt;https://www.pexels.com&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
Logging is such an important part of my approach to debugging that I sometimes struggle to understand how programmers avoid including logging in their applications.&lt;br /&gt;
&lt;br /&gt;
Having sufficiently detailed logs enables me to avoid having to make assumptions about variable values and program logic flow. &lt;br /&gt;
&lt;br /&gt;
For example, when a customer wants to know why their credit card was charged twice I want to be able to answer with certainty that we processed the transaction only once and be able to produce the data that I sent to the payment provider.&lt;br /&gt;
&lt;br /&gt;
I have three very simple rules for logging that I follow whenever I&#39;m feeling like being nice to future me. &amp;nbsp;If I hate future me and want him to spend more time answering queries than is needed then I forget these rules:&lt;br /&gt;
&lt;br /&gt;
&lt;ol&gt;
&lt;li&gt;The first command in any function I write is a debug statement confirming entry into the function&lt;/li&gt;
&lt;li&gt;Any time that the script terminates with an error then the error condition is logged, along with the exception message and variable values if applicable.&lt;/li&gt;
&lt;li&gt;When I catch an exception then I log that as a debug message in the place that I catch it rather than letting it bubble up the stack. &amp;nbsp;If I&#39;m the one throwing the exception then I log in the place where I throw it.&amp;nbsp;&lt;/li&gt;
&lt;/ol&gt;
&lt;div&gt;
These rules have grown on me from the experience of debugging code and having to deal with an assortment of customer queries that have been escalated to the development team.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
By logging the entry into functions I can go back on my logs and see the path that a request took through the code. &amp;nbsp;Instead of wondering how a particular state of execution came about I have a good trail of functions that led me to that point. &amp;nbsp;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
To me errors that fail silently are the worst possible errors. &amp;nbsp;I don&#39;t expect that the user needs to be alerted to every error or its details, but if I am unable to send a transactional email then I expect that there should be a log of that fact. That might sound self-evident but I&#39;ve recently worked on a project where we send a mail and don&#39;t check if it was successful or not. &amp;nbsp;This would only occasionally happen and we only noticed something was amiss when a customer complained. &amp;nbsp;I was not able to determine when the problem arose, how often it happened, or to whom I should resend transactional mails along with an apology.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
Logging an exception in the place I catch it has consistently proven to be helpful. &amp;nbsp;Having a log as close as possible to the source of the error condition helps to narrow down the stack that it occurred in. &amp;nbsp;This is especially valuable when I rethrow the exception with a user friendly message because I don&#39;t lose the technical details of the program state.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
Because my logs can get very spammy in production I use the &quot;Fingers Crossed&quot; feature of Monolog. &amp;nbsp;I prefer this to the alternative of increasing the bar for logging to &quot;info&quot; and above because when an error occurs then I have a verbose track of my program state. &amp;nbsp;I&#39;ve created a &lt;a href=&quot;https://gist.github.com/andybeak/fc40ba25dd5e61a69b18078db2fb6a74&quot; target=&quot;_blank&quot;&gt;gist showing the setup&lt;/a&gt; in Laravel 5.1 and 5.2 but the approach will work anywhere that you use Monolog.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
Another useful trick I&#39;ve learned is to integrate my application errors into my log aggregating platform. &amp;nbsp;I use Loggly to aggregate my logs and push application error messages to it. &amp;nbsp;This lets me easily view my application errors in the context of my various server logs so spotting an nginx error or something in syslog that could contribute to the application problem is a lot easier. &amp;nbsp;The gist that I linked above shows my Loggly setup, but you can also read &lt;a href=&quot;https://www.loggly.com/docs/php-monolog/&quot; target=&quot;_blank&quot;&gt;their documentation&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
Useful and appropriate logging is an indispensable tool for debugging and if you&#39;re not working on developing your own logging style to support your approach to debugging then hop on it!&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
</description><link>http://blog.mystery.co.za/2016/05/logging-as-debugging-tool.html</link><author>noreply@blogger.com (Andrew Beak)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgPOdeyRu5FlLv20gCCVXT0W2VNURmB5fIrle0m6J6zEHNkFSizxLNZQd9G18y9sS9Qeo7hVppDHWwvPhp8WoRt-ac7I1gaQJdINVRUcZeaIt4tuzZJB-M4dxpFPReeG9XyjAnZfKuu4_49/s72-c/wood-sun-forest-evening-7557.jpg" height="72" width="72"/><thr:total>0</thr:total><georss:featurename>Cheltenham, Cheltenham, Gloucestershire, UK</georss:featurename><georss:point>51.8993855 -2.0782533000000285</georss:point><georss:box>51.820986 -2.2396148000000284 51.977785000000004 -1.9168918000000286</georss:box></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-1356169494544655761.post-1223116675442805347</guid><pubDate>Fri, 08 Apr 2016 16:37:00 +0000</pubDate><atom:updated>2016-04-09T17:26:53.091+01:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">security</category><category domain="http://www.blogger.com/atom/ns#">web development</category><title>Are tokens enough to prevent CSRF?</title><description>&lt;table cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;float: right; margin-left: 1em; text-align: right;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgVZGw__lJRs9BL3_8632aVxpzoI5Zhr4CLqqaVrfG6iz6Mppz3NUktqlfEd6_ao1fq6N-EQZSEt-3hOkZb8vXQJCypyzsrc_uveq_3NBfn623m-2gs-tWBduOjY8oFJjv4-ILAcL2u4ySt/s1600/pirate-667763_1280.jpg&quot; imageanchor=&quot;1&quot; style=&quot;clear: right; margin-bottom: 1em; margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;213&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgVZGw__lJRs9BL3_8632aVxpzoI5Zhr4CLqqaVrfG6iz6Mppz3NUktqlfEd6_ao1fq6N-EQZSEt-3hOkZb8vXQJCypyzsrc_uveq_3NBfn623m-2gs-tWBduOjY8oFJjv4-ILAcL2u4ySt/s320/pirate-667763_1280.jpg&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;Image: Pixabay&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
CSRF attacks exploit the trust that a website has in a client like a web browser. &amp;nbsp;These attacks rely on the website trusting that a request from a client is actually the intention of the person using that client. &lt;br /&gt;
&lt;br /&gt;
An attacker will try to trick the web browser into issuing a request to the server. &amp;nbsp;The server will assume that the request is valid because it trusts the client.&lt;br /&gt;
&lt;br /&gt;
At its most simple a CSRF attack could involve making a malicious form on a webpage that causes the client to send a POST request to a url.&lt;br /&gt;
&lt;br /&gt;
As an example, imagine that a user called Alice is logged into Facebook in one tab and is browsing the internet on another tab. &amp;nbsp;A filthy pirate Bob creates a malicious form in a webpage that submits a POST request to Facebook that sends a person to a link of Rick Astley dancing. &amp;nbsp;Alice arrives on the page we made and Javascript submits the form to Facebook. &amp;nbsp;Facebook trusts Alice&#39;s web browser and there is a valid session for her so it processes the request. &amp;nbsp;Before she knows it her Facebook status is a link to Rick Astley (who, by the way, will never give you up).&lt;br /&gt;
&lt;br /&gt;
Of course Facebook is not vulnerable to this, and neither should your code be.&lt;br /&gt;
&lt;br /&gt;
The best way to mitigate CSRF attacks is to generate a very random token which you store in Alice&#39;s session. &amp;nbsp;You then make sure that whenever your output a form on your site that you include this token in the form. &amp;nbsp;Alice will send the token whenever she submits the form and you can compare it to the one stored in her session to make sure that the request is originating from your site.&lt;br /&gt;
&lt;br /&gt;
Bob has no way of knowing what the token in Alice&#39;s session is and so he can&#39;t trick her browser into submitting it to our site. &amp;nbsp;Our site will get a request from Alice&#39;s client but because it doesn&#39;t have the token we can reject it.&lt;br /&gt;
&lt;br /&gt;
In other words the effect of the token is to stop relying on implicit trust for the client and rather set up a challenge response system whereby the client proves it is trustworthy. &amp;nbsp;If Bob wants to send a request that will be accepted he must find a way to read a token off a form that your site has rendered for Alice. &amp;nbsp;This is not a trivial task but can possibly be done - there are very creative ways (like &lt;a href=&quot;https://thehackernews.com/2016/04/microsoft-bug-bounty.html&quot;&gt;this attack&lt;/a&gt;) to abuse requests.&lt;br /&gt;
&lt;br /&gt;
Another way to prevent CSRF is to rely on multi-factor authentication. &amp;nbsp;We can group ways to authenticate into knowledge (where you know something like a password), possession (where you have something like a USB dongle), or inherent (where you are something). &lt;br /&gt;
&lt;br /&gt;
Instead of just relying on one of these mechanisms we can use two (or more) in order to authenticate. &amp;nbsp;For example we can ask a person for a password and also require that they enter a code sent to the mobile phone which proves they have the mobile phone linked to their account.&lt;br /&gt;
&lt;br /&gt;
CSRF will become much harder for Bob to accomplish if our form is protected with multi-factor authentication (MFA). &amp;nbsp;Of course this comes with a user experience cost so only critical forms need to be protected with MFA. &amp;nbsp;For less critical forms the single authentication method of a CSRF token will suffice.&lt;br /&gt;
&lt;br /&gt;
There is debate around whether it is useful to check whether the referrer header matches your site is helpful in deterring CSRF. &amp;nbsp;It is true that it is trivial to spoof this header in a connection that you control. &amp;nbsp;However it is more difficult to get this level of control in a typical CSRF attack where browsers will rewrite the referrer header in an ajax call (see the &lt;a href=&quot;https://www.w3.org/TR/XMLHttpRequest/#the-setrequestheader()-method&quot;&gt;specification&lt;/a&gt;). &amp;nbsp;By itself it is not sufficient to deter CSRF, but it can raise the difficulty level for attackers.&lt;br /&gt;
&lt;br /&gt;
Cookies should obviously not be used to mitigate CSRF. &amp;nbsp;They are sent along with any request to the domain whether the user intended to make the request or not.&lt;br /&gt;
&lt;br /&gt;
Setting a session timeout window can help a little bit as it will narrow the window that requests will be trusted by your application. &amp;nbsp;This will also improve your session security by making it harder for fixation attacks to be effective.&lt;br /&gt;
&lt;br /&gt;
Tokens are the most convenient way to make CSRF harder to accomplish on your site. &amp;nbsp;When used in conjunction with referrer checks and a narrow session window you can make it significantly harder for an opponent to accomplish a successful attack.&lt;br /&gt;
&lt;br /&gt;
For critically important forms multi-factor authentication are the way to go. &amp;nbsp;They interrupt the user experience and enforce explicit authentication. &amp;nbsp;This has a negative affect on your UX but makes it impossible (I think!) for an automated CSRF attack to be effective.&lt;br /&gt;
&lt;br /&gt;</description><link>http://blog.mystery.co.za/2016/04/are-tokens-enough-to-prevent-csrf.html</link><author>noreply@blogger.com (Andrew Beak)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgVZGw__lJRs9BL3_8632aVxpzoI5Zhr4CLqqaVrfG6iz6Mppz3NUktqlfEd6_ao1fq6N-EQZSEt-3hOkZb8vXQJCypyzsrc_uveq_3NBfn623m-2gs-tWBduOjY8oFJjv4-ILAcL2u4ySt/s72-c/pirate-667763_1280.jpg" height="72" width="72"/><thr:total>0</thr:total></item></channel></rss>