<?xml version="1.0" encoding="utf-8" standalone="yes"?><feed xmlns="http://www.w3.org/2005/Atom">
  <title></title>
  <subtitle></subtitle>
  <id>https://www.endpointdev.com/blog/tags/php/</id>
  <link href="https://www.endpointdev.com/blog/tags/php/"/>
  <link href="https://www.endpointdev.com/blog/tags/php/" rel="self"/>
  <updated>2025-02-28T00:00:00+00:00</updated>
  <author>
    <name>End Point Dev</name>
  </author>
  
    <entry>
      <title>How to Analyze Application Logs and Extract Actionable Insights</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2025/02/how-to-analyze-application-logs-and-extract-actionable-insights/"/>
      <id>https://www.endpointdev.com/blog/2025/02/how-to-analyze-application-logs-and-extract-actionable-insights/</id>
      <published>2025-02-28T00:00:00+00:00</published>
      <author>
        <name>Edgar Mlowe</name>
      </author>
      <content type="html">
        &lt;p&gt;&lt;img src=&#34;/blog/2025/02/how-to-analyze-application-logs-and-extract-actionable-insights/logs.webp&#34; alt=&#34;A side-on view of a large pile of wooden logs&#34;&gt;&lt;/p&gt;
&lt;p&gt;Photo by &lt;a href=&#34;https://unsplash.com/@tcdinger&#34;&gt;Timo C. Dinger&lt;/a&gt; on &lt;a href=&#34;https://unsplash.com/photos/brown-and-black-wood-logs-Oo3L5fL1lBU&#34;&gt;Unsplash&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Logs often accumulate unnoticed—until something breaks. Then, they suddenly become vital tools for diagnosing issues. By combining just a few command-line techniques, you can quickly spot recurring problems, identify suspicious activity, and strengthen your application’s defenses.&lt;/p&gt;
&lt;p&gt;Below is a sample error log we’ll reference in the examples:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plaintext&#34; data-lang=&#34;plaintext&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[2025-01-01 12:34:56] [client 192.168.1.1:12345] PHP Notice: Undefined variable $foo in /var/www/html/index.php on line 45 | referer: http://example.com/index.php
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[2025-01-01 12:34:56] [client 192.168.1.1:12345] PHP Notice: Undefined variable $foo in /var/www/html/index.php on line 45 | referer: http://example.com/index.php
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[2025-01-01 12:34:57] [client 192.168.1.1:12345] PHP Notice: Undefined variable $foo in /var/www/html/index.php on line 45 | referer: http://example.com/index.php
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[2025-01-01 12:35:00] [client 10.0.0.2:6789] PHP Warning: Division by zero in /var/www/html/script.php on line 23 | referer: http://example.com/script.php
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[2025-01-01 13:35:01] [client 10.0.0.2:6789] PHP Warning: Division by zero in /var/www/html/script.php on line 23 | referer: http://example.com/script.php
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[2025-01-01 13:36:10] [client 192.168.1.1:12345] PHP Fatal error: Call to undefined function baz() in /var/www/html/lib.php on line 78 | referer: http://example.com/index.php
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[2025-01-01 14:36:11] [client 172.16.0.5:54321] PHP Fatal error: Call to undefined function baz() in /var/www/html/lib.php on line 78 | referer: http://example.com/index.php&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3 id=&#34;a-note-on-commands&#34;&gt;A Note on Commands&lt;/h3&gt;
&lt;p&gt;Throughout this blog, you’ll notice repeated use of commands like &lt;code&gt;awk&lt;/code&gt;, &lt;code&gt;grep&lt;/code&gt;, &lt;code&gt;sed&lt;/code&gt;, &lt;code&gt;uniq&lt;/code&gt;, and &lt;code&gt;sort&lt;/code&gt;. These tools are indispensable for log analysis, allowing you to filter, transform, and summarize data efficiently. Here are some tips for learning these tools:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Use &lt;code&gt;man &amp;lt;command&amp;gt;&lt;/code&gt; (e.g., &lt;code&gt;man awk&lt;/code&gt;) to access the manual and dive deeper into a specific command&lt;/li&gt;
&lt;li&gt;Experiment with small examples to understand how commands like &lt;code&gt;awk&lt;/code&gt; process patterns or how &lt;code&gt;grep&lt;/code&gt; extracts data&lt;/li&gt;
&lt;li&gt;For a broader look at how these commands enhance productivity, check out my blog post: &lt;a href=&#34;/blog/2024/06/practical-linux-comandline-tips/&#34;&gt;Practical Linux Command Line Tips for Productivity and Efficiency&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These commands are not just for log analysis—they’re powerful for any data manipulation task you encounter. With practice, they can become essential to your workflow.&lt;/p&gt;
&lt;h3 id=&#34;1-debugging-application-errors&#34;&gt;1. Debugging Application Errors&lt;/h3&gt;
&lt;h4 id=&#34;11-summarize-frequent-issues&#34;&gt;1.1 Summarize Frequent Issues&lt;/h4&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sed -E &amp;#39;s/^\[.*\] //&amp;#39; error.log | sort | uniq -c | sort -nr&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Purpose&lt;/strong&gt;: Remove timestamps/​client&amp;rsquo;s IP, then sort and count duplicates&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Outcome&lt;/strong&gt;: A quick snapshot of which errors appear most often&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plaintext&#34; data-lang=&#34;plaintext&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;3 PHP Notice: Undefined variable $foo ...
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;2 PHP Warning: Division by zero ...
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;2 PHP Fatal error: Call to undefined function baz() ...&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h4 id=&#34;12-categorize-errors-by-type&#34;&gt;1.2 Categorize Errors by Type&lt;/h4&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;grep -oE &amp;#39;PHP [^:]+&amp;#39; error.log | sort | uniq -c | sort -nr&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Purpose&lt;/strong&gt;: Extract error types (e.g., Notice, Warning, Fatal error)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Outcome&lt;/strong&gt;: Know whether notices, warnings, or fatal errors dominate&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plaintext&#34; data-lang=&#34;plaintext&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;3 Notice
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;2 Warning
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;2 Fatal error&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h4 id=&#34;13-find-problematic-scripts&#34;&gt;1.3 Find Problematic Scripts&lt;/h4&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;awk -F&amp;#39;in &amp;#39; &amp;#39;{print $2}&amp;#39; error.log | awk &amp;#39;{print $1}&amp;#39; | sort | uniq -c | sort -nr&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Purpose&lt;/strong&gt;: Identify which files generate the most errors&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Outcome&lt;/strong&gt;: Pinpoint error hotspots for focused debugging&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plaintext&#34; data-lang=&#34;plaintext&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;3 /var/www/html/index.php
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;2 /var/www/html/script.php
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;2 /var/www/html/lib.php&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3 id=&#34;2-monitoring-system-behavior&#34;&gt;2. Monitoring System Behavior&lt;/h3&gt;
&lt;h4 id=&#34;21-track-problematic-ips&#34;&gt;2.1 Track Problematic IPs&lt;/h4&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;awk -F&amp;#39;\[client &amp;#39; &amp;#39;{print $2}&amp;#39; error.log | awk -F&amp;#39;:&amp;#39; &amp;#39;{print $1}&amp;#39; | sort | uniq -c | sort -nr&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Purpose&lt;/strong&gt;: Count how many errors each IP triggers&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Outcome&lt;/strong&gt;: Identify suspicious or high-traffic IPs&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plaintext&#34; data-lang=&#34;plaintext&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;4 192.168.1.1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;2 10.0.0.2
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;1 172.16.0.5&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h4 id=&#34;22-spot-high-error-time-periods&#34;&gt;2.2 Spot High-Error Time Periods&lt;/h4&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;awk -F&amp;#39;[][]&amp;#39; &amp;#39;{split($2, time, &amp;#34;:&amp;#34;); print $1, time[1]&amp;#34;:&amp;#34;time[2]&amp;#34;:00&amp;#34;}&amp;#39; error.log | sort | uniq -c | sort -nr&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Purpose&lt;/strong&gt;: Group errors by minute, revealing spikes or trends&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Outcome&lt;/strong&gt;: Correlate error surges with deployments or traffic peaks&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plaintext&#34; data-lang=&#34;plaintext&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;3 2025-01-01 12:34:00
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;1 2025-01-01 14:36:00
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;1 2025-01-01 13:36:00
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;...&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h4 id=&#34;23-analyze-ipreferer-pairs&#34;&gt;2.3 Analyze IP–Referer Pairs&lt;/h4&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;grep &amp;#34;referer:&amp;#34; error.log | awk -F&amp;#39;\\[client &amp;#39; &amp;#39;{print $2}&amp;#39; | awk -F&amp;#39;:| referer: &amp;#39; &amp;#39;{print $1, $3}&amp;#39; | sort | uniq -c | sort -nr&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Purpose&lt;/strong&gt;: Match IP addresses with the URLs they refer from&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Outcome&lt;/strong&gt;: Detect repeat offenders or malicious traffic patterns&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plaintext&#34; data-lang=&#34;plaintext&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;3 192.168.1.1 Undefined variable $foo ...
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;2 10.0.0.2 Division by zero ...
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;1 172.16.0.5 Call to undefined function baz() ...&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3 id=&#34;3-enhancing-security&#34;&gt;3. Enhancing Security&lt;/h3&gt;
&lt;h4 id=&#34;31-watch-sensitive-urls&#34;&gt;3.1 Watch Sensitive URLs&lt;/h4&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;grep -E &amp;#34;admin|login&amp;#34; script_error_frequency.txt | sort -nr&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Purpose&lt;/strong&gt;: Flag frequent hits on admin or login pages&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Outcome&lt;/strong&gt;: Gauge whether sensitive endpoints are under attack&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plaintext&#34; data-lang=&#34;plaintext&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;2 /var/www/html/login.php
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;2 /var/www/html/admin.php&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h4 id=&#34;32-pinpoint-critical-times&#34;&gt;3.2 Pinpoint Critical Times&lt;/h4&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;grep -E &amp;#34;admin|login&amp;#34; error.log | awk -F&amp;#39;[][]&amp;#39; &amp;#39;{split($2, time, &amp;#34;:&amp;#34;); print $1, time[1]&amp;#34;:&amp;#34;time[2]&amp;#34;:00&amp;#34;}&amp;#39; | sort | uniq -c | sort -nr&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Purpose&lt;/strong&gt;: Identify specific time windows for sensitive access&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Outcome&lt;/strong&gt;: Cross-reference suspicious activity with your security logs&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plaintext&#34; data-lang=&#34;plaintext&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;2 2025-01-01 12:34:00
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;1 2025-01-01 13:35:00
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;1 2025-01-01 12:35:00&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;These commands demonstrate how quickly you can glean insights from logs. With a little creativity, you can expand them to track response times, detect performance bottlenecks, and safeguard critical endpoints. Whether you’re tackling PHP errors or any other type of log data, the same principles apply: filter, sort, count, and investigate.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Curious to learn more?&lt;/strong&gt; Combine these strategies with automation tools, integrate them into CI/CD pipelines, or hook them up to visual dashboards. Your logs will become a gold mine of actionable information.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Integrating Laravel With a React Frontend</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2021/05/integrating-laravel-with-a-react-frontend/"/>
      <id>https://www.endpointdev.com/blog/2021/05/integrating-laravel-with-a-react-frontend/</id>
      <published>2021-05-07T00:00:00+00:00</published>
      <author>
        <name>Daniel Gomm</name>
      </author>
      <content type="html">
        &lt;p&gt;&lt;img src=&#34;/blog/2021/05/integrating-laravel-with-a-react-frontend/banner.jpg&#34; alt=&#34;&#34;&gt;
Photo by &lt;a href=&#34;https://unsplash.com/@scottwebb&#34;&gt;Scott Webb&lt;/a&gt; on &lt;a href=&#34;https://unsplash.com/photos/K8PXJMU2-3s&#34;&gt;Unsplash&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Frontend frameworks can be useful, and provide a lot of advantages over server-side rendering of views. It’s not uncommon now for websites to be purely presentational frontend applications. Thankfully &lt;a href=&#34;https://laravel.com/&#34;&gt;Laravel&lt;/a&gt; provides some helpers for including a dedicated frontend, including a fantastic npm package, laravel-mix, which heavily simplifies the use of webpack.&lt;/p&gt;
&lt;p&gt;In this article I’ll go over how to set up a new Laravel application to work with React as its frontend. While this article may focus on React, the main issues are the same regardless of framework. You’ll need to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Add your JavaScript application to the project’s file system and set up a build process for the frontend sources&lt;/li&gt;
&lt;li&gt;Write some additional code to bootstrap your frontend application once the page has loaded&lt;/li&gt;
&lt;li&gt;Carefully set up URL conventions to distinguish between frontend and backend routes.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;scaffolding-the-frontend&#34;&gt;Scaffolding The Frontend&lt;/h3&gt;
&lt;p&gt;In a standard Laravel 8 application (created using &lt;code&gt;composer create-project laravel/laravel &amp;lt;NAME&amp;gt;&lt;/code&gt;), the frontend JS application is stored in the &lt;code&gt;/resources/js&lt;/code&gt; folder. Laravel provides a helper package called &lt;a href=&#34;https://packagist.org/packages/laravel/ui&#34;&gt;laravel/ui&lt;/a&gt;, which can be used to scaffold the frontend with many popular frameworks, including React. To scaffold an empty React application, you can run the following:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bat&#34; data-lang=&#34;bat&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;composer require laravel/ui
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;php artisan ui react&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This will add a new folder &lt;code&gt;resources/js/components/&lt;/code&gt; with a single file called Example.js in it, which contains a basic stateless functional component called &lt;code&gt;Example&lt;/code&gt;. It’ll also add a new line to &lt;code&gt;resources/js/app.js&lt;/code&gt; that requires the &lt;code&gt;Example&lt;/code&gt; component. Finally, &lt;code&gt;webpack.mix.js&lt;/code&gt; will be updated to include adding React in the build. I’ll go over what this file does in the next section.&lt;/p&gt;
&lt;h3 id=&#34;compiling-assets-with-laravel-mix&#34;&gt;Compiling Assets With Laravel Mix&lt;/h3&gt;
&lt;p&gt;Laravel Mix is an npm package that comes bundled with every Laravel application. It’s not Laravel specific though; you can add it to any application where you want a simple build process. It defines helpers for popular frameworks, React included. The &lt;code&gt;mix.react()&lt;/code&gt; helper automatically handles adding in Babel to support using JSX syntax.
For Laravel, the frontend build process is configured in &lt;code&gt;webpack.mix.js&lt;/code&gt;. By default, it includes some scaffolding code that gives you a general idea of how it can be used:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;const&lt;/span&gt; mix = require(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;laravel-mix&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;mix
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  .js(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;resources/js/app.js&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;public/js&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  .react()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  .sass(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;resources/sass/app.scss&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;public/css&amp;#34;&lt;/span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;To run this build process, use the &lt;code&gt;npm run dev&lt;/code&gt; command. This will use laravel-mix to compile everything specified in &lt;code&gt;webpack.mix.js&lt;/code&gt;. The output directory for the build is also specified there. You can also start a basic development server by running &lt;code&gt;php artisan serve&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;This works just fine out of the box, but one thing worth noting is that by default, it’ll package all the code, including your dependencies, in the same file: &lt;code&gt;public/js/app.js&lt;/code&gt;. This will cause the entire dependency tree to be reloaded if you make even a single line change to your code. You can use the &lt;code&gt;mix.extract()&lt;/code&gt; helper to put the modules into a separate file, &lt;code&gt;public/js/vendor.js&lt;/code&gt;. This allows the browser to cache your dependencies, which won’t change too much, separately from your application, which will change much more often. Here’s how this looks in &lt;code&gt;webpack.mix.js&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;mix
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  .js(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;resources/js/app.js&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;public/js&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  .react()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  .extract([&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;react&amp;#34;&lt;/span&gt;])
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  .sass(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;resources/sass/app.scss&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;public/css&amp;#34;&lt;/span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Then, to actually include your built JavaScript sources, go to &lt;code&gt;views/welcome.blade.php&lt;/code&gt; and add them in the header, in this order:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-html&#34; data-lang=&#34;html&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;head&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  . . .
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#888&#34;&gt;&amp;lt;!-- Include Frontend Application (webpack mix) --&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &amp;lt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;script&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;defer&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;src&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;/js/manifest.js&amp;#34;&lt;/span&gt;&amp;gt;&amp;lt;/&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;script&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &amp;lt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;script&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;defer&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;src&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;/js/vendor.js&amp;#34;&lt;/span&gt;&amp;gt;&amp;lt;/&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;script&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &amp;lt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;script&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;defer&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;src&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;/js/app.js&amp;#34;&lt;/span&gt;&amp;gt;&amp;lt;/&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;script&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;/&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;head&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The order is important because each successive script depends on the content of the previous one being defined.&lt;/p&gt;
&lt;p&gt;Notice that all the script tags have the &lt;code&gt;defer&lt;/code&gt; attribute added to them. This forces the browser to wait until the DOM has fully loaded in order to execute the scripts. If you don’t add the &lt;code&gt;defer&lt;/code&gt; attribute, you’ll end up with a blank screen when you try to load the application. This happens because the browser will, by default, run your scripts as soon as they’re loaded. And, when they’re in the head section, they get loaded before the body. So, if the script loads before the body, the root element of the React application won’t be in the DOM yet, which in turn causes the application to fail to load.&lt;/p&gt;
&lt;h3 id=&#34;handling-frontend-routing&#34;&gt;Handling Frontend Routing&lt;/h3&gt;
&lt;p&gt;The next roadblock to tackle for setting up the frontend is routing. If you’re planning to have the frontend do its own routing, you’ll need to make sure that the backend routes don’t clash with the frontend ones. You’ll also need to make sure that, for all routes that the backend doesn’t recognize, it falls back to rendering the layout page that bootstraps the frontend, and not a 404 page. If you fail to do the latter, nested frontend routes won’t work if you navigate to them directly, or refresh the page after navigating from the root URL.&lt;/p&gt;
&lt;p&gt;One way to ensure the routes don’t clash is to add a prefix like &lt;code&gt;/app/&lt;/code&gt; for web routes. API routes already have the &lt;code&gt;/api/&lt;/code&gt; prefix set up by default, and shouldn’t pose any issues. Then, since all frontend routes won’t be recognized by Laravel, we’ll want to add a fallback route. The fallback route ensures that &lt;code&gt;welcome.blade.php&lt;/code&gt;, which contains our root React component Example, gets rendered instead of a 404 error page for all frontend routes. We can do this by using Laravel’s &lt;code&gt;Route::fallback()&lt;/code&gt; function in &lt;code&gt;/routes/web.php&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Route::&lt;span style=&#34;color:#369&#34;&gt;fallback&lt;/span&gt;(&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;function&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;return&lt;/span&gt; view(welcome);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;});&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Make sure you add this at the very bottom of &lt;code&gt;/routes/web.php&lt;/code&gt;, so that it’s the last route registered by your application. This is recommended by the Laravel docs and is also good practice since this route should be the last possible route to match any given URL.&lt;/p&gt;
&lt;h3 id=&#34;csrf-tokens&#34;&gt;CSRF Tokens&lt;/h3&gt;
&lt;p&gt;One other thing that’s important to mention is that by default Laravel has built-in features for generating and verifying CSRF tokens. This is set up in the &lt;code&gt;VerifyCsrfToken&lt;/code&gt; middleware class that comes bundled with a fresh application. It provides nice and easy helpers for Blade pages like &lt;code&gt;@csrf&lt;/code&gt; to ease adding this to your forms as a hidden input. However, if you’re making forms outside of Blade in React, you might receive an error page that says &lt;strong&gt;419 Page Expired&lt;/strong&gt; when you try to submit a form or send a request:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2021/05/integrating-laravel-with-a-react-frontend/419-page-expired.jpg&#34; alt=&#34;419 Page Expired Error&#34;&gt;&lt;/p&gt;
&lt;p&gt;This error happens for both vanilla HTML forms, and when sending a POST request via JavaScript, depending on the library being used. For example, I’ve encountered this issue when using &lt;strong&gt;jQuery&lt;/strong&gt;, but not &lt;strong&gt;axios&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;You can handle this in a few different ways. The easiest way is to simply add an exception for this route in your &lt;code&gt;VerifyCsrfToken&lt;/code&gt; class:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;VerifyCsrfToken&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;extends&lt;/span&gt; Middleware
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;/**
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;     * The URIs that should be excluded from CSRF verification.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;     *
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;     * @var array
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;     */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;protected&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$except&lt;/span&gt; = [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;/my-route&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;However, this removes CSRF protection entirely and in most cases, you’ll want the CSRF protection in your forms. This can be done by setting either &lt;code&gt;X-XSRF-TOKEN&lt;/code&gt; or &lt;code&gt;X-CSRF-TOKEN&lt;/code&gt; request headers, and also by adding a &lt;code&gt;_token&lt;/code&gt; property to the request parameters containing the CSRF token. It’s important to note that these similarly named values are not the same thing. The &lt;strong&gt;XSRF&lt;/strong&gt; token is just an encrypted version of the actual &lt;strong&gt;CSRF&lt;/strong&gt; token. Laravel 8 always sets the &lt;code&gt;XSRF-TOKEN&lt;/code&gt; cookie in the response headers by default:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2021/05/integrating-laravel-with-a-react-frontend/xsrf-token-cookie.jpg&#34; alt=&#34;XSRF-TOKEN Cookie&#34;&gt;&lt;/p&gt;
&lt;p&gt;This means that &lt;code&gt;XSRF-TOKEN&lt;/code&gt; is defined in &lt;code&gt;document.cookie&lt;/code&gt; when the page loads. By default, &lt;strong&gt;axios&lt;/strong&gt; (which is included with your new Laravel application) automatically looks for this value in the cookie, and adds it to the request headers.&lt;/p&gt;
&lt;h3 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;And that’s it! I’ve found Laravel works pretty well with a dedicated frontend once you get the initial setup out of the way. Have any questions? Feel free to leave a comment!&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Advanced WordPress customizations</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2020/11/advanced-wordpress-customizations/"/>
      <id>https://www.endpointdev.com/blog/2020/11/advanced-wordpress-customizations/</id>
      <published>2020-11-27T00:00:00+00:00</published>
      <author>
        <name>Juan Pablo Ventoso</name>
      </author>
      <content type="html">
        &lt;p&gt;&lt;img src=&#34;/blog/2020/11/advanced-wordpress-customizations/wordpress-logo-phone.jpg&#34; alt=&#34;WordPress&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.flickr.com/photos/cdharrison/4289847815/&#34;&gt;Photo&lt;/a&gt; by &lt;a href=&#34;https://www.flickr.com/photos/cdharrison/&#34;&gt;Chris Harrison&lt;/a&gt; on Flickr, &lt;a href=&#34;https://creativecommons.org/licenses/by/2.0/&#34;&gt;CC BY 2.0&lt;/a&gt;, cropped&lt;/p&gt;
&lt;p&gt;WordPress is the &lt;a href=&#34;https://www.isitwp.com/popular-cms-market-share/&#34;&gt;most popular CMS&lt;/a&gt;, allowing you to create a professional-looking website at a relatively low cost. It also has a really strong community behind it, creating great content and supporting developers across the world.&lt;/p&gt;
&lt;p&gt;But being popular also means being the main target of hacker attacks, and that’s why it’s crucial to keep the CMS, the theme, and all the plugins updated. When the requirements go beyond what WordPress offers on the surface, we need to find an efficient way to add our custom logic into the CMS without interfering with version upgrades, keeping the focus on security.&lt;/p&gt;
&lt;h3 id=&#34;custom-css-and-javascript&#34;&gt;Custom CSS and JavaScript&lt;/h3&gt;
&lt;p&gt;A pretty common scenario in WordPress consists of installing a theme that fits most of our requirements and writing an extra layer of functionality over it to get that custom look and user experience we are looking for. Updating the theme files means that we cannot easily upgrade or change the theme without backing up our changes, and then restoring them after the upgrade, so that’s definitely not a good approach.&lt;/p&gt;
&lt;p&gt;To make our way around this issue, some themes offer a section to add custom JavaScript or CSS rules. But sometimes we need change themes in the middle of our developing process, so I usually rely on plugins to make my frontend changes. One simple, straightforward plugin I usually install to add custom styles and frontend scripts is &lt;a href=&#34;https://wordpress.org/plugins/custom-css-js/&#34;&gt;Simple Custom CSS and JS&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2020/11/advanced-wordpress-customizations/wordpress-simple-custom-css-js.jpg&#34; alt=&#34;Simple Custom CSS and JS&#34;&gt;&lt;/p&gt;
&lt;p&gt;It has several customization options I usually need for SEO purposes, including the possibility to create several independent code segments and load each one in a different section (header/​footer) to improve loading speed. We can also include our custom content as embedded source/​styles or as external references. It also includes an editor with syntax highlighting, and allows adding custom content to the WordPress admin section.&lt;/p&gt;
&lt;h3 id=&#34;custom-php-code&#34;&gt;Custom PHP code&lt;/h3&gt;
&lt;p&gt;Another thing I usually need when customizing a WordPress website is the ability to run my own PHP code inside a WordPress page or post. That is not allowed by the CMS by default, but like most things in WordPress, there is a plugin that will make our lives easier: It’s called &lt;a href=&#34;https://wordpress.org/plugins/insert-php-code-snippet/&#34;&gt;Insert PHP Code Snippet&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;You can create your own PHP routines as snippets that can be inserted with shortcodes into a WordPress page, posts, template, or whenever you can add a shortcode. This will allow running any custom backend code anywhere on the website.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2020/11/advanced-wordpress-customizations/wordpress-custom-php-snippet.jpg&#34; alt=&#34;Custom PHP Code Snippet&#34;&gt;&lt;/p&gt;
&lt;p&gt;To run the code on a page or post, all that needs to be done is pasting the shortcode on the content with the “PHP” button that appears on the button bar on the editor:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2020/11/advanced-wordpress-customizations/wordpress-custom-php-snippet-shortcode.jpg&#34; alt=&#34;Shortcode example&#34;&gt;&lt;/p&gt;
&lt;h3 id=&#34;custom-hooks&#34;&gt;Custom hooks&lt;/h3&gt;
&lt;p&gt;When our logic needs to be fired up from an event on the CMS, or if we need to make changes to the default WordPress behavior or data, we will need to use the &lt;a href=&#34;https://developer.wordpress.org/reference/functions/add_action/&#34;&gt;add_action() function&lt;/a&gt; or the &lt;a href=&#34;https://developer.wordpress.org/reference/functions/add_filter/&#34;&gt;add_filter() function&lt;/a&gt;.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;add_action()&lt;/code&gt; allows us to execute a PHP function on specific points of the WordPress execution, for example when a post is created or commented, or when a user is created. A full list of actions is available &lt;a href=&#34;https://codex.wordpress.org/Plugin_API/Action_Reference&#34;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;add_filter()&lt;/code&gt; allows us to update information associated with WordPress, for example, to set a custom CSS class for the body or to replace the login button text. A full list of filters is available &lt;a href=&#34;https://codex.wordpress.org/Plugin_API/Filter_Reference&#34;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The following example sends an email to the webmaster when a new comment is created, using the &lt;code&gt;add_action()&lt;/code&gt; function:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;email_comment&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  wp_mail(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;webmaster@website.com&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;New comment&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;New comment posted on the website&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;add_action(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;comment_post&amp;#39;&lt;/span&gt;,&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;email_comment&amp;#39;&lt;/span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Here’s another example that allows to perform a string replacement across the website by using the &lt;code&gt;add_filter()&lt;/code&gt; function:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;replace_text&lt;/span&gt;(&lt;span style=&#34;color:#369&#34;&gt;$text&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;return&lt;/span&gt; str_replace(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;Text to search&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;Text to replace with&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#369&#34;&gt;$text&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;add_filter(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;gettext&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;replace_text&amp;#39;&lt;/span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We can add these code segments using the PHP Snippet plugin (recommended) or using the &lt;code&gt;functions.php&lt;/code&gt; file included in our theme. This last option is not recommended since it has the difficulty we discussed above about missing our custom content after upgrading the theme.&lt;/p&gt;
&lt;h3 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;WordPress is a great CMS with an elegant backend and a big list of themes and plugins we can use. But before we start adding custom code, we need to make sure it will persist after upgrading everything else to the latest version.&lt;/p&gt;
&lt;p&gt;The techniques we saw in this post are meant for websites that only need some small custom changes or improvements. If we need to include a complete layer of logic into a website, it’s always recommended to write a custom plugin from scratch, if there is nothing out there that serves the purpose. But that’s material for another blog post!&lt;/p&gt;
&lt;p&gt;At End Point, we build web solutions in WordPress and many other technologies, keeping security and high standards in mind. &lt;a href=&#34;/contact/&#34;&gt;Contact us&lt;/a&gt; if you want to find out more.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Craft: A CMS for developers</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2020/10/craft-a-cms-for-developers/"/>
      <id>https://www.endpointdev.com/blog/2020/10/craft-a-cms-for-developers/</id>
      <published>2020-10-31T00:00:00+00:00</published>
      <author>
        <name>Kevin Campusano</name>
      </author>
      <content type="html">
        &lt;p&gt;&lt;img src=&#34;/blog/2020/10/craft-a-cms-for-developers/banner.png&#34; alt=&#34;Craft CMS banner&#34;&gt;&lt;/p&gt;
&lt;p&gt;As a software engineer, I thrive and thoroughly enjoy working on fully custom software products, applications conceived to model and help in the execution of some business process and that are built from the ground up by a team of developers.&lt;/p&gt;
&lt;p&gt;Such projects are often complex and expensive, though, and for some clients, they can be overkill. Some clients come up with requirements that are better served by off-the-shelf software solutions. One group of such solutions are &lt;a href=&#34;https://en.wikipedia.org/wiki/Content_management_system&#34;&gt;content management systems (CMS)&lt;/a&gt;. As a rule of thumb, if a client wants a website whose main purpose is to showcase some content, their brand or image, and custom business logic requirements are limited, then chances are that a CMS will fit the bill nicely.&lt;/p&gt;
&lt;p&gt;Lately we’ve been using the &lt;a href=&#34;https://craftcms.com/&#34;&gt;Craft CMS&lt;/a&gt; for a client that meets the aforementioned criteria, and I gotta say, I’ve been pleasantly surprised by the developer experience it offers.&lt;/p&gt;
&lt;p&gt;Unlike most of the technology and products we discuss in our blog, Craft CMS is not &lt;a href=&#34;https://opensource.org/osd&#34;&gt;Open Source&lt;/a&gt; or &lt;a href=&#34;https://www.gnu.org/philosophy/free-sw.html&#34;&gt;Free Software&lt;/a&gt;. The source code is readily available in &lt;a href=&#34;https://github.com/craftcms/cms&#34;&gt;GitHub&lt;/a&gt; for anybody to use, study, and modify, but commercial use of it is restricted and certain features are exclusive to a so-called “Pro” edition. Learn more by reading their &lt;a href=&#34;https://github.com/craftcms/cms/blob/develop/LICENSE.md&#34;&gt;license&lt;/a&gt; and their &lt;a href=&#34;https://craftcms.com/pricing&#34;&gt;pricing structure&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The features that we will discuss in this article are all part of the no-charge “Solo” edition of Craft CMS 3 that can be used for noncommercial websites.&lt;/p&gt;
&lt;p&gt;In this article I’m going to talk through a few of the key aspects of Craft that make me think that it’s really a CMS made for developers. Let’s get started:&lt;/p&gt;
&lt;h3 id=&#34;craft-is-easy-to-get-up-and-running&#34;&gt;Craft is easy to get up and running&lt;/h3&gt;
&lt;p&gt;Craft is just a PHP application. And it is as typical as modern PHP applications go, capable of being initially set up with Composer and of running on top of a MySQL database (it also supports Postgres!) and the Apache web server. &lt;a href=&#34;https://craftcms.com/docs/3.x/console-commands.html&#34;&gt;It can all be done via console&lt;/a&gt; too, if that’s how you roll.&lt;/p&gt;
&lt;p&gt;If you already have a box with Apache, PHP, MySQL and Composer, it all amounts to little more than creating a MySQL database for Craft, &lt;code&gt;composer install&lt;/code&gt;ing the Craft package, sorting out some permissions, running &lt;code&gt;php craft setup&lt;/code&gt;, following the prompts, and finally, configuring a virtual host in Apache to serve the &lt;code&gt;web&lt;/code&gt; directory from inside where Craft was installed.&lt;/p&gt;
&lt;p&gt;All of this is explained in Craft’s &lt;a href=&#34;https://craftcms.com/docs/3.x/installation.html&#34;&gt;official documentation&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&#34;craft-is-easy-to-put-in-containers&#34;&gt;Craft is easy to put in containers&lt;/h3&gt;
&lt;p&gt;For ease of development and project bootstrapping, I’ve created a containerized setup with Docker and &lt;a href=&#34;https://docs.docker.com/compose/&#34;&gt;Docker Compose&lt;/a&gt; that encapsulates some infrastructure tailored to my development needs. You can get the relevant files &lt;a href=&#34;https://github.com/megakevin/craft-cms-docker-bootstrap&#34;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If you want to follow along, clone that repo, and you’ll end up with this file structure (as shown by the &lt;code&gt;tree&lt;/code&gt; command):&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;.
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;├── apache_config
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;│   └── 000-default.conf
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;├── docker-compose.yml
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;├── Dockerfile
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;└── README.md
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;1 directory, 4 files&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This setup includes two containers: one for running Apache and Craft, and another for running MySQL. The &lt;code&gt;apache_config/000-default.conf&lt;/code&gt; contains some Apache VirtualHost configuration for serving the site. &lt;code&gt;docker-compose.yml&lt;/code&gt; defines the whole infrastructure: both containers, a network that they use to talk to each other, and a volume to persist MySQL database files. The &lt;code&gt;Dockerfile&lt;/code&gt; is the definition of the image for the container that runs Apache and Craft.&lt;/p&gt;
&lt;p&gt;Feel free to explore the files; I’ve made sure to comment them so that they are easy to understand and modify as you see fit.&lt;/p&gt;
&lt;p&gt;Note: If you want to run this setup, be sure to change the &lt;code&gt;ServerAdmin&lt;/code&gt; value in &lt;code&gt;apache_config/000-default.conf&lt;/code&gt;, and the &lt;code&gt;USER&lt;/code&gt;, &lt;code&gt;UID&lt;/code&gt;, and &lt;code&gt;GID&lt;/code&gt; values in &lt;code&gt;docker-compose.yml&lt;/code&gt; under &lt;code&gt;services &amp;gt; web &amp;gt; build &amp;gt; args&lt;/code&gt; according to your environment and user account information.&lt;/p&gt;
&lt;p&gt;If you have Docker and Docker Compose installed in your machine, you can go to the directory just created by the &lt;code&gt;git clone&lt;/code&gt; and:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Run &lt;code&gt;docker-compose up&lt;/code&gt; to set up the infrastructure. You will see Docker and Docker Compose creating the image defined in &lt;code&gt;Dockerfile&lt;/code&gt; and the containers defined in &lt;code&gt;docker-compose.yml&lt;/code&gt;. Then the logs of the various containers will start showing. It you want to run this in the background, use &lt;code&gt;docker-compose up -d&lt;/code&gt; instead and it will give you control of the terminal immediately after it’s done.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Run &lt;code&gt;docker-compose exec web bash&lt;/code&gt; to connect to the &lt;code&gt;web&lt;/code&gt; container. This is the container that has Craft’s code and is running Apache. You’ll be “logged into” the container and be placed in &lt;code&gt;/var/www&lt;/code&gt;. This is the directory where we will install Craft.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Once in there, run &lt;code&gt;composer create-project craftcms/craft ./install&lt;/code&gt; to install Craft with Composer. In other words, it will download all of the files that Craft needs to run. You should see something like this at the end:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;gt; @php craft setup/welcome
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   ______ .______          ___       _______ .___________.
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  /      ||   _  \        /   \     |   ____||           |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; |  ,----&amp;#39;|  |_)  |      /  ^  \    |  |__   `---|  |----`
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; |  |     |      /      /  /_\  \   |   __|      |  |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; |  `----.|  |\  \----./  _____  \  |  |         |  |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  \______|| _| `._____/__/     \__\ |__|         |__|
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;     A       N   E   W       I   N   S   T   A   L   L
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;               ______ .___  ___.      _______.
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;              /      ||   \/   |     /       |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;             |  ,----&amp;#39;|  \  /  |    |   (----`
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;             |  |     |  |\/|  |     \   \
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;             |  `----.|  |  |  | .----)   |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;              \______||__|  |__| |_______/
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Generating an application ID ... done (CraftCMS--xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Generating a security key ... done (xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Welcome to Craft CMS! Run the following command if you want to setup Craft from your terminal:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    /var/www/install/craft setup&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;After that’s done, use this bit of bash black magic:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(&lt;span style=&#34;color:#038&#34;&gt;shopt&lt;/span&gt; -s dotglob; mv -v ./install/* .)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This command moves all the files that composer just downloaded into &lt;code&gt;/var/www/install&lt;/code&gt; out from there and into &lt;code&gt;/var/www&lt;/code&gt;. Then &lt;code&gt;rmdir install&lt;/code&gt; to remove the &lt;code&gt;install&lt;/code&gt; directory because we no longer need it.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Now do &lt;code&gt;php craft setup&lt;/code&gt; to use Craft’s CLI to set up the site, its configuration, and its database structure. Just follow the prompts. When it asks you for database configuration, choose &lt;code&gt;mysql&lt;/code&gt; as the database driver, set &lt;code&gt;mysql&lt;/code&gt; as the database server name (because that’s the name we’ve given it in our &lt;code&gt;docker-compose.yml&lt;/code&gt;), and use the environment variables defined in &lt;code&gt;docker-compose.yml&lt;/code&gt; around lines 14 to 17 for the rest of the values. You can change these to whatever you want as long as you make sure that it coincides with how you defined your MySQL database container in the &lt;code&gt;docker-compose.yml&lt;/code&gt; file. The prompts should look something like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Which database driver are you using? [mysql,pgsql,?]: mysql
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Database server name or IP address: [127.0.0.1] mysql
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Database port: [3306]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Database username: [root] craft
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Database password:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Database name: craft_demo
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Database table prefix:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Testing database credentials ... success!
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Saving database credentials to your .env file ... done&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Next, sort out some Craft file permission requirements with this command:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;chmod -R o+w config storage web/cpresources&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;These are directories that Craft needs write access to.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Now start up Apache with &lt;code&gt;sudo service apache2 start&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;And that’s it! Open a browser to &lt;code&gt;localhost&lt;/code&gt; or &lt;code&gt;127.0.0.1&lt;/code&gt; and you should see your Craft 3 homepage:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2020/10/craft-a-cms-for-developers/welcome_to_craft.jpg&#34; alt=&#34;Welcome to Craft browser screenshot&#34;&gt;&lt;/p&gt;
&lt;p&gt;You can start playing with the control panel or the &lt;code&gt;templates/index.twig&lt;/code&gt; right away.&lt;/p&gt;
&lt;h3 id=&#34;crafts-design-makes-sense&#34;&gt;Craft’s design makes sense&lt;/h3&gt;
&lt;p&gt;When it comes to content modeling, Craft offers a set of abstractions that make sense. The main concepts to understand are &lt;a href=&#34;https://craftcms.com/docs/3.x/entries.html&#34;&gt;sections and entries&lt;/a&gt;. Entries are the main pieces of content. An “article” in a news site or a “post” in a blog. Sections are the way Craft groups entries together. They are useful when your site has multiple streams of content. You can, for example, have a site where you publish news, opinion pieces, and random thoughts. With Craft, that would translate neatly into three separate sections, each one with its own type of entries.&lt;/p&gt;
&lt;p&gt;Craft also allows you to set up &lt;a href=&#34;https://craftcms.com/docs/3.x/fields.html&#34;&gt;custom fields&lt;/a&gt; for every type of entry. For example, the entries on your news section may need to include a link to the original source of the news, while your opinion pieces need a short description instead. You can configure your entries using custom fields so that they include the data that makes sense for your use case.&lt;/p&gt;
&lt;p&gt;Let’s see what that looks like in concrete terms. Now that we have a Craft instance running in &lt;code&gt;localhost&lt;/code&gt;, go to &lt;code&gt;localhost/admin&lt;/code&gt; in your browser. You should see Craft’s control panel. Click on the “Settings” option in the navigation bar to the left of the screen, then select the “Sections” item under “Content”:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2020/10/craft-a-cms-for-developers/control_panel_settings_sections.png&#34; alt=&#34;Screenshot of Control Panel &amp;gt; Settings &amp;gt; Sections&#34;&gt;&lt;/p&gt;
&lt;p&gt;Next, click on the “+ New Section” button by the top of the screen and you’ll be shown the section creation form. We will create a “News” section so let’s fill in the form like this:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2020/10/craft-a-cms-for-developers/control_panel_new_section.png&#34; alt=&#34;Screenshot of Create a new section&#34;&gt;&lt;/p&gt;
&lt;p&gt;The “Name” and “Handle” fields are pretty self explanatory. The “&lt;a href=&#34;https://craftcms.com/docs/3.x/entries.html#section-types&#34;&gt;Section Type&lt;/a&gt;” is a concept we haven’t discussed yet. “&lt;a href=&#34;https://craftcms.com/docs/3.x/entries.html#channels&#34;&gt;Channel&lt;/a&gt;” is the most appropriate for a news section, which is a stream of multiple entries with the same structure.&lt;/p&gt;
&lt;p&gt;There are other types: “&lt;a href=&#34;https://craftcms.com/docs/3.x/entries.html#singles&#34;&gt;Single&lt;/a&gt;” is a type which you would use for entries that are unique, like a home or contact page. For sections of type “Single”, there’s generally one single entry that fits in them. This is unlike “Channels” which fit multiple entries. The other section type is “&lt;a href=&#34;https://craftcms.com/docs/3.x/entries.html#structures&#34;&gt;Structure&lt;/a&gt;”, which also accommodates multiple entries, but rather than a stream of ever-growing content, it’s more appropriate for similar entries that share a certain theme. A “Structure” section type is appropriate for things like services offered or projects in a portfolio.&lt;/p&gt;
&lt;p&gt;Learn all about entries, sections, section types, and more in &lt;a href=&#34;https://craftcms.com/docs/3.x/entries.html&#34;&gt;Craft’s official docs&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Now that we’ve filled the form, click the “Save and edit entry types” button. This has created our new “News” section and defined the “&lt;a href=&#34;https://craftcms.com/docs/3.x/entries.html#entry-types&#34;&gt;entry type&lt;/a&gt;” that this section will be able to contain. The control panel now shows this:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2020/10/craft-a-cms-for-developers/control_panel_entry_type.png&#34; alt=&#34;Screenshot of The section’s default entry type&#34;&gt;&lt;/p&gt;
&lt;p&gt;In Craft, a section can contain multiple types of entries. For our purposes with the news section, though, just the default one is enough. Click on it, and you’ll see an editor where you can select fields that make up that entry type:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2020/10/craft-a-cms-for-developers/control_panel_entry_type_fields.png&#34; alt=&#34;Screenshot of The entry type’s fields&#34;&gt;&lt;/p&gt;
&lt;p&gt;The editor I mentioned before is below the “Field Layout” title. Here’s where we can pick and choose which fields make up the entries for the “News” section. We have a fresh installation of Craft though, so we don’t have any fields. Let’s create a few by going to Settings &amp;gt; Fields.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2020/10/craft-a-cms-for-developers/control_panel_new_fields.png&#34; alt=&#34;Screenshot of Defining new fields&#34;&gt;&lt;/p&gt;
&lt;p&gt;This is where we can define new fields to be used for our entries throughout the site. Click the “+ New Field” button near the top of the screen and you’ll be presented with the field creation form where you can specify all manner of details. For now, we just care about “Name”, “Handle” and “Field Type”. Let’s create three fields:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;One named “Heading” with a type of “Plain Text”.&lt;/li&gt;
&lt;li&gt;One named “Body” with a type of “Plain Text”.&lt;/li&gt;
&lt;li&gt;One named “Source” with a type of “URL”.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;You should end up with something like this in the control panel’s “Fields” page (&lt;code&gt;localhost/admin/settings/fields&lt;/code&gt;):&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2020/10/craft-a-cms-for-developers/control_panel_three_fields.png&#34; alt=&#34;Screenshot of New fields ready&#34;&gt;&lt;/p&gt;
&lt;p&gt;Now, if we go back to our “News” section’s default entry type at &lt;code&gt;http://localhost/admin/settings/sections/1/entrytypes/1&lt;/code&gt; or Settings &amp;gt; Sections &amp;gt; News &amp;gt; Entry Types &amp;gt; News…&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2020/10/craft-a-cms-for-developers/control_panel_field_layout.png&#34; alt=&#34;Screenshot of New fields for News entries&#34;&gt;&lt;/p&gt;
&lt;p&gt;You can see how the new fields that we just created are present in the “Field Layout” panel. In order to make these fields available for our “News” entries, we just need to drag them into the box named “Content” inside the greyish area.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2020/10/craft-a-cms-for-developers/control_panel_field_layout_applied.png&#34; alt=&#34;Screenshot of New fields for News entries assigned&#34;&gt;&lt;/p&gt;
&lt;p&gt;Click the “Save” button at the top, and that’s all it takes to set up a “Channel” section, an entry type for it, and a few fields.&lt;/p&gt;
&lt;p&gt;Now that we’ve set up the blueprints for them, let’s actually create a few entries in the “News” section. To do so, click on the “Entries” link in the navigation bar to the left which should’ve revealed itself by now, and you’ll see this screen:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2020/10/craft-a-cms-for-developers/control_panel_entries.png&#34; alt=&#34;Screenshot of The entries screen&#34;&gt;&lt;/p&gt;
&lt;p&gt;If you’re used to CMS back ends, this is pretty familiar. In this screen you can create new entries and browse existing ones.&lt;/p&gt;
&lt;p&gt;Click the big red “+ New Entry” button and select “News” in the resulting pop-up menu. You should see a form with the fields that we defined in the “Field Layout” panel during previous steps. Feel free to create a few news entries. I’ve created these two:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2020/10/craft-a-cms-for-developers/control_panel_entries_created.png&#34; alt=&#34;Screenshot of Entries created&#34;&gt;&lt;/p&gt;
&lt;h3 id=&#34;craft-gives-you-complete-freedom-over-your-front-end&#34;&gt;Craft gives you complete freedom over your front end&lt;/h3&gt;
&lt;p&gt;Most CMSs can be thought of as having two components: a front end and a back end. The back end is where content is authored and the front end is where the style and structure in which the content is presented. In Craft, most of the effort has gone into creating a solid, highly customizable back end.&lt;/p&gt;
&lt;p&gt;As we’ve just seen, Craft comes out of the box with a back end control panel where site administrators and content creators can author new content. As far as front end goes though, Craft has nothing. For a developer well versed in front end web technologies, this is freeing and transformative.&lt;/p&gt;
&lt;p&gt;Craft makes no assumption and makes no decision for you when it comes to developing your site’s look and feel. It gets out of your way and lets you do your job. There’s no concept of “theme”. There’s no obscure framework to learn and integrate into. There’s no proprietary templating language to struggle with. In Craft, you are completely free to write HTML, CSS and JS as you see fit to obtain your desired effect for your site.&lt;/p&gt;
&lt;p&gt;You can develop templates using the tried and true &lt;a href=&#34;https://twig.symfony.com/&#34;&gt;Twig&lt;/a&gt; templating engine, which, if you have some experience with PHP, you’ve most likely already encountered and worked with. All the content created in the back end is exposed to the Twig templates via objects. Let’s see how.&lt;/p&gt;
&lt;p&gt;First we need to specify a template for our sections. Continuing with our example, let’s assign a template to our “News” section. Go to Settings &amp;gt; Sections &amp;gt; News and scroll down to find the “Site Settings” area. In the table there, type &lt;code&gt;news&lt;/code&gt; into the “Template” column. Now go to the &lt;code&gt;template&lt;/code&gt; directory where craft was installed and create a new &lt;code&gt;news.twig&lt;/code&gt; file. The contents can be simple, like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-html&#34; data-lang=&#34;html&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;html&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;lang&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;en&amp;#34;&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;head&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;meta&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;charset&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;UTF-8&amp;#34;&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;meta&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;name&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;viewport&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;content&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;width=device-width, initial-scale=1.0&amp;#34;&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;title&lt;/span&gt;&amp;gt;{{ entry.title }} - My Craft Demo&amp;lt;/&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;title&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;/&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;head&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;body&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;h1&lt;/span&gt;&amp;gt;{{ entry.heading }}&amp;lt;/&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;h1&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;p&lt;/span&gt;&amp;gt;{{ entry.body }}&amp;lt;/&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;p&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;p&lt;/span&gt;&amp;gt;&amp;lt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;a&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;href&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;{{ entry.source }}&amp;#34;&lt;/span&gt;&amp;gt;Source&amp;lt;/&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;a&lt;/span&gt;&amp;gt;&amp;lt;/&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;p&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;/&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;body&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;/&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;html&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The only noteworthy aspect of this template is how we are injecting the data that we defined in the back end into this template. We use double curly brackets to reference the &lt;code&gt;entry&lt;/code&gt; variable. This is provided to Twig by Craft and contains all the fields that we defined for our entries in the “News” section.&lt;/p&gt;
&lt;p&gt;With that done, save and visit any of the entries you created and you’ll see something like this:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2020/10/craft-a-cms-for-developers/website_entry.png&#34; alt=&#34;Screenshot of Our first entry&#34;&gt;&lt;/p&gt;
&lt;p&gt;As you can see, this entry’s URL is &lt;code&gt;localhost/news/i-just-learned-that-craft-uses-twig&lt;/code&gt;. Yours will obviously differ depending on the title (and slug) that you gave them.&lt;/p&gt;
&lt;p&gt;What this example lacks in complexity, it more than makes up for in potential. This is a plain old HTML document that we’ve created, with a Twig template, of course. This is the complete freedom that I like about Craft. From this point on, you can do whatever you want in terms of front end development: use whatever CSS or JavaScript framework or library you want, organize your template files in a way that makes sense to you, your team, and your website, etc. The sky is the limit.&lt;/p&gt;
&lt;p&gt;That’s good for individual news pages. But now let’s try to link to them from the homepage. To do so, we need to edit the &lt;code&gt;templates/index.twig&lt;/code&gt; file. Around line 174, remove the &lt;code&gt;&amp;lt;ul&amp;gt;&lt;/code&gt; that’s there along with all its &lt;code&gt;&amp;lt;li&amp;gt;&lt;/code&gt;s and put this instead:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-html&#34; data-lang=&#34;html&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{% set entries = craft.entries().section(&amp;#39;news&amp;#39;).all() %}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;ul&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {% for entry in entries %}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &amp;lt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;li&lt;/span&gt;&amp;gt;&amp;lt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;a&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;href&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;{{ entry.url }}&amp;#34;&lt;/span&gt;&amp;gt;{{ entry.title }}&amp;lt;/&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;a&lt;/span&gt;&amp;gt;&amp;lt;/&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;li&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {% endfor %}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;/&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;ul&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Here, we leverage Twig’s templating engine capabilities, sprinkled with some of Craft’s features to obtain a list of all the entries in our “News” section. Then, we iterate over them to render links.&lt;/p&gt;
&lt;p&gt;Effectively, Craft enhances what you can do with Twig by exposing an API for accessing the data that exists in the CMS back end.&lt;/p&gt;
&lt;p&gt;If you’re familiar with any sort of templating language like those included in most web application frameworks like &lt;a href=&#34;https://rubyonrails.org/&#34;&gt;Ruby on Rails&lt;/a&gt;, &lt;a href=&#34;https://symfony.com/&#34;&gt;Symfony&lt;/a&gt;, &lt;a href=&#34;https://docs.microsoft.com/en-us/aspnet/core/mvc/overview?view=aspnetcore-3.1&#34;&gt;ASP.NET Core MVC&lt;/a&gt;, etc., you’ll probably feel right at home with this.&lt;/p&gt;
&lt;p&gt;Here’s what the homepage looks like now:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2020/10/craft-a-cms-for-developers/homepage_with_links.jpg&#34; alt=&#34;Screenshot of Homepage is ready&#34;&gt;&lt;/p&gt;
&lt;p&gt;You can click on any of the links and they will take you to the specific entry page.&lt;/p&gt;
&lt;p&gt;You can learn more about querying entries in Craft’s &lt;a href=&#34;https://craftcms.com/docs/3.x/entries.html#editing-entries&#34;&gt;official documentation&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&#34;craft-is-cool-&#34;&gt;Craft is cool 🕶️&lt;/h3&gt;
&lt;p&gt;So, in conclusion, I’ve found that Craft is a cool tool to have in the toolbox. It is a full-fledged CMS with tons of customization opportunities for how to model and organize the content and data of your site. When it comes to developing the front end, though, it gets out of your way and lets you do your job. That, to me, is a big win.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>An introduction to automated testing for web applications with Symfony</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2020/09/automated-testing-with-symfony/"/>
      <id>https://www.endpointdev.com/blog/2020/09/automated-testing-with-symfony/</id>
      <published>2020-09-22T00:00:00+00:00</published>
      <author>
        <name>Kevin Campusano</name>
      </author>
      <content type="html">
        &lt;p&gt;&lt;img src=&#34;/blog/2020/09/automated-testing-with-symfony/banner.png&#34; alt=&#34;Banner&#34;&gt;&lt;/p&gt;
&lt;p&gt;Testing is an immense topic in software engineering. A lot has been written and a lot of experience has been collected on it by the greater software development community. There are many different tests, techniques, approaches, philosophies, strategies.&lt;/p&gt;
&lt;p&gt;With such a big topic, it would be futile to try touching on every aspect of it in this article. Instead, I’ll try to take a pragmatic approach and discuss a testing strategy I’ve found success with in the past as well how much testing is necessary before I feel comfortable putting code into production. This article could also serve as a sort of introduction to automated testing using the &lt;a href=&#34;https://symfony.com/&#34;&gt;Symfony framework&lt;/a&gt; as a vehicle to explore various types of testing without diving too deep into edge cases or framework specifics, and instead leaning more into the concepts and design decisions that go into writing them. Still, we’ll make sure to have a running and competent test suite by the end.&lt;/p&gt;
&lt;p&gt;So we’re going to talk about automated testing, which in its own right is a very important part of the larger discipline of software testing. It’s also a topic that, as a developer (and as such, responsible for implementing this type of tests), I’m passionate about.&lt;/p&gt;
&lt;p&gt;Let’s get started.&lt;/p&gt;
&lt;h3 id=&#34;the-types-of-tests-were-going-to-write&#34;&gt;The types of tests we’re going to write&lt;/h3&gt;
&lt;p&gt;For web applications, as far as automated tests go, there are three categories I think are essential to have and which complement each other very well:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&#34;https://en.wikipedia.org/wiki/Unit_testing&#34;&gt;Unit tests&lt;/a&gt;: These are the most numerous, low-level, and, in my opinion, the most important type of developer tests. Unit tests don’t only make sure that the system does what it is supposed to do, but also that it’s correctly factored where individual components are decoupled. Unit tests focus on exercising specific classes and methods running in complete isolation, which becomes harder if the class you want to test is tightly coupled with its dependencies/​collaborators. These tests validate the behavior of basic programming constructs like classes and the algorithms within them. They need to be small and fast, since their intent is to be run frequently by developers as they implement features and change existing ones.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&#34;https://martinfowler.com/bliki/IntegrationTest.html&#34;&gt;Integration tests&lt;/a&gt;: These tests go one level of abstraction higher when compared to unit tests. They make sure that independently developed components work properly together. In web apps, I like my integration tests to focus primarily on how the system interacts with external components. In this article, we’re going to use integration-level tests to validate functionality that has to do with interaction with a database and an external Web API.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&#34;https://en.wikipedia.org/wiki/Functional_testing&#34;&gt;Functional tests&lt;/a&gt;: These are the tests at the highest level of abstraction. These try to closely mimic the user’s experience with the system by interacting with the app as a user would. In terms of a web app, this means making HTTP requests, clicking buttons, filling out and submitting forms, inspecting HTML results, etc. As these tests exercise the complete system end to end, they are allowed to be a bit slower. This is not an issue because they are not run as frequently as unit or integration tests.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If we can build an automated testing suite that provides good coverage and exercises the system at these three levels, I will feel confident that the &lt;a href=&#34;https://en.wikipedia.org/wiki/System_under_test&#34;&gt;system under test&lt;/a&gt; can work properly in production. An added bonus is that with tests like these, we would have a live documentation of the system, the features it provides and, to an extent, how it works.&lt;/p&gt;
&lt;p&gt;Virtually all serious software development ecosystems have at least one automated testing framework or library which we can leverage to write our tests. For our purposes in this article, we’re going to be using the Symfony PHP framework which integrates beautifully with &lt;a href=&#34;https://phpunit.de/&#34;&gt;PHPUnit&lt;/a&gt; to provide developers with an effective and even fun way to write tests.&lt;/p&gt;
&lt;h3 id=&#34;getting-to-know-the-system-under-test&#34;&gt;Getting to know the system under test&lt;/h3&gt;
&lt;p&gt;To help illustrate the topic I’ve prepared a very straightforward weather app. It only offers one feature: it will allow the user to see the current weather of a given city in the US. It does this by presenting a form where people can type in a city and a state, submit it, and get their information back.&lt;/p&gt;
&lt;p&gt;The app obtains this information by contacting the &lt;a href=&#34;https://openweathermap.org/&#34;&gt;OpenWeatherMap Web API&lt;/a&gt;. It also stores all requests in a database for posterity.&lt;/p&gt;
&lt;p&gt;The site is a typical Symfony web app. It uses the &lt;a href=&#34;https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller&#34;&gt;MVC&lt;/a&gt; pattern and &lt;a href=&#34;https://martinfowler.com/bliki/DomainDrivenDesign.html&#34;&gt;Domain Driven Design&lt;/a&gt; concepts like &lt;a href=&#34;https://martinfowler.com/bliki/EvansClassification.html&#34;&gt;entities&lt;/a&gt;, &lt;a href=&#34;https://martinfowler.com/eaaCatalog/repository.html&#34;&gt;repositories&lt;/a&gt; and &lt;a href=&#34;https://martinfowler.com/bliki/EvansClassification.html&#34;&gt;services&lt;/a&gt;. Here’s a diagram explaining the static structure of the app:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2020/09/automated-testing-with-symfony/static-structure.png&#34; alt=&#34;Static Structure&#34;&gt;&lt;/p&gt;
&lt;p&gt;For a simple app like this, our entities are little more than containers for our data. The repositories take care of encapsulating the database access logic, and the services contain the logic that leverages the other objects to fulfill our business requirements.&lt;/p&gt;
&lt;p&gt;The front end, as you’ll see, is super simple. Not really any client-side JavaScript logic to speak of, so this is more of an old-school, back-end-only app. Good enough for what we’re trying to do here though.&lt;/p&gt;
&lt;p&gt;So our only use case is the current weather request. We do, however, have a couple of alternate scenarios within that use case. First, if the user types in an invalid US state code, the app will show a validation error. Second, if the user inputs a city that the OpenWeatherMap Web API can’t find, the app will show another error message.&lt;/p&gt;
&lt;p&gt;To get a better idea of how the classes interact with each other, here’s a sequence diagram detailing how the app serves the main weather query scenario:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2020/09/automated-testing-with-symfony/sequence-diagram.png&#34; alt=&#34;Sequence diagram&#34;&gt;&lt;/p&gt;
&lt;p&gt;As you can see, the controller receives the request and calls upon the service class to validate the input and retrieve the information for the city specified by the user. Then, the service takes care of orchestrating the other objects like entities, repositories, and other services to fulfill the request and return back the weather information that eventually gets rendered with the template.&lt;/p&gt;
&lt;p&gt;You can explore the source code for our demo app &lt;a href=&#34;https://github.com/megakevin/end-point-blog-automated-testing-with-symfony&#34;&gt;here&lt;/a&gt;. The interesting files are under the &lt;code&gt;src&lt;/code&gt; and &lt;code&gt;template&lt;/code&gt; directories. Their contents should be self-explanatory: &lt;code&gt;src/Controller&lt;/code&gt; contains our controller, &lt;code&gt;src/Service&lt;/code&gt; contains the service classes, and so on.&lt;/p&gt;
&lt;p&gt;If you want to run it, you need to have installed &lt;a href=&#34;https://www.php.net/&#34;&gt;PHP&lt;/a&gt;, &lt;a href=&#34;https://getcomposer.org/&#34;&gt;Composer&lt;/a&gt;, the &lt;a href=&#34;https://symfony.com/download&#34;&gt;Symfony CLI&lt;/a&gt;, and these extensions: php-sqlite3, php-xml, php-curl. If you like &lt;a href=&#34;https://www.docker.com/&#34;&gt;Docker&lt;/a&gt;, there’s a Dockerfile in the repo that you can use to fire up a container ready for running the app.&lt;/p&gt;
&lt;p&gt;Once you’ve got all that set up, do the following:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Clone the git repo with &lt;code&gt;git clone git@github.com:megakevin/end-point-blog-automated-testing-with-symfony.git&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;cd&lt;/code&gt; into the directory that was just created and install the project’s dependencies with &lt;code&gt;composer install&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Initialize the database with &lt;code&gt;bin/console doctrine:schema:create&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Fire up the application with &lt;code&gt;composer serve&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Go to &lt;code&gt;localhost:3000&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;You should now be able to see something like this:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2020/09/automated-testing-with-symfony/screenshot.png&#34; alt=&#34;Screenshot&#34;&gt;&lt;/p&gt;
&lt;p&gt;Ok, now that we understand the system under test, what it does, and how it works, we’re ready to test it.&lt;/p&gt;
&lt;h3 id=&#34;unit-tests&#34;&gt;Unit tests&lt;/h3&gt;
&lt;h4 id=&#34;surveying-a-class-to-write-unit-tests-for-it&#34;&gt;Surveying a class to write unit tests for it&lt;/h4&gt;
&lt;p&gt;I’d like to start by looking into the simplest kind of tests that we need to write for this app. Those would be unit tests for our entity classes. These tests are simple because the classes that they exercise are simple as well. If you look at our &lt;code&gt;Weather&lt;/code&gt; and &lt;code&gt;WeatherQuery&lt;/code&gt; classes inside &lt;code&gt;src/Entity&lt;/code&gt; you’ll see that they contain little more than some fields with their corresponding accessors and some convenience factory methods. They also don’t have any dependencies, which is convenient because that’s one less thing for our &lt;a href=&#34;https://en.wikipedia.org/wiki/Test_fixture&#34;&gt;test fixtures&lt;/a&gt; to worry about.&lt;/p&gt;
&lt;p&gt;So, the first step I always take is inspecting the class that I’m about to test, to try to determine what’s interesting from a testing perspective. I try to think about what the main responsibility of the class is, what sort of logic is actually adding value, what things could be broken inadvertently by other developers, what potential changes in the code I would like the test suite to alert developers of (by failing tests!), what features would benefit from having their API captured/​documented in the form of an automated test. I ask myself these questions because oftentimes it’s not feasible to achieve 100% code coverage with unit tests (or with any kind of tests, for that matter). So, in those cases when we need to be strategic about what tests we write, I try to write those that will add the most value. When faced with the reality of limited resources, I try to approach these things from a “best bang for your buck” angle.&lt;/p&gt;
&lt;p&gt;With that in mind, if we look at our &lt;code&gt;WeatherQuery&lt;/code&gt; class, here are the things that come to my attention:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;First&lt;/strong&gt;, there are some fields annotated with validation logic. That’s the &lt;code&gt;@Assert&lt;/code&gt; comments on top of the field definitions. For example:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;/**
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;* @ORM\Column(type=&amp;#34;string&amp;#34;, length=255)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;* @Assert\NotBlank(message=&amp;#34;The city should not be blank.&amp;#34;)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;* @Assert\Type(&amp;#34;string&amp;#34;)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;*/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;private&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$city&lt;/span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;There are a few more. It’d be interesting to test those validation rules.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Second&lt;/strong&gt;, for every field, there are accessor methods defined as well. That is, getters and setters like these:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;getState&lt;/span&gt;(): ?string
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$this&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;state&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;setState&lt;/span&gt;(string &lt;span style=&#34;color:#369&#34;&gt;$state&lt;/span&gt;): self
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#369&#34;&gt;$this&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;state&lt;/span&gt; = &lt;span style=&#34;color:#369&#34;&gt;$state&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$this&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;These methods are very simple. If we were going for 100% coverage, we may want to exercise those accessors. But being the strategic developers we are, I think we can ignore those for now. The logic is very simple, not really much opportunity for things to go wrong here.&lt;/p&gt;
&lt;p&gt;These types of methods become even less relevant in other languages like &lt;a href=&#34;https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/properties&#34;&gt;C#&lt;/a&gt; or &lt;a href=&#34;https://ruby-doc.org/docs/ruby-doc-bundle/UsersGuide/rg/accessors.html&#34;&gt;Ruby&lt;/a&gt; which support accessors as language level constructs. Testing those would be a moot point in those languages, since it would mean testing language/​framework features, and not code that we own. There’s no good reason to do that.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Third&lt;/strong&gt;, there’s a convenience factory method defined at the bottom of the class. It looks like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;static&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;build&lt;/span&gt;(string &lt;span style=&#34;color:#369&#34;&gt;$city&lt;/span&gt;, string &lt;span style=&#34;color:#369&#34;&gt;$state&lt;/span&gt;): WeatherQuery
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#369&#34;&gt;$city&lt;/span&gt; = ucwords(&lt;span style=&#34;color:#369&#34;&gt;$city&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#369&#34;&gt;$state&lt;/span&gt; = strtoupper(&lt;span style=&#34;color:#369&#34;&gt;$state&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#369&#34;&gt;$weatherQuery&lt;/span&gt; = (&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;new&lt;/span&gt; WeatherQuery())
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        -&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;setCity&lt;/span&gt;(&lt;span style=&#34;color:#369&#34;&gt;$city&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        -&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;setState&lt;/span&gt;(&lt;span style=&#34;color:#369&#34;&gt;$state&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        -&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;setCreated&lt;/span&gt;(&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;new&lt;/span&gt; DateTime())
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$weatherQuery&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This &lt;code&gt;build&lt;/code&gt; method has some interesting logic to it. There’s some pre-processing that happens to the input parameters. It’d also be interesting for a test to exercise that the resulting object is correctly constructed.&lt;/p&gt;
&lt;h4 id=&#34;deciding-what-tests-to-write&#34;&gt;Deciding what tests to write&lt;/h4&gt;
&lt;p&gt;So by analyzing our class we’ve identified parts of it that are interesting to test. We want to test the validation logic and the &lt;code&gt;build&lt;/code&gt; method.&lt;/p&gt;
&lt;p&gt;Let’s start with the &lt;code&gt;build&lt;/code&gt; method as that one’s easier.&lt;/p&gt;
&lt;p&gt;Now that we’ve identified the unit that we will test, we need to come up with test cases that exercise it in various ways. Looking at &lt;code&gt;build&lt;/code&gt;’s code line by line, I can come up with a few interesting test cases. Here’s my thought process:&lt;/p&gt;
&lt;p&gt;The first thing that I notice is that the method takes two parameters, city and state, and does this with them:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#369&#34;&gt;$city&lt;/span&gt; = ucwords(&lt;span style=&#34;color:#369&#34;&gt;$city&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#369&#34;&gt;$state&lt;/span&gt; = strtoupper(&lt;span style=&#34;color:#369&#34;&gt;$state&lt;/span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;So the test suite should validate that these values get processed like this.&lt;/p&gt;
&lt;p&gt;There’s also this part where the object to return gets constructed:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#369&#34;&gt;$weatherQuery&lt;/span&gt; = (&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;new&lt;/span&gt; WeatherQuery())
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    -&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;setCity&lt;/span&gt;(&lt;span style=&#34;color:#369&#34;&gt;$city&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    -&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;setState&lt;/span&gt;(&lt;span style=&#34;color:#369&#34;&gt;$state&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    -&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;setCreated&lt;/span&gt;(&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;new&lt;/span&gt; DateTime())
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This code is taking the parameters and assigning them to fields in the object. That’d be something interesting to validate. There’s also a “created” field that gets initialized with the current date and time. Also interesting.&lt;/p&gt;
&lt;p&gt;Having seen that, I can come up with this set of test cases for the &lt;code&gt;build&lt;/code&gt; method:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Test that it assigns the parameters to the correct fields in the resulting object.&lt;/li&gt;
&lt;li&gt;Test that it capitalizes each word in the city.&lt;/li&gt;
&lt;li&gt;Test that it capitalizes the state.&lt;/li&gt;
&lt;li&gt;Test that it sets the current moment as the “created” field in the resulting object.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&#34;tactics-for-writing-the-unit-tests&#34;&gt;Tactics for writing the unit tests&lt;/h4&gt;
&lt;p&gt;I think we’ve addressed the main concerns of this method. Now let’s write it in PHP with PHPUnit.&lt;/p&gt;
&lt;h5 id=&#34;where-to-put-them-and-what-to-name-them&#34;&gt;Where to put them and what to name them&lt;/h5&gt;
&lt;p&gt;In the demo application that you’ve hopefully downloaded and explored, we write our tests under the &lt;code&gt;tests&lt;/code&gt; directory. This is the default location Symfony gives us for our tests and I think it’s a good one. For our unit tests, we will put them in &lt;code&gt;tests/unit&lt;/code&gt;. Tests for this class in particular go in the &lt;code&gt;tests/unit/Entity/WeatherQueryTest.php&lt;/code&gt; file.&lt;/p&gt;
&lt;p&gt;Notice our naming and file location convention. The name of the file that contains a given class’s test cases is the same as the class itself, only with the word &lt;code&gt;Test&lt;/code&gt; as a suffix. We’ve also made sure to mimic the project’s &lt;code&gt;src&lt;/code&gt; directory structure in our &lt;code&gt;tests/unit&lt;/code&gt; directory. So, in this example, the &lt;code&gt;WeatherQuery&lt;/code&gt; class is located in &lt;code&gt;src/Entity/WeatherQuery.php&lt;/code&gt;. That means that its tests should live in &lt;code&gt;tests/unit/Entity/WeatherQueryTest.php&lt;/code&gt;. Following this convention keeps things simple and easy to navigate and maintain.&lt;/p&gt;
&lt;h5 id=&#34;what-a-phpunit-test-class-looks-like&#34;&gt;What a PHPUnit test class looks like&lt;/h5&gt;
&lt;p&gt;PHPUnit makes writing tests easy. A collection of tests is defined as a series of methods inside a class that extends the &lt;code&gt;PHPUnit\Framework\TestCase&lt;/code&gt; class. Here’s what our &lt;code&gt;WeatherQueryTest.php&lt;/code&gt; file looks like:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;namespace&lt;/span&gt; App\Tests\Unit\Entity;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;use&lt;/span&gt; PHPUnit\Framework\TestCase;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;// ...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;WeatherQueryTest&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;extends&lt;/span&gt; TestCase
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// build
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;testBuildAssignsTheParametersToTheCorrectFields&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#888&#34;&gt;// ...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// ...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Notice how the test class name is, again, the same name of the class under test, suffixed with the word &lt;code&gt;Test&lt;/code&gt;. Notice how it extends from the &lt;code&gt;TestCase&lt;/code&gt; class and notice how our test methods (that is, test cases) all begin with the word &lt;code&gt;test&lt;/code&gt;. This is necessary for PHPUnit. This is how we signal to it that this is, in fact, a test case that it needs to run.&lt;/p&gt;
&lt;h5 id=&#34;how-to-name-the-individual-test-methods&#34;&gt;How to name the individual test methods&lt;/h5&gt;
&lt;p&gt;Another important thing to note is the naming convention that we’re using here for our test methods. Remember the test cases that we came up with above:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Test that it assigns the parameters to the correct fields in the resulting object.&lt;/li&gt;
&lt;li&gt;Test that it capitalizes each word in the city.&lt;/li&gt;
&lt;li&gt;Test that it capitalizes the state.&lt;/li&gt;
&lt;li&gt;Test that it sets the current moment as the “created” field in the resulting object.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These are wordy descriptions of exactly what the test cases are about. Just by reading these, we can get a good idea of what the purpose of the test case is, and its expected outcome. For the same reasons of readability and self documentation, the names of our test methods in the actual test suite implementation need to be as close to that as possible. I’ve come up with these:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;testBuildAssignsTheParametersToTheCorrectFields&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;testBuildCapitalizesTheGivenCityParameter&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;testBuildCapitalizesTheGivenStateParameter&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;testBuildSetsTheCurrentMomentAsTheCreatedField&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;They begin with the word &lt;code&gt;test&lt;/code&gt; in order to fulfill PHPUnit’s requirements. Then, they include as many details as possible on what they are about.&lt;/p&gt;
&lt;h4 id=&#34;the-anatomy-of-a-test-case&#34;&gt;The anatomy of a test case&lt;/h4&gt;
&lt;p&gt;When it comes to actually writing the tests, I put great focus on making them as easy to understand as possible. Test cases do not only serve the purpose of validating system behavior. They also can serve as a live documentation of the system’s features, and, in the case of unit tests, its inner workings. To be able to fulfill that purpose, tests need to be easy to read, navigate, and understand.&lt;/p&gt;
&lt;p&gt;That’s why I always like to call attention to the general three steps that almost every test follows: “Setup”, “Exercise” and “Verify”. Or, as I like to call them: “Arrange”, “Act” and “Assert”. So the first thing I do is add those three as comments in the test method body:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;testBuildAssignsTheParametersToTheCorrectFields&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// Arrange
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// Act
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// Assert
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;“Arrange” is the step where we set up our test fixture. That is, we configure the world around the unit under test — the input, dependencies, etc. — so that we can control all the variables that go into the execution of the unit.&lt;/p&gt;
&lt;p&gt;“Act” is quite simply where we have our unit under test be executed.&lt;/p&gt;
&lt;p&gt;“Assert” is where we verify that the unit under test behaved correctly, that it met the test case’s expectations. We normally do this by checking the output of the method or some other side effect that its execution causes.&lt;/p&gt;
&lt;p&gt;In the &lt;a href=&#34;https://en.wikipedia.org/wiki/Behavior-driven_development&#34;&gt;Behavior Driven Development&lt;/a&gt; world, “Given”, “When” and “Then” are parallels for these. You may see that terminology used as well. The spirit is the same.&lt;/p&gt;
&lt;p&gt;Usually the next most obvious step is Act. This example is like that: We just need to call our &lt;code&gt;build&lt;/code&gt; method and capture its output. Adding that, our test method would look like this now:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;testBuildAssignsTheParametersToTheCorrectFields&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// Arrange
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// Act
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#369&#34;&gt;$result&lt;/span&gt; = WeatherQuery::&lt;span style=&#34;color:#369&#34;&gt;build&lt;/span&gt;(&lt;span style=&#34;color:#369&#34;&gt;$testCity&lt;/span&gt;, &lt;span style=&#34;color:#369&#34;&gt;$testState&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// Assert
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;By writing our Act portion, we discovered that we need parameters, AKA input values. That’s what the Arrange step is for:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;testBuildAssignsTheParametersToTheCorrectFields&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// Arrange
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#369&#34;&gt;$testCity&lt;/span&gt; = &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;MyCity&amp;#39;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#369&#34;&gt;$testState&lt;/span&gt; = &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;ST&amp;#39;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// Act
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#369&#34;&gt;$result&lt;/span&gt; = WeatherQuery::&lt;span style=&#34;color:#369&#34;&gt;build&lt;/span&gt;(&lt;span style=&#34;color:#369&#34;&gt;$testCity&lt;/span&gt;, &lt;span style=&#34;color:#369&#34;&gt;$testState&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// Assert
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Finally, we need to actually validate that the &lt;code&gt;build&lt;/code&gt; method did what we expected. We do that by writing some assertions. What did we expect it to do, though? Well, the name of the test method has the answer: we expect it to “assign the given parameters into the correct fields of the resulting object”. In PHPUnit, we do it like so:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;testBuildAssignsTheParametersToTheCorrectFields&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// Arrange
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#369&#34;&gt;$testCity&lt;/span&gt; = &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;MyCity&amp;#39;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#369&#34;&gt;$testState&lt;/span&gt; = &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;ST&amp;#39;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// Act
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#369&#34;&gt;$result&lt;/span&gt; = WeatherQuery::&lt;span style=&#34;color:#369&#34;&gt;build&lt;/span&gt;(&lt;span style=&#34;color:#369&#34;&gt;$testCity&lt;/span&gt;, &lt;span style=&#34;color:#369&#34;&gt;$testState&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// Assert
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#369&#34;&gt;$this&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;assertEquals&lt;/span&gt;(&lt;span style=&#34;color:#369&#34;&gt;$testCity&lt;/span&gt;, &lt;span style=&#34;color:#369&#34;&gt;$result&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;getCity&lt;/span&gt;());
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#369&#34;&gt;$this&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;assertEquals&lt;/span&gt;(&lt;span style=&#34;color:#369&#34;&gt;$testState&lt;/span&gt;, &lt;span style=&#34;color:#369&#34;&gt;$result&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;getState&lt;/span&gt;());
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;PHPUnit has an extensive number of different types of assertion methods. In this case, we use one of the simpler ones: &lt;code&gt;assertEquals&lt;/code&gt;. This method will compare two values and report the test as a failure if they are different. It will report it as a success if they are equal.&lt;/p&gt;
&lt;p&gt;In this test case, we want PHPUnit to validate for us that the resulting object’s &lt;code&gt;city&lt;/code&gt; field is the same that we passed in. Same for the object’s &lt;code&gt;state&lt;/code&gt; field.&lt;/p&gt;
&lt;p&gt;And that does it for our first unit test! We’ve prepared some input, called the unit that we wanted to test, and inspected its state to validate that it did what it was supposed to do.&lt;/p&gt;
&lt;h4 id=&#34;running-phpunit-tests&#34;&gt;Running PHPUnit tests&lt;/h4&gt;
&lt;p&gt;Symfony also makes it easy to run unit tests. By default, Symfony projects created with either &lt;code&gt;symfony new my_project_name --full&lt;/code&gt; or &lt;code&gt;composer create-project symfony/website-skeleton my_project_name&lt;/code&gt; &lt;a href=&#34;https://symfony.com/doc/current/setup.html#creating-symfony-applications&#34;&gt;already include&lt;/a&gt; the PHPUnit library and the configuration necessary to execute unit tests like this one. You can run it, along with all the others in this test class with this command:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;bin/phpunit tests/unit/Entity/WeatherQueryTest.php&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Which will result in something like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ bin/phpunit tests/unit/Entity/WeatherQueryTest.php
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;PHPUnit 7.5.20 by Sebastian Bergmann and contributors.
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Testing App&lt;span style=&#34;color:#04d;background-color:#fff0f0&#34;&gt;\T&lt;/span&gt;ests&lt;span style=&#34;color:#04d;background-color:#fff0f0&#34;&gt;\U&lt;/span&gt;nit&lt;span style=&#34;color:#04d;background-color:#fff0f0&#34;&gt;\E&lt;/span&gt;ntity&lt;span style=&#34;color:#04d;background-color:#fff0f0&#34;&gt;\W&lt;/span&gt;eatherQueryTest
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;........                                                            &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;8&lt;/span&gt; / &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;8&lt;/span&gt; (100%)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Time: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;64&lt;/span&gt; ms, Memory: 8.00 MB
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;OK (&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;8&lt;/span&gt; tests, &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;9&lt;/span&gt; assertions)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Notice how we just call the PHPUnit executable and pass it the file name that contains the tests that we want to run.&lt;/p&gt;
&lt;p&gt;I prefer the &lt;a href=&#34;https://en.wikipedia.org/wiki/TestDox&#34;&gt;TestDox&lt;/a&gt; style output though, so I like to use this instead:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;bin/phpunit --testdox tests/unit/Entity/WeatherQueryTest.php&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Which gives us:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ bin/phpunit --testdox tests/unit/Entity/WeatherQueryTest.php
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;PHPUnit 7.5.20 by Sebastian Bergmann and contributors.
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Testing App&lt;span style=&#34;color:#04d;background-color:#fff0f0&#34;&gt;\T&lt;/span&gt;ests&lt;span style=&#34;color:#04d;background-color:#fff0f0&#34;&gt;\U&lt;/span&gt;nit&lt;span style=&#34;color:#04d;background-color:#fff0f0&#34;&gt;\E&lt;/span&gt;ntity&lt;span style=&#34;color:#04d;background-color:#fff0f0&#34;&gt;\W&lt;/span&gt;eatherQueryTest
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;App&lt;span style=&#34;color:#04d;background-color:#fff0f0&#34;&gt;\T&lt;/span&gt;ests&lt;span style=&#34;color:#04d;background-color:#fff0f0&#34;&gt;\U&lt;/span&gt;nit&lt;span style=&#34;color:#04d;background-color:#fff0f0&#34;&gt;\E&lt;/span&gt;ntity&lt;span style=&#34;color:#04d;background-color:#fff0f0&#34;&gt;\W&lt;/span&gt;eatherQuery
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; ✔ Build assigns the parameters to the correct fields
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; ✔ Build capitalizes the given city parameter
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; ✔ Build capitalizes the given state parameter
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; ✔ Build sets the current moment as the created field
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;...
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Time: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;68&lt;/span&gt; ms, Memory: 8.00 MB
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;OK (&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;8&lt;/span&gt; tests, &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;9&lt;/span&gt; assertions)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And this is where those really long and detailed names for the test methods pays off. Now running a test suite results in output that reads in plain English and serves as a specification of sorts on how a given class works. This fulfills the automated testing suite’s secondary objective of serving as live documentation for the system.&lt;/p&gt;
&lt;h4 id=&#34;other-interesting-details&#34;&gt;Other interesting details&lt;/h4&gt;
&lt;p&gt;Now that we’ve discussed my thought process for writing this first unit test, the rest of the &lt;code&gt;build&lt;/code&gt; unit tests should be pretty self-explanatory. They all follow the same pattern; the only difference is the Arrange and Assert parts, which change according to what the particular tests’s purpose is.&lt;/p&gt;
&lt;p&gt;For instance, notice how the &lt;code&gt;testBuildCapitalizesTheGivenCityParameter&lt;/code&gt; test passes the &lt;code&gt;build&lt;/code&gt; function &lt;code&gt;&#39;my city&#39;&lt;/code&gt; as the value for the city parameter. Then, it asserts that the return object’s property was set to &lt;code&gt;My City&lt;/code&gt;. Thus, assuring that the &lt;code&gt;$city = ucwords($city);&lt;/code&gt; line in the &lt;code&gt;build&lt;/code&gt; method’s implementation is working properly. If somebody changes this by mistake, the test will break and let them know that they have to fix it.&lt;/p&gt;
&lt;p&gt;Another interesting test case is this one: &lt;code&gt;testBuildSetsTheCurrentMomentAsTheCreatedField&lt;/code&gt;. Here, we need to assert that the &lt;code&gt;WeatherQuery&lt;/code&gt; object returned by the &lt;code&gt;build&lt;/code&gt; method has its “created” field set to the current moment in time. However, timestamps are so precise that one obtained before calling the method and one obtained inside it are different. We still need to assert equality though. So, to deal with that, we use this variation of the &lt;code&gt;assertEquals&lt;/code&gt; assertion:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#369&#34;&gt;$this&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;assertEquals&lt;/span&gt;(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    (&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;new&lt;/span&gt; DateTime())-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;getTimestamp&lt;/span&gt;(),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#369&#34;&gt;$result&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;getCreated&lt;/span&gt;()-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;getTimestamp&lt;/span&gt;(),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This one tests for equality, but with a certain allowed difference. If the difference is within this margin of error, then the assertion deems that the two values are equal — just what we need in this case. This basically says: “Assert that the current timestamp and the resulting object’s timestamp are ‘pretty much’ the same.” That’s as good a job as PHPUnit can do to for that assertion, and that’s good enough for us.&lt;/p&gt;
&lt;h4 id=&#34;testing-behavior-with-varying-input&#34;&gt;Testing behavior with varying input&lt;/h4&gt;
&lt;p&gt;Ok, we’ve now tested one piece of functionality defined within the &lt;code&gt;WeatherQuery&lt;/code&gt; class. There is something else to test though: the validation rules. The class has several validation rules defined as annotations. We are able to define validation rules like this thanks to &lt;a href=&#34;https://symfony.com/doc/current/validation.html&#34;&gt;Symfony’s Validator component&lt;/a&gt;. To validate an object against those rules, we need to use the &lt;code&gt;Symfony\Component\Validator\Validation&lt;/code&gt; class. Here’s what a test that exercises those validation values looks like:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;testValidation&lt;/span&gt;(&lt;span style=&#34;color:#369&#34;&gt;$city&lt;/span&gt;, &lt;span style=&#34;color:#369&#34;&gt;$state&lt;/span&gt;, &lt;span style=&#34;color:#369&#34;&gt;$expected&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// Arrange
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#369&#34;&gt;$subject&lt;/span&gt; = WeatherQuery::&lt;span style=&#34;color:#369&#34;&gt;build&lt;/span&gt;(&lt;span style=&#34;color:#369&#34;&gt;$city&lt;/span&gt;, &lt;span style=&#34;color:#369&#34;&gt;$state&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#369&#34;&gt;$validator&lt;/span&gt; = Validation::&lt;span style=&#34;color:#369&#34;&gt;createValidatorBuilder&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        -&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;enableAnnotationMapping&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        -&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;getValidator&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// Act
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#369&#34;&gt;$result&lt;/span&gt; = &lt;span style=&#34;color:#369&#34;&gt;$validator&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;validate&lt;/span&gt;(&lt;span style=&#34;color:#369&#34;&gt;$subject&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// Assert
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#369&#34;&gt;$this&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;assertEquals&lt;/span&gt;(&lt;span style=&#34;color:#369&#34;&gt;$expected&lt;/span&gt;, count(&lt;span style=&#34;color:#369&#34;&gt;$result&lt;/span&gt;) == &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;If you are familiar at all with Symfony, then you may know what all this is about. If not, then it’s not hard at all. We just need this recipe: &lt;code&gt;Validation::createValidatorBuilder()-&amp;gt;enableAnnotationMapping()-&amp;gt;getValidator();&lt;/code&gt; to obtain a &lt;code&gt;Validator&lt;/code&gt; instance. We can pass a &lt;code&gt;WeatherQuery&lt;/code&gt; object to it and it will validate it for us using the annotations defined within the class.&lt;/p&gt;
&lt;p&gt;That’s how we validate an object in Symfony. However, validation is a common technique that’s done regardless of framework. In general, to test validation rules, we always use the same approach:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Create the validation subject with some input data.&lt;/li&gt;
&lt;li&gt;Call whatever component to validate said input data.&lt;/li&gt;
&lt;li&gt;Assert that the validation result is what we expect given the input.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This means that testing a set of validation rules boils down to running the same process over and over again with different input. The input is the only thing that varies. PHPUnit has the &lt;a href=&#34;https://phpunit.readthedocs.io/en/9.3/writing-tests-for-phpunit.html#data-providers&#34;&gt;data provider&lt;/a&gt; concept which lends itself beautifully for these scenarios.&lt;/p&gt;
&lt;p&gt;To illustrate this, let’s continue with our validation example. Consider the annotation and signature of the test method:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;/**
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;* @dataProvider getValidationTestCases
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;*/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;testValidation&lt;/span&gt;(&lt;span style=&#34;color:#369&#34;&gt;$city&lt;/span&gt;, &lt;span style=&#34;color:#369&#34;&gt;$state&lt;/span&gt;, &lt;span style=&#34;color:#369&#34;&gt;$expected&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// ...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;@dataProvider getValidationTestCases&lt;/code&gt; comment tells PHPUnit to look for a method named &lt;code&gt;getValidationTestCases&lt;/code&gt; to obtain the list of sets of arguments to pass to the annotated test method.&lt;/p&gt;
&lt;p&gt;Our &lt;code&gt;testValidation&lt;/code&gt; method expects three parameters: a city, a state, and a boolean representing whether the validation should succeed or not. So, the &lt;code&gt;getValidationTestCases&lt;/code&gt; method needs to provide them. Let’s look at it now:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;getValidationTestCases&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;return&lt;/span&gt; [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;Succeeds when data is correct&amp;#39;&lt;/span&gt; =&amp;gt; [ &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;New York&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;NY&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;true&lt;/span&gt; ],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;Fails when city is missing&amp;#39;&lt;/span&gt; =&amp;gt; [ &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;NY&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;false&lt;/span&gt; ],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;Fails when state is missing&amp;#39;&lt;/span&gt; =&amp;gt; [ &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;New York&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;false&lt;/span&gt; ],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;Fails when state is not a valid US state&amp;#39;&lt;/span&gt; =&amp;gt; [ &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;New York&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;AAA&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;false&lt;/span&gt; ],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  ];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;getValidationTestCases&lt;/code&gt; returns an associative array that contains four items. Each item’s key is a description of the test case and the value is the data that makes up the test case. That data is what will get passed as parameters to &lt;code&gt;testValidation&lt;/code&gt;. The keys are used in the TestDox output to make it more descriptive, like so:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ bin/phpunit --testdox tests/unit/Entity/WeatherQueryTest.php
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;PHPUnit 7.5.20 by Sebastian Bergmann and contributors.
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Testing App&lt;span style=&#34;color:#04d;background-color:#fff0f0&#34;&gt;\T&lt;/span&gt;ests&lt;span style=&#34;color:#04d;background-color:#fff0f0&#34;&gt;\U&lt;/span&gt;nit&lt;span style=&#34;color:#04d;background-color:#fff0f0&#34;&gt;\E&lt;/span&gt;ntity&lt;span style=&#34;color:#04d;background-color:#fff0f0&#34;&gt;\W&lt;/span&gt;eatherQueryTest
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;App&lt;span style=&#34;color:#04d;background-color:#fff0f0&#34;&gt;\T&lt;/span&gt;ests&lt;span style=&#34;color:#04d;background-color:#fff0f0&#34;&gt;\U&lt;/span&gt;nit&lt;span style=&#34;color:#04d;background-color:#fff0f0&#34;&gt;\E&lt;/span&gt;ntity&lt;span style=&#34;color:#04d;background-color:#fff0f0&#34;&gt;\W&lt;/span&gt;eatherQuery
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;...
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; ✔ Validation with data &lt;span style=&#34;color:#038&#34;&gt;set&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Succeeds when data is correct&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; ✔ Validation with data &lt;span style=&#34;color:#038&#34;&gt;set&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Fails when city is missing&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; ✔ Validation with data &lt;span style=&#34;color:#038&#34;&gt;set&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Fails when state is missing&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; ✔ Validation with data &lt;span style=&#34;color:#038&#34;&gt;set&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Fails when state is not a valid US state&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Time: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;68&lt;/span&gt; ms, Memory: 8.00 MB
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;OK (&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;8&lt;/span&gt; tests, &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;9&lt;/span&gt; assertions)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;As you can see, this results in PHPUnit running the &lt;code&gt;testValidation&lt;/code&gt; method four times, once per each test case defined in &lt;code&gt;getValidationTestCases&lt;/code&gt;, using their corresponding data as parameters.&lt;/p&gt;
&lt;p&gt;I find PHPUnit’s data provider feature very useful for testing input validation logic. However, it can be used for other types of tests as well. I always consider trying it out whenever I see a series of test cases that look very similar to each other. This usually means that they can be written in a generic way to eliminate repetition. The data provider feature makes it easy to push variability out of the test cases code and into the input arguments.&lt;/p&gt;
&lt;h4 id=&#34;using-mocks-to-test-classes-with-dependencies&#34;&gt;Using mocks to test classes with dependencies&lt;/h4&gt;
&lt;p&gt;Now let’s move on to a set of classes that are generally a bit less straightforward to test: services. Service classes, as opposed to entities, are seldom so independent and self-contained. Because of their very nature as integrators and orchestrators of other classes that fulfill core business logic, services often have dependencies and collaborators.&lt;/p&gt;
&lt;p&gt;Take a look at our &lt;code&gt;WeatherService&lt;/code&gt; class in &lt;code&gt;src/Service/WeatherService.php&lt;/code&gt;. Just by looking at the constructor you can see that it depends on others to function:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;__construct&lt;/span&gt;(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ValidatorInterface &lt;span style=&#34;color:#369&#34;&gt;$validator&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    WeatherQueryRepository &lt;span style=&#34;color:#369&#34;&gt;$repository&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    WeatherApiClient &lt;span style=&#34;color:#369&#34;&gt;$apiClient&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#369&#34;&gt;$this&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;validator&lt;/span&gt; = &lt;span style=&#34;color:#369&#34;&gt;$validator&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#369&#34;&gt;$this&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;repository&lt;/span&gt; = &lt;span style=&#34;color:#369&#34;&gt;$repository&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#369&#34;&gt;$this&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;apiClient&lt;/span&gt; = &lt;span style=&#34;color:#369&#34;&gt;$apiClient&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In order to work properly, the &lt;code&gt;WeatherService&lt;/code&gt; needs a few objects: a &lt;code&gt;ValidatorInterface&lt;/code&gt;, a &lt;code&gt;WeatherQueryRepository&lt;/code&gt;, and a &lt;code&gt;WeatherApiClient&lt;/code&gt;. Instead of directly creating these objects though, this class uses a technique called &lt;a href=&#34;https://en.wikipedia.org/wiki/Dependency_injection&#34;&gt;dependency injection&lt;/a&gt;, where the objects it needs are passed to it via its constructor. In other words, all of its “dependencies” are “injected” into it. Dependency injection is key for unit testing. Here’s why: For unit tests, our objective is to test a unit in complete isolation. This means that, within a given test case, we want to exercise the code of one class and one class alone. If an object leverages other objects to do some work, we don’t care about those other objects. If we did, then the unit test loses focus. It becomes something else. The unit under test is no longer a unit.&lt;/p&gt;
&lt;p&gt;Dependency injection allows client code to specify which concrete dependencies a given object will use. That’s a feature that test cases can take advantage of to pass in fake objects that it sets up and controls. We call these &lt;a href=&#34;https://en.wikipedia.org/wiki/Mock_object&#34;&gt;mocks&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Mocks are essentially fake objects that a test fixture uses to pass as dependencies to its test subject. They are objects that, to the eyes of the unit under test, are the real deal objects that they normally use and work with. However, in reality they are made up objects that look like the subject’s dependencies that the test case fully controls and can inspect.&lt;/p&gt;
&lt;p&gt;Why do unit tests need to test objects in isolation? Because unit tests need to be simple, fast, and easy to understand. Having a test case focused only on validating a single, small piece of functionality is a great way to achieve those three goals.&lt;/p&gt;
&lt;p&gt;Ok, let’s see an example of some mock objects in action. Consider the &lt;code&gt;testGetCurrentWeatherDoesNotReturnSuccessWhenTheApiCallIsUnsuccessful&lt;/code&gt; test case in &lt;code&gt;tests/unit/Service/WeatherServiceTest.php&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;testGetCurrentWeatherDoesNotReturnSuccessWhenTheApiCallIsUnsuccessful&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// Arrange
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#369&#34;&gt;$validator&lt;/span&gt; = Validation::&lt;span style=&#34;color:#369&#34;&gt;createValidatorBuilder&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        -&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;enableAnnotationMapping&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        -&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;getValidator&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#369&#34;&gt;$mockRepository&lt;/span&gt; = &lt;span style=&#34;color:#369&#34;&gt;$this&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;createMock&lt;/span&gt;(WeatherQueryRepository::&lt;span style=&#34;color:#369&#34;&gt;class&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#369&#34;&gt;$mockApiClient&lt;/span&gt; = &lt;span style=&#34;color:#369&#34;&gt;$this&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;createMock&lt;/span&gt;(WeatherApiClient::&lt;span style=&#34;color:#369&#34;&gt;class&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#369&#34;&gt;$mockApiClient&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;method&lt;/span&gt;(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;getCurrentWeather&amp;#39;&lt;/span&gt;)-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;willReturn&lt;/span&gt;([
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;success&amp;#39;&lt;/span&gt; =&amp;gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ]);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#369&#34;&gt;$service&lt;/span&gt; = &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;new&lt;/span&gt; WeatherService(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#369&#34;&gt;$validator&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#369&#34;&gt;$mockRepository&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#369&#34;&gt;$mockApiClient&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    );
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// Act
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#369&#34;&gt;$result&lt;/span&gt; = &lt;span style=&#34;color:#369&#34;&gt;$service&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;getCurrentWeather&lt;/span&gt;(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;New York&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;NY&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// Assert
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#369&#34;&gt;$this&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;assertFalse&lt;/span&gt;(&lt;span style=&#34;color:#369&#34;&gt;$result&lt;/span&gt;[&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;success&amp;#39;&lt;/span&gt;]);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;One feature of the &lt;code&gt;GetCurrentWeather&lt;/code&gt; method in the &lt;code&gt;WeatherService&lt;/code&gt; is that it uses a dependency, &lt;code&gt;WeatherApiClient&lt;/code&gt;, to make an HTTP request to the OpenWeatherMap Web API, by calling its &lt;code&gt;getCurrentWeather&lt;/code&gt; method. If the request is not successful, &lt;code&gt;WeatherApiClient&lt;/code&gt; returns an array which contains a &lt;code&gt;success&lt;/code&gt; field that’s set to false. The good thing about a decoupled design made possible by dependency injection is that the &lt;code&gt;WeatherService&lt;/code&gt; doesn’t care about HTTP requests or responses or any of that. It only cares and knows about &lt;code&gt;WeatherApiClient&lt;/code&gt;’s contract. In this case, the contract dictates that it returns an array with a &lt;code&gt;success&lt;/code&gt; field. Knowing this, the test case can create a mock object that looks just like a &lt;code&gt;WeatherApiClient&lt;/code&gt; instance and make it return something that will make the &lt;code&gt;WeatherService&lt;/code&gt; think that the request failed. It’s done like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#369&#34;&gt;$mockApiClient&lt;/span&gt; = &lt;span style=&#34;color:#369&#34;&gt;$this&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;createMock&lt;/span&gt;(WeatherApiClient::&lt;span style=&#34;color:#369&#34;&gt;class&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#369&#34;&gt;$mockApiClient&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;method&lt;/span&gt;(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;getCurrentWeather&amp;#39;&lt;/span&gt;)-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;willReturn&lt;/span&gt;([
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;success&amp;#39;&lt;/span&gt; =&amp;gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;]);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The first statement uses PHPUnit’s &lt;code&gt;createMock&lt;/code&gt; method to obtain a fake object. Then, the second statement configures it by specifying that, whenever the &lt;code&gt;getCurrentWeather&lt;/code&gt; gets called on that mock, it will return the value that will make &lt;code&gt;WeatherService&lt;/code&gt; think that the request failed.&lt;/p&gt;
&lt;p&gt;After that’s set up, the rest of the test should be straightforward: In the Arrange section we set up other mocks for each of &lt;code&gt;WeatherService&lt;/code&gt;’s constructor parameters (i.e. injected dependencies) and create a real instance of &lt;code&gt;WeatherService&lt;/code&gt; which is our unit under test; in the Act section, we exercise our UUT by calling the method we want to test; and finally, in the Assert section, we validate that the UUT has returned a result that signals that the operation was unsuccessful by looking at the resulting array’s &lt;code&gt;success&lt;/code&gt; field.&lt;/p&gt;
&lt;h4 id=&#34;verifying-behavior-instead-of-state-expectations-with-phpunit&#34;&gt;Verifying behavior instead of state: Expectations with PHPUnit&lt;/h4&gt;
&lt;p&gt;In this last example, we used the mock to control the UUT’s behavior and asserted on the result by verifying data or state. Mocks can do much more than that, though. We can configure them to return whatever value we want, like we just did, but we can also inspect them to know if they have been called, what parameters were given to them, etc. This allows us to write a slightly different style of test — one that, instead of verifying state, verifies behavior. Here’s an example of such a test case:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;testGetCurrentWeatherCallsOnTheApiClientToGetWeatherData&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// Arrange
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#369&#34;&gt;$validator&lt;/span&gt; = Validation::&lt;span style=&#34;color:#369&#34;&gt;createValidatorBuilder&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        -&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;enableAnnotationMapping&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        -&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;getValidator&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#369&#34;&gt;$mockRepository&lt;/span&gt; = &lt;span style=&#34;color:#369&#34;&gt;$this&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;createMock&lt;/span&gt;(WeatherQueryRepository::&lt;span style=&#34;color:#369&#34;&gt;class&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#369&#34;&gt;$mockApiClient&lt;/span&gt; = &lt;span style=&#34;color:#369&#34;&gt;$this&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;createMock&lt;/span&gt;(WeatherApiClient::&lt;span style=&#34;color:#369&#34;&gt;class&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#369&#34;&gt;$mockApiClient&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;method&lt;/span&gt;(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;getCurrentWeather&amp;#39;&lt;/span&gt;)-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;willReturn&lt;/span&gt;([
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;success&amp;#39;&lt;/span&gt; =&amp;gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;true&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;response&amp;#39;&lt;/span&gt; =&amp;gt; &lt;span style=&#34;color:#369&#34;&gt;$this&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;testApiResponse&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ]);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#369&#34;&gt;$service&lt;/span&gt; = &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;new&lt;/span&gt; WeatherService(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#369&#34;&gt;$validator&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#369&#34;&gt;$mockRepository&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#369&#34;&gt;$mockApiClient&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    );
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// Expect
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#369&#34;&gt;$mockApiClient&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        -&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;expects&lt;/span&gt;(&lt;span style=&#34;color:#369&#34;&gt;$this&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;once&lt;/span&gt;())
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        -&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;method&lt;/span&gt;(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;getCurrentWeather&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        -&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;with&lt;/span&gt;(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;New York&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;NY&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// Act
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#369&#34;&gt;$service&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;getCurrentWeather&lt;/span&gt;(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;New York&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;NY&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;See how this test case is not verifying a result from a method call. Instead, it verifies whether our mock was called in a specific way by the unit under test. Also, our typical Arrange, Act, Assert formula has changed a bit. The test now reads Arrange, Expect, Act. This is how we write expectation-style test cases in PHPUnit. Let’s look at the statement:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#369&#34;&gt;$mockApiClient&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    -&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;expects&lt;/span&gt;(&lt;span style=&#34;color:#369&#34;&gt;$this&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;once&lt;/span&gt;())
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    -&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;method&lt;/span&gt;(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;getCurrentWeather&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    -&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;with&lt;/span&gt;(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;New York&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;NY&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This basically says: “In this test case, expect that the mock &lt;code&gt;WeatherApiClient&lt;/code&gt;’s &lt;code&gt;getCurrentWeather&lt;/code&gt; method gets called with &lt;code&gt;&#39;New York&#39;&lt;/code&gt; and &lt;code&gt;&#39;NY&#39;&lt;/code&gt; as parameters.” If, within the test case, this doesn’t happen, then it will be reported as a failure.&lt;/p&gt;
&lt;h4 id=&#34;the-thought-process-of-writing-tests-with-mocks&#34;&gt;The thought process of writing tests with mocks&lt;/h4&gt;
&lt;p&gt;How do we write these types of tests, though? Well, we read the code that we want to test line by line, try to identify the spots where dependencies are used, and how their results affect the behavior of the unit under test. This way we can mock them properly, configuring those mocks so that we can trigger the execution paths within the unit under test that we want to exercise. These types of tests are as &lt;a href=&#34;https://en.wikipedia.org/wiki/White-box_testing&#34;&gt;white-box&lt;/a&gt; as can be, and are really coupled with the implementation. Most changes to the implementation will break them. This is good because subtle bugs can be caught. This could also be bad because tests breaking often means more work fixing them. I personally like my tests to have this fine-grained focus, so I’m a big fan of mocking aggressively, but the most important thing is to find the balance that works best for you, your team and your project.&lt;/p&gt;
&lt;p&gt;To continue our example, if we look at the &lt;code&gt;getCurrentWeather&lt;/code&gt; method in &lt;code&gt;WeatherService&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;getCurrentWeather&lt;/span&gt;(string &lt;span style=&#34;color:#369&#34;&gt;$city&lt;/span&gt;, string &lt;span style=&#34;color:#369&#34;&gt;$state&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#369&#34;&gt;$weatherQuery&lt;/span&gt; = WeatherQuery::&lt;span style=&#34;color:#369&#34;&gt;build&lt;/span&gt;(&lt;span style=&#34;color:#369&#34;&gt;$city&lt;/span&gt;, &lt;span style=&#34;color:#369&#34;&gt;$state&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#369&#34;&gt;$this&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;repository&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;add&lt;/span&gt;(&lt;span style=&#34;color:#369&#34;&gt;$weatherQuery&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#369&#34;&gt;$result&lt;/span&gt; = &lt;span style=&#34;color:#369&#34;&gt;$this&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;apiClient&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;getCurrentWeather&lt;/span&gt;(&lt;span style=&#34;color:#369&#34;&gt;$city&lt;/span&gt;, &lt;span style=&#34;color:#369&#34;&gt;$state&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;if&lt;/span&gt; (!&lt;span style=&#34;color:#369&#34;&gt;$result&lt;/span&gt;[&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;success&amp;#39;&lt;/span&gt;]) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;return&lt;/span&gt; [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;success&amp;#39;&lt;/span&gt; =&amp;gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;false&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;weatherQuery&amp;#39;&lt;/span&gt; =&amp;gt; &lt;span style=&#34;color:#369&#34;&gt;$weatherQuery&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        ];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#369&#34;&gt;$apiResponse&lt;/span&gt; = &lt;span style=&#34;color:#369&#34;&gt;$result&lt;/span&gt;[&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;response&amp;#39;&lt;/span&gt;];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#369&#34;&gt;$weather&lt;/span&gt; = Weather::&lt;span style=&#34;color:#369&#34;&gt;build&lt;/span&gt;(&lt;span style=&#34;color:#369&#34;&gt;$weatherQuery&lt;/span&gt;, &lt;span style=&#34;color:#369&#34;&gt;$apiResponse&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;return&lt;/span&gt; [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;success&amp;#39;&lt;/span&gt; =&amp;gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;true&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;weather&amp;#39;&lt;/span&gt; =&amp;gt; &lt;span style=&#34;color:#369&#34;&gt;$weather&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;weatherQuery&amp;#39;&lt;/span&gt; =&amp;gt; &lt;span style=&#34;color:#369&#34;&gt;$weatherQuery&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We can work through it line by line and we see that:&lt;/p&gt;
&lt;p&gt;First, it takes its parameters and creates a new instance of &lt;code&gt;WeatherQuery&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#369&#34;&gt;$weatherQuery&lt;/span&gt; = WeatherQuery::&lt;span style=&#34;color:#369&#34;&gt;build&lt;/span&gt;(&lt;span style=&#34;color:#369&#34;&gt;$city&lt;/span&gt;, &lt;span style=&#34;color:#369&#34;&gt;$state&lt;/span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Nothing to do with this for now, from a testing perspective. We can’t mock anything here because it’s a static dependency. &lt;code&gt;WeatherQuery&lt;/code&gt; is being referenced directly and its static &lt;code&gt;build&lt;/code&gt; method is being called. Being a static dependency means that, as far as the test is concerned, &lt;code&gt;WeatherQuery::build&lt;/code&gt; may as well be defined inside &lt;code&gt;WeatherService&lt;/code&gt;. That’s why we normally should try to avoid static dependencies like this and prefer using interfaces and passing dependencies into objects via dependency injection. If we wanted to test &lt;code&gt;WeatherService&lt;/code&gt; without &lt;code&gt;WeatherQuery&lt;/code&gt;, we wouldn’t be able to do so. Because they are statically bound to one another. No big deal in this case, since this dependency is a simple factory method that saves &lt;code&gt;WeatherService&lt;/code&gt; from having to construct an object that’s pretty simple. If the logic to construct &lt;code&gt;WeatherQuery&lt;/code&gt; instances was too complex to warrant it being isolated from &lt;code&gt;WeatherService&lt;/code&gt; tests, then we’d be in trouble. We’d need to refactor to maybe add a new &lt;code&gt;WeatherQuery&lt;/code&gt; &lt;a href=&#34;https://en.wikipedia.org/wiki/Factory_method_pattern&#34;&gt;factory class&lt;/a&gt; and pass that into &lt;code&gt;WeatherService&lt;/code&gt; as an injected dependency that can be then mocked. We’re good with what we have now though, as it is not that complex.&lt;/p&gt;
&lt;p&gt;Then &lt;code&gt;getCurrentWeather&lt;/code&gt; calls on the repository to store the &lt;code&gt;WeatherQuery&lt;/code&gt; in the database:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#369&#34;&gt;$this&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;repository&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;add&lt;/span&gt;(&lt;span style=&#34;color:#369&#34;&gt;$weatherQuery&lt;/span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This one is interesting. It’s a textbook example of a side effect that we can easily write a test for using mocks. The mock in this case would be of the &lt;code&gt;WeatherQueryRepository&lt;/code&gt; dependency and we would verify that its &lt;code&gt;add&lt;/code&gt; method gets called with the expected parameter. This unit test would be impossible to write without mocks, because the result of this call to &lt;code&gt;WeatherQueryRepository&lt;/code&gt;’s &lt;code&gt;add&lt;/code&gt; is not captured nor returned as part of &lt;code&gt;getCurrentWeather&lt;/code&gt;’s result. The only insight that a test case could have into this aspect of &lt;code&gt;getCurrentWeather&lt;/code&gt;’s execution is via a mock.&lt;/p&gt;
&lt;p&gt;The test case that covers this line is &lt;code&gt;testGetCurrentWeatherCallsOnTheRepositoryToSaveANewWeatherQuery&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Next up, we’ve got:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#369&#34;&gt;$result&lt;/span&gt; = &lt;span style=&#34;color:#369&#34;&gt;$this&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;apiClient&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;getCurrentWeather&lt;/span&gt;(&lt;span style=&#34;color:#369&#34;&gt;$city&lt;/span&gt;, &lt;span style=&#34;color:#369&#34;&gt;$state&lt;/span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This calls the &lt;code&gt;WeatherApiClient&lt;/code&gt;’s &lt;code&gt;getCurrentWeather&lt;/code&gt; method. This is also one that we can test by mocking the dependency, and doing some behavior verification on whether and how the method was called. &lt;code&gt;testGetCurrentWeatherCallsOnTheApiClientToGetWeatherData&lt;/code&gt; does this.&lt;/p&gt;
&lt;p&gt;We can also do state verification because the result gets captured in a variable and used for the unit under test’s return value. As we can see in the conditional statement that follows:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;if&lt;/span&gt; (!&lt;span style=&#34;color:#369&#34;&gt;$result&lt;/span&gt;[&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;success&amp;#39;&lt;/span&gt;]) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;return&lt;/span&gt; [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;success&amp;#39;&lt;/span&gt; =&amp;gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;false&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;weatherQuery&amp;#39;&lt;/span&gt; =&amp;gt; &lt;span style=&#34;color:#369&#34;&gt;$weatherQuery&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The tests that cover this are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;testGetCurrentWeatherDoesNotReturnSuccessWhenTheApiCallIsUnsuccessful&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;testGetCurrentWeatherReturnsAWeatherQueryObjectWithCorrectFieldsWhenTheApiCallIsUnsuccessful&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Finally, the method captures the API response, builds a &lt;code&gt;Weather&lt;/code&gt; object based on it (again, a static dependency that we can’t intercept with a mock), and returns a result.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#369&#34;&gt;$apiResponse&lt;/span&gt; = &lt;span style=&#34;color:#369&#34;&gt;$result&lt;/span&gt;[&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;response&amp;#39;&lt;/span&gt;];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#369&#34;&gt;$weather&lt;/span&gt; = Weather::&lt;span style=&#34;color:#369&#34;&gt;build&lt;/span&gt;(&lt;span style=&#34;color:#369&#34;&gt;$weatherQuery&lt;/span&gt;, &lt;span style=&#34;color:#369&#34;&gt;$apiResponse&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;return&lt;/span&gt; [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;success&amp;#39;&lt;/span&gt; =&amp;gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;true&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;weather&amp;#39;&lt;/span&gt; =&amp;gt; &lt;span style=&#34;color:#369&#34;&gt;$weather&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;weatherQuery&amp;#39;&lt;/span&gt; =&amp;gt; &lt;span style=&#34;color:#369&#34;&gt;$weatherQuery&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;];&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Here, we can test that the resulting associative array’s fields are being generated correctly. We do that with this trifecta of test cases:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;testGetCurrentWeatherReturnsSuccessWhenTheApiCallIsSuccessful&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;testGetCurrentWeatherReturnsAWeatherObjectWithCorrectFieldsWhenTheApiCallIsSuccessful&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;testGetCurrentWeatherReturnsAWeatherQueryObjectWithCorrectFieldsWhenTheApiCallIsSuccessful&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You can run all of the unit tests in the demo with &lt;code&gt;bin/phpunit --testdox tests/unit&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&#34;integration-tests&#34;&gt;Integration tests&lt;/h3&gt;
&lt;p&gt;Like I said, I firmly believe that unit tests are the bread and butter of any serious automated test suite. However, I also like to do some higher-level tests to supplement the unit tests. Integration tests are such tests. These focus on exercising system behavior that interacts with external components like databases and web APIs. In fact, in our demo app’s test suite we have both. Let’s look into them.&lt;/p&gt;
&lt;h4 id=&#34;testing-database-interaction&#34;&gt;Testing database interaction&lt;/h4&gt;
&lt;p&gt;Many frameworks offer ways to help testing database interaction code by creating databases that are exclusive to the test environment. Symfony is no exception. The idea is simple: We create a new database that is exclusive for testing, configure the test runner to use that, and write some tests that exercise some code that talks to the database. Luckily for us, we decided to encapsulate all database interaction logic inside a repository class, so for our database integration tests, it’s obvious where we should focus on: &lt;code&gt;WeatherQueryRepository&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Consider the sole test case at &lt;code&gt;tests/functional/Repository/WeatherQueryRepositoryTest.php&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;testAddSavesANewRecordIntoTheDatabase&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// Arrange
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#369&#34;&gt;$testWeatherQuery1&lt;/span&gt; = WeatherQuery::&lt;span style=&#34;color:#369&#34;&gt;build&lt;/span&gt;(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;My City 1&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;MY STATE 1&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#369&#34;&gt;$testWeatherQuery2&lt;/span&gt; = WeatherQuery::&lt;span style=&#34;color:#369&#34;&gt;build&lt;/span&gt;(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;My City 2&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;MY STATE 2&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    self::&lt;span style=&#34;color:#369&#34;&gt;bootKernel&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#369&#34;&gt;$repository&lt;/span&gt; = self::&lt;span style=&#34;color:#369&#34;&gt;$container&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;get&lt;/span&gt;(WeatherQueryRepository::&lt;span style=&#34;color:#369&#34;&gt;class&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// Act
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#369&#34;&gt;$repository&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;add&lt;/span&gt;(&lt;span style=&#34;color:#369&#34;&gt;$testWeatherQuery1&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#369&#34;&gt;$repository&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;add&lt;/span&gt;(&lt;span style=&#34;color:#369&#34;&gt;$testWeatherQuery2&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// Assert
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#369&#34;&gt;$records&lt;/span&gt; = &lt;span style=&#34;color:#369&#34;&gt;$repository&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;findAll&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#369&#34;&gt;$this&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;assertEquals&lt;/span&gt;(&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;2&lt;/span&gt;, count(&lt;span style=&#34;color:#369&#34;&gt;$records&lt;/span&gt;));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#369&#34;&gt;$this&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;assertEquals&lt;/span&gt;(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;My City 1&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#369&#34;&gt;$records&lt;/span&gt;[&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0&lt;/span&gt;]-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;getCity&lt;/span&gt;());
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#369&#34;&gt;$this&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;assertEquals&lt;/span&gt;(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;MY STATE 1&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#369&#34;&gt;$records&lt;/span&gt;[&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0&lt;/span&gt;]-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;getState&lt;/span&gt;());
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#369&#34;&gt;$this&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;assertEquals&lt;/span&gt;(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;My City 2&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#369&#34;&gt;$records&lt;/span&gt;[&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1&lt;/span&gt;]-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;getCity&lt;/span&gt;());
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#369&#34;&gt;$this&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;assertEquals&lt;/span&gt;(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;MY STATE 2&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#369&#34;&gt;$records&lt;/span&gt;[&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1&lt;/span&gt;]-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;getState&lt;/span&gt;());
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This one is pretty straightforward. We create two objects, call the &lt;code&gt;add&lt;/code&gt; method to insert them as records, then query the database with &lt;code&gt;findAll&lt;/code&gt; and assert on the data to make sure the objects we get out from the database are the same that we put in.&lt;/p&gt;
&lt;p&gt;One interesting aspect about this test class is that we are calling two methods on &lt;code&gt;WeatherQueryRepository&lt;/code&gt;: &lt;code&gt;add&lt;/code&gt; and &lt;code&gt;findAll&lt;/code&gt;. However, we only have a test case for &lt;code&gt;add&lt;/code&gt;. Why is that? If we look at &lt;code&gt;WeatherQueryRepository&lt;/code&gt;’s implementation, we find out quickly: the &lt;code&gt;add&lt;/code&gt; method is the only method in our class. We get &lt;code&gt;findAll&lt;/code&gt; from &lt;code&gt;Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository&lt;/code&gt;, a framework base class that our repository inherits from. The test suite for our app should only be concerned in testing the code that we wrote and own. Our test suite has no business testing code that belongs to Symfony, Doctrine, or any other framework or library. Test cases for those belong in their test suites, not ours.&lt;/p&gt;
&lt;p&gt;Another interesting point is that we can’t create a “real” &lt;code&gt;WeatherQueryRepository&lt;/code&gt; to play with on our own, outside of the context of Symfony. That’s why we:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;have this test class extend &lt;code&gt;Symfony\Bundle\FrameworkBundle\Test\KernelTestCase&lt;/code&gt; instead of plain old &lt;code&gt;PHPUnit\Framework\TestCase&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;have to go through some hoops to obtain a &lt;code&gt;WeatherQueryRepository&lt;/code&gt; instance:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;self::&lt;span style=&#34;color:#369&#34;&gt;bootKernel&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#369&#34;&gt;$repository&lt;/span&gt; = self::&lt;span style=&#34;color:#369&#34;&gt;$container&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;get&lt;/span&gt;(WeatherQueryRepository::&lt;span style=&#34;color:#369&#34;&gt;class&lt;/span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This is just how Symfony allows us to obtain fully featured objects in the context of our tests, just as they would be if they were being executed in the application’s normal day-to-day runtime. We ask Symfony for an instance instead of us directly &lt;code&gt;new&lt;/code&gt;ing it up. And this is good news, because with a framework like Symfony that uses dependency injection so heavily, and many of our custom classes end up depending on complex framework objects, instantiating them on our own can get complicated.&lt;/p&gt;
&lt;p&gt;In Symfony, there are a few configurations that need to happen so that we are able to run database integration tests. &lt;a href=&#34;https://symfony.com/doc/current/testing/database.html&#34;&gt;Symfony’s own documentation&lt;/a&gt; is excellent for learning how to, but in a nutshell, here’s what needs to happen:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Install this bundle which allows each test case to run with the same, unmodified database: &lt;code&gt;composer require --dev dama/doctrine-test-bundle&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Enable it by adding the following to the &lt;code&gt;phpunit.xml.dist&lt;/code&gt; file:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-xml&#34; data-lang=&#34;xml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;phpunit&amp;gt;&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;&amp;lt;!-- ... --&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;extensions&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;extension&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;class=&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;DAMA\DoctrineTestBundle\PHPUnit\PHPUnitExtension&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;/extensions&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;&amp;lt;!-- ... --&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;/phpunit&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create a test database with &lt;code&gt;bin/console doctrine:schema:create --env=test&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Once that setup is done, tests can be run with something like &lt;code&gt;tests/functional/Repository/WeatherQueryRepositoryTest.php&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ bin/phpunit --testdox tests/functional/Repository/WeatherQueryRepositoryTest.php
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;PHPUnit 7.5.20 by Sebastian Bergmann and contributors.
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Testing App&lt;span style=&#34;color:#04d;background-color:#fff0f0&#34;&gt;\T&lt;/span&gt;ests&lt;span style=&#34;color:#04d;background-color:#fff0f0&#34;&gt;\F&lt;/span&gt;unctional&lt;span style=&#34;color:#04d;background-color:#fff0f0&#34;&gt;\R&lt;/span&gt;epository&lt;span style=&#34;color:#04d;background-color:#fff0f0&#34;&gt;\W&lt;/span&gt;eatherQueryRepositoryTest
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;App&lt;span style=&#34;color:#04d;background-color:#fff0f0&#34;&gt;\T&lt;/span&gt;ests&lt;span style=&#34;color:#04d;background-color:#fff0f0&#34;&gt;\F&lt;/span&gt;unctional&lt;span style=&#34;color:#04d;background-color:#fff0f0&#34;&gt;\R&lt;/span&gt;epository&lt;span style=&#34;color:#04d;background-color:#fff0f0&#34;&gt;\W&lt;/span&gt;eatherQueryRepository
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; ✔ Add saves a new record into the database
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Time: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;160&lt;/span&gt; ms, Memory: 20.00 MB
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;OK (&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1&lt;/span&gt; test, &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;5&lt;/span&gt; assertions)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h4 id=&#34;testing-calls-to-an-external-web-api&#34;&gt;Testing calls to an external Web API&lt;/h4&gt;
&lt;p&gt;Another interesting aspect of our application that our integration test should cover is the logic that calls on the OpenWeatherMap Web API. In &lt;code&gt;tests/functional/Service/WeatherApiClientTest.php&lt;/code&gt;, we’ve done just that. Look at the file and you’ll see that it is pretty similar to our other integration test that we wrote for &lt;code&gt;WeatherQueryRepository&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;?php
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;namespace&lt;/span&gt; App\Tests\Functional\Service;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;use&lt;/span&gt; Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;use&lt;/span&gt; App\Service\WeatherApiClient;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;WeatherApiClientTest&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;extends&lt;/span&gt; KernelTestCase
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;testGetCurrentWeatherInvokesTheApiAndReturnsTheExpectedResponse&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#888&#34;&gt;// Arrange
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;        self::&lt;span style=&#34;color:#369&#34;&gt;bootKernel&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#369&#34;&gt;$apiClient&lt;/span&gt; = self::&lt;span style=&#34;color:#369&#34;&gt;$container&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;get&lt;/span&gt;(WeatherApiClient::&lt;span style=&#34;color:#369&#34;&gt;class&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#888&#34;&gt;// Act
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;        &lt;span style=&#34;color:#369&#34;&gt;$result&lt;/span&gt; = &lt;span style=&#34;color:#369&#34;&gt;$apiClient&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;getCurrentWeather&lt;/span&gt;(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;New York&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;NY&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#888&#34;&gt;// Assert
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;        &lt;span style=&#34;color:#369&#34;&gt;$this&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;assertArrayHasKey&lt;/span&gt;(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;weather&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#369&#34;&gt;$result&lt;/span&gt;[&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;response&amp;#39;&lt;/span&gt;]);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#369&#34;&gt;$this&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;assertGreaterThanOrEqual&lt;/span&gt;(&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1&lt;/span&gt;, &lt;span style=&#34;color:#369&#34;&gt;$result&lt;/span&gt;[&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;response&amp;#39;&lt;/span&gt;][&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;weather&amp;#39;&lt;/span&gt;]);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#369&#34;&gt;$this&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;assertArrayHasKey&lt;/span&gt;(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;main&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#369&#34;&gt;$result&lt;/span&gt;[&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;response&amp;#39;&lt;/span&gt;][&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;weather&amp;#39;&lt;/span&gt;][&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0&lt;/span&gt;]);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#369&#34;&gt;$this&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;assertArrayHasKey&lt;/span&gt;(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;description&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#369&#34;&gt;$result&lt;/span&gt;[&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;response&amp;#39;&lt;/span&gt;][&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;weather&amp;#39;&lt;/span&gt;][&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0&lt;/span&gt;]);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#369&#34;&gt;$this&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;assertArrayHasKey&lt;/span&gt;(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;main&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#369&#34;&gt;$result&lt;/span&gt;[&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;response&amp;#39;&lt;/span&gt;]);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#369&#34;&gt;$this&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;assertArrayHasKey&lt;/span&gt;(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;temp&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#369&#34;&gt;$result&lt;/span&gt;[&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;response&amp;#39;&lt;/span&gt;][&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;main&amp;#39;&lt;/span&gt;]);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#369&#34;&gt;$this&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;assertArrayHasKey&lt;/span&gt;(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;feels_like&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#369&#34;&gt;$result&lt;/span&gt;[&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;response&amp;#39;&lt;/span&gt;][&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;main&amp;#39;&lt;/span&gt;]);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#369&#34;&gt;$this&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;assertArrayHasKey&lt;/span&gt;(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;temp_min&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#369&#34;&gt;$result&lt;/span&gt;[&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;response&amp;#39;&lt;/span&gt;][&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;main&amp;#39;&lt;/span&gt;]);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#369&#34;&gt;$this&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;assertArrayHasKey&lt;/span&gt;(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;temp_max&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#369&#34;&gt;$result&lt;/span&gt;[&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;response&amp;#39;&lt;/span&gt;][&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;main&amp;#39;&lt;/span&gt;]);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#369&#34;&gt;$this&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;assertArrayHasKey&lt;/span&gt;(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;pressure&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#369&#34;&gt;$result&lt;/span&gt;[&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;response&amp;#39;&lt;/span&gt;][&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;main&amp;#39;&lt;/span&gt;]);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#369&#34;&gt;$this&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;assertArrayHasKey&lt;/span&gt;(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;humidity&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#369&#34;&gt;$result&lt;/span&gt;[&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;response&amp;#39;&lt;/span&gt;][&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;main&amp;#39;&lt;/span&gt;]);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#369&#34;&gt;$this&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;assertArrayHasKey&lt;/span&gt;(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;visibility&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#369&#34;&gt;$result&lt;/span&gt;[&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;response&amp;#39;&lt;/span&gt;]);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#369&#34;&gt;$this&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;assertArrayHasKey&lt;/span&gt;(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;wind&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#369&#34;&gt;$result&lt;/span&gt;[&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;response&amp;#39;&lt;/span&gt;]);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#369&#34;&gt;$this&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;assertArrayHasKey&lt;/span&gt;(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;speed&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#369&#34;&gt;$result&lt;/span&gt;[&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;response&amp;#39;&lt;/span&gt;][&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;wind&amp;#39;&lt;/span&gt;]);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#369&#34;&gt;$this&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;assertArrayHasKey&lt;/span&gt;(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;deg&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#369&#34;&gt;$result&lt;/span&gt;[&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;response&amp;#39;&lt;/span&gt;][&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;wind&amp;#39;&lt;/span&gt;]);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The same key elements are here: A test class that inherits from &lt;code&gt;Symfony\Bundle\FrameworkBundle\Test\KernelTestCase&lt;/code&gt;, a test method that starts up the Symfony kernel and obtains a fully configured instance of &lt;code&gt;WeatherApiClient&lt;/code&gt;, and the usual Arrange, Act, and Assert structure that should be familiar by now. The Act portion just calls the method that we want to test and captures its return value. The assertions are done using state verification to validate that the result from the call to the API contains the data that we expect. Simple.&lt;/p&gt;
&lt;p&gt;Running this test with a command like &lt;code&gt;bin/phpunit tests/functional/Service/WeatherApiClientTest.php&lt;/code&gt; will actually make a real HTTP request to the OpenWeatherMap Web API and return back its response.&lt;/p&gt;
&lt;p&gt;Perhaps the most interesting aspect to discuss about this integration test is how different it is from a unit test written against the same class. If we look inside &lt;code&gt;src/Service/WeatherApiClient.php&lt;/code&gt;, we see that the &lt;code&gt;getCurrentWeather&lt;/code&gt; method is where the magic happens:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;getCurrentWeather&lt;/span&gt;(string &lt;span style=&#34;color:#369&#34;&gt;$city&lt;/span&gt;, string &lt;span style=&#34;color:#369&#34;&gt;$state&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#369&#34;&gt;$response&lt;/span&gt; = &lt;span style=&#34;color:#369&#34;&gt;$this&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;httpClient&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;request&lt;/span&gt;(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;GET&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;http://api.openweathermap.org/data/2.5/weather?q=&amp;#39;&lt;/span&gt; . &lt;span style=&#34;color:#369&#34;&gt;$city&lt;/span&gt; . &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;,&amp;#39;&lt;/span&gt; . &lt;span style=&#34;color:#369&#34;&gt;$state&lt;/span&gt; . &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;,us&amp;amp;appid=&amp;#39;&lt;/span&gt; . &lt;span style=&#34;color:#369&#34;&gt;$this&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;openWeatherMapAppId&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    );
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#369&#34;&gt;$response&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;getStatusCode&lt;/span&gt;() != &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;200&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;return&lt;/span&gt; [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;success&amp;#39;&lt;/span&gt; =&amp;gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;false&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        ];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#369&#34;&gt;$response&lt;/span&gt; = json_decode(&lt;span style=&#34;color:#369&#34;&gt;$response&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;getContent&lt;/span&gt;(), &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;true&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;return&lt;/span&gt; [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;success&amp;#39;&lt;/span&gt; =&amp;gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;true&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;response&amp;#39;&lt;/span&gt; =&amp;gt; &lt;span style=&#34;color:#369&#34;&gt;$response&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The method leverages the &lt;code&gt;$httpClient&lt;/code&gt; (which is an object of type &lt;code&gt;Symfony\Contracts\HttpClient\HttpClientInterface&lt;/code&gt;, injected into our &lt;code&gt;WeatherApiClient&lt;/code&gt; class by Symfony), to make an HTTP request to the OpenWeatherMap Web API endpoint. Depending on its response, it constructs and returns a result value. There’s some logic in this method, calls to dependencies being made, results being inspected, conditionals, associative arrays being constructed, etc. Our integration test doesn’t care about any of that. It only cares that the Web API is getting called and that it returns what it expects.&lt;/p&gt;
&lt;p&gt;Unit tests, on the other hand, do care about these details. As a result, &lt;code&gt;tests/unit/Service/WeatherApiClientTest.php&lt;/code&gt;, which contains the unit tests for that same class, goes more in depth into it and exercises all the possible code paths, while providing a mock for the &lt;code&gt;HttpClientInterface&lt;/code&gt; dependency. This makes sure that the unit test does not execute code from anything other than the unit under test. So, we end up with unit test cases like:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;testGetCurrentWeatherCallsOnTheHttpClientToMakeAGetRequestToTheWeatherApi&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;testGetCurrentWeatherDoesNotReturnSuccessIfTheResponseStatusCodeIsNot200&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;testGetCurrentWeatherReturnsSuccessAndResponseDataAsArrayIfTheResponseStatusCodeIs200&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Which should be self-explanatory at this point.&lt;/p&gt;
&lt;p&gt;Essentially, the integration test would break if the API was down at some point, or if its contract has changed and we call it incorrectly. The unit test would break if somebody modifies the way the algorithm functions. They complement each other well.&lt;/p&gt;
&lt;h3 id=&#34;functional-tests&#34;&gt;Functional tests&lt;/h3&gt;
&lt;p&gt;Now it’s time to move on to the final type of test that we’re going to discuss in this article, and the one that sits at the highest level of abstraction: functional tests. The focus of this kind of test is to exercise the complete system, end to end, while trying to mimic the experience that a user would have with it.&lt;/p&gt;
&lt;p&gt;To achieve this, Symfony gives us some tools that we can use to interact with the application at the protocol level, just like a browser would. That is, sending HTTP requests to and inspecting the responses from a complete application running in a sandbox, fully configured and integrated with all of its external components like databases, etc.&lt;/p&gt;
&lt;p&gt;Let’s look at &lt;code&gt;tests/functional/Controller/WeatherControllerTest.php&lt;/code&gt; to see that in action. The first thing to note is that functional test classes need to inherit from &lt;code&gt;Symfony\Bundle\FrameworkBundle\Test\WebTestCase&lt;/code&gt;. This is how we tell Symfony that we need the tools necessary to talk to our application as if we were doing so via HTTP.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;use&lt;/span&gt; Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;WeatherControllerTest&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;extends&lt;/span&gt; WebTestCase
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// ...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Then, in the test cases themselves, we find our usual three-step structure. Take the first one, for example:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;testIndexWorks&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// Arrange
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#369&#34;&gt;$client&lt;/span&gt; = &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;static&lt;/span&gt;::&lt;span style=&#34;color:#369&#34;&gt;createClient&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// Act
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#369&#34;&gt;$client&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;request&lt;/span&gt;(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;GET&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;/&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// Assert
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#369&#34;&gt;$this&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;assertResponseStatusCodeSame&lt;/span&gt;(&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;200&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This is a simple test which makes a GET request to the root route of the site and expects it to return a 200 HTTP status code. Notice how we use the &lt;code&gt;static::createClient()&lt;/code&gt; method to obtain an HTTP client object that we can use to talk to our application. Next we use the client to do just that with &lt;code&gt;$client-&amp;gt;request(&#39;GET&#39;, &#39;/&#39;);&lt;/code&gt; and finally use the &lt;code&gt;assertResponseStatusCodeSame&lt;/code&gt; assertion to check that the request was successful. All of these methods are available for our test because we defined our test class by extending &lt;code&gt;WebTestCase&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;We can go much deeper into our assertions on the responses though. Look at the &lt;code&gt;testShowDisplaysAllWeatherInfoIfGivenValidInput&lt;/code&gt; test case, for example:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;testShowDisplaysAllWeatherInfoIfGivenValidInput&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// Arrange
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#369&#34;&gt;$client&lt;/span&gt; = &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;static&lt;/span&gt;::&lt;span style=&#34;color:#369&#34;&gt;createClient&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// Act
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#369&#34;&gt;$client&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;request&lt;/span&gt;(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;GET&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;/show/New%20York/NY&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// Assert
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#369&#34;&gt;$this&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;assertSelectorTextContains&lt;/span&gt;(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;div.wrapper&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;The current weather&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#369&#34;&gt;$this&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;assertSelectorTextContains&lt;/span&gt;(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;div.wrapper&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;Temp&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#369&#34;&gt;$this&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;assertSelectorTextContains&lt;/span&gt;(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;div.wrapper&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;Feels like&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// ...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This is very similar to the previous one. The main differences are that, first, we’re requesting a different route (the one that results in a page that presents the actual weather data that was requested), and second, that instead of only asserting HTTP status code of the response, we assert on the actual contents of the page that would be rendered in a browser. We can use &lt;code&gt;assertSelectorTextContains&lt;/code&gt; for that.&lt;/p&gt;
&lt;p&gt;Symfony includes many more goodies that help with functional testing. You can learn more &lt;a href=&#34;https://symfony.com/doc/current/testing.html#functional-tests&#34;&gt;in the official docs&lt;/a&gt;. There are a few more examples in the demo source code so you may want to look them over now that you understand what their intent is and how they are written.&lt;/p&gt;
&lt;h3 id=&#34;closing-thoughts&#34;&gt;Closing thoughts&lt;/h3&gt;
&lt;p&gt;In this article, we’ve gone through some of the basics of automated testing for web apps. We’ve used Symfony as a vehicle for conveying that information and, as such, learned a little bit about what it takes to build a multifaceted test suite with that framework. And we did all that by offering many examples from a real (albeit simple) test suite that works.&lt;/p&gt;
&lt;p&gt;We’ve discussed the three types of tests that I like to use in terms of their level of abstraction: unit tests, integration tests, and functional tests; and what they are, what aspect of the system they focus on, and how to write them. I believe that a test suite that offers good coverage while including these three kinds of tests offers the best bang for the buck and gives us a cozy safety net to fall back to while we develop our applications. These three levels of testing complement each other very well.&lt;/p&gt;
&lt;p&gt;We’ve also touched on the general approach and thought process behind writing automated tests. We discussed a three step approach: Arrange, Act, and Assert. We also talked about test cases that do classic state verification, where input is provided to a unit under test, the unit is exercised, and finally its output is verified.&lt;/p&gt;
&lt;p&gt;We also saw a more advanced testing technique where, by leveraging mock objects, we do behavior verification. Here, we assert on whether the unit under test has called its collaborators/​dependencies in a certain way, a style which is great for testing side effects and classes that need other dependencies in order to work. These two styles of tests work best when used together to compose a larger test suite.&lt;/p&gt;
&lt;p&gt;And that’s it for now! This has been quite a trip into some aspects of software testing. Like I said at the beginning, this discipline is huge, and one never stops learning. But I think what we’ve discussed here is a good starting point to continue deepening our understanding while also serving as a competent enough strategy to use in a real-world application.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Job opening: PHP / JavaScript developer</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2020/06/job-php-javascript-developer/"/>
      <id>https://www.endpointdev.com/blog/2020/06/job-php-javascript-developer/</id>
      <published>2020-06-29T00:00:00+00:00</published>
      <author>
        <name>Jon Jensen</name>
      </author>
      <content type="html">
        &lt;img src=&#34;/blog/2020/06/job-php-javascript-developer/20200518-194421-sm.jpg&#34; alt=&#34;waterfall and mountains&#34; /&gt;
&lt;!-- Photo by Jon Jensen --&gt;
&lt;p&gt;We are looking for a PHP software engineer to work with us during business hours somewhere in the UTC-7 to UTC-4 time zones (U.S. Pacific to Eastern Time). This role can be full-time or part-time.&lt;/p&gt;
&lt;p&gt;We are an Internet technology consulting company based in New York City, with 50 employees serving many clients ranging from small family businesses to large corporations. The company turns 25 years old this year!&lt;/p&gt;
&lt;p&gt;Even before COVID-19 most of us worked remotely from home offices. We collaborate using SSH, GitHub, GitLab, chat, video conferencing, and of course email and phones.&lt;/p&gt;
&lt;h3 id=&#34;what-you-will-be-doing&#34;&gt;What you will be doing:&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Develop new web applications and support existing ones for our clients&lt;/li&gt;
&lt;li&gt;Work together with End Point co-workers and our clients’ in-house staff&lt;/li&gt;
&lt;li&gt;Use your desktop OS of choice: Linux, macOS, Windows&lt;/li&gt;
&lt;li&gt;Use open source tools and contribute back as opportunity arises&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;what-you-bring&#34;&gt;What you bring:&lt;/h3&gt;
&lt;p&gt;Professional experience developing and supporting web applications in these technical areas:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;5+ years of development with PHP and front-end JavaScript&lt;/li&gt;
&lt;li&gt;Frameworks such as Symfony, Laravel, Magento and Vue.js, React, Angular&lt;/li&gt;
&lt;li&gt;Databases such as PostgreSQL, MySQL, MongoDB, Redis, Solr, Elasticsearch, etc.&lt;/li&gt;
&lt;li&gt;Security consciousness&lt;/li&gt;
&lt;li&gt;Git version control&lt;/li&gt;
&lt;li&gt;Automated testing&lt;/li&gt;
&lt;li&gt;HTTP, REST APIs&lt;/li&gt;
&lt;li&gt;Bonus for familiarity with another ecosystem such as Ruby on Rails, Python/​Django, Java, .NET/​C#, Node.js, Go, Rust, Scala, Kotlin, Swift …&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These work traits are just as important:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Strong verbal and written communication skills&lt;/li&gt;
&lt;li&gt;An eye for detail&lt;/li&gt;
&lt;li&gt;Tenacity in solving problems&lt;/li&gt;
&lt;li&gt;A feeling of ownership of your projects&lt;/li&gt;
&lt;li&gt;Work both independently and as part of a team&lt;/li&gt;
&lt;li&gt;Focus on customer needs&lt;/li&gt;
&lt;li&gt;A good remote work environment&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;what-work-here-offers&#34;&gt;What work here offers:&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Collaborate with knowledgeable, friendly, helpful, and diligent co-workers around the world&lt;/li&gt;
&lt;li&gt;Flexible, sane work hours&lt;/li&gt;
&lt;li&gt;Annual bonus opportunity&lt;/li&gt;
&lt;li&gt;Freedom from being tied to an office location&lt;/li&gt;
&lt;li&gt;For full-time staff: paid holidays and vacation&lt;/li&gt;
&lt;li&gt;For U.S. employees: health insurance subsidy and 401(k) retirement savings plan&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;get-in-touch-with-us&#34;&gt;Get in touch with us:&lt;/h3&gt;
&lt;p&gt;&lt;del&gt;Please email us an introduction to jobs@endpointdev.com to apply.&lt;/del&gt;
&lt;strong&gt;(This job has been filled.)&lt;/strong&gt;
Include your location, a resume/​CV, your GitHub or LinkedIn URLs, and whatever else helps us get to know you.&lt;/p&gt;
&lt;p&gt;We look forward to hearing from you! Direct work seekers only, please—​this role is not for agencies or subcontractors.&lt;/p&gt;
&lt;p&gt;We are an equal opportunity employer and value diversity at our company. We do not discriminate on the basis of sex/​gender, race, religion, color, national origin, sexual orientation, age, marital status, veteran status, or disability status.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Magento 2: Creating a custom theme</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2020/06/magento-2-creating-a-custom-theme/"/>
      <id>https://www.endpointdev.com/blog/2020/06/magento-2-creating-a-custom-theme/</id>
      <published>2020-06-24T00:00:00+00:00</published>
      <author>
        <name>Juan Pablo Ventoso</name>
      </author>
      <content type="html">
        &lt;p&gt;&lt;img src=&#34;/blog/2020/06/magento-2-creating-a-custom-theme/paint-orange-blue.jpg&#34; alt=&#34;blue and yellow paint from a tube on a canvas&#34;&gt;
&lt;a href=&#34;https://flic.kr/p/Se3vkA&#34;&gt;Photo&lt;/a&gt; by &lt;a href=&#34;https://www.flickr.com/photos/mariaeklind/&#34;&gt;Maria Eklind&lt;/a&gt;, &lt;a href=&#34;https://creativecommons.org/licenses/by-sa/2.0/&#34;&gt;CC BY-SA 2.0&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;In my previous post, we went through the steps needed to &lt;a href=&#34;/blog/2020/04/magento-2-creating-a-custom-module/&#34;&gt;create a custom module in Magento 2&lt;/a&gt;. While modules consist of a set of classes to add new features to Magento, a theme controls how these features, and the entire website in general, will be displayed to the user. As stated in the &lt;a href=&#34;https://devdocs.magento.com/guides/v2.3/frontend-dev-guide/themes/theme-overview.html&#34;&gt;Magento guide&lt;/a&gt;, a theme uses a combination of custom templates, layouts, styles, and images to provide a consistent look and feel across a Magento store.&lt;/p&gt;
&lt;h3 id=&#34;creating-a-new-magento-2-theme&#34;&gt;Creating a new Magento 2 theme&lt;/h3&gt;
&lt;p&gt;We can create a theme based on a default “parent” theme or create a standalone theme from scratch. In most cases, I would recommend the first option. For this example, we will use &lt;a href=&#34;https://magento2-demo.magebit.com/&#34;&gt;Luma&lt;/a&gt; as our parent theme. The other option would be inheriting from the default “blank” theme.&lt;/p&gt;
&lt;p&gt;Here’s an initial task list to get our new theme ready:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Create a new directory for the theme&lt;/li&gt;
&lt;li&gt;Create the &lt;code&gt;registration.php&lt;/code&gt; script&lt;/li&gt;
&lt;li&gt;Create the &lt;code&gt;theme.xml&lt;/code&gt; information file&lt;/li&gt;
&lt;li&gt;Activate the new theme&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&#34;creating-a-new-directory-for-the-theme&#34;&gt;Creating a new directory for the theme&lt;/h4&gt;
&lt;p&gt;While all our backend code should go in &lt;code&gt;app/code&lt;/code&gt;, the frontend content is expected to go in &lt;code&gt;app/design&lt;/code&gt;. And as our theme will only apply design changes to the frontend content, we should create the new directory for it under the path &lt;code&gt;app/design/frontend&lt;/code&gt;. If we want to create a theme for the admin area instead, we need to create the directory inside &lt;code&gt;app/design/adminhtml&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Let’s create a directory named &lt;code&gt;EndPoint&lt;/code&gt; (our vendor name, continuing with the example from our previous article) and a subdirectory inside it, &lt;code&gt;MyTheme&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#038&#34;&gt;cd&lt;/span&gt; {website_root}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;mkdir -p app/design/frontend/EndPoint/MyTheme&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h4 id=&#34;creating-registrationphp&#34;&gt;Creating registration.php&lt;/h4&gt;
&lt;p&gt;Similar to the file we created for our module, &lt;code&gt;registration.php&lt;/code&gt; tells Magento to register the new theme with the name and location we specify. Our file will be located at &lt;code&gt;app/design/frontend/EndPoint/MyTheme/registration.php&lt;/code&gt; and should have the following content:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;?php
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;\Magento\Framework\Component\ComponentRegistrar::&lt;span style=&#34;color:#369&#34;&gt;register&lt;/span&gt;(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    \Magento\Framework\Component\ComponentRegistrar::&lt;span style=&#34;color:#369&#34;&gt;THEME&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;frontend/EndPoint/MyTheme&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;__DIR__&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This way, Magento will know what path our theme will have.&lt;/p&gt;
&lt;h4 id=&#34;creating-themexml&#34;&gt;Creating theme.xml&lt;/h4&gt;
&lt;p&gt;The next step is to create our theme information file, where we will specify the theme name and parent theme. So our &lt;code&gt;app/design/frontend/EndPoint/MyTheme/theme.xml&lt;/code&gt; file should have the following content:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-xml&#34; data-lang=&#34;xml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#c00;font-weight:bold&#34;&gt;&amp;lt;?xml version=&amp;#34;1.0&amp;#34;?&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;theme&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;xmlns:xsi=&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;http://www.w3.org/2001/XMLSchema-instance&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;xsi:noNamespaceSchemaLocation=&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;urn:magento:framework:Config/etc/theme.xsd&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;title&amp;gt;&lt;/span&gt;MyTheme&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;parent&amp;gt;&lt;/span&gt;Magento/luma&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;/parent&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;/theme&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;em&gt;Optional&lt;/em&gt;: If we want our theme to be easily distributed as a package, we can also create a &lt;code&gt;composer.json&lt;/code&gt; file in the theme’s root directory. The content for the file should be as follows, specifying the theme’s description, dependencies, version, and license types:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;name&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;endpoint/mytheme&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;description&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;My Theme by End Point&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;require&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;magento/theme-frontend-luma&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;100.0.*&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;magento/framework&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;100.0.*&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;type&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;magento2-theme&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;version&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;100.0.1&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;license&amp;#34;&lt;/span&gt;: [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;OSL-3.0&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;AFL-3.0&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;autoload&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;files&amp;#34;&lt;/span&gt;: [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;registration.php&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        ]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h4 id=&#34;activating-our-new-theme&#34;&gt;Activating our new theme&lt;/h4&gt;
&lt;p&gt;That was easy! We have everything we need to activate our new theme. To do that we log in to our admin area and enable our theme. Once in the dashboard, we need to go to Content &amp;gt; Design &amp;gt; Configuration, edit our store view, and select our new theme from the dropdown list:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;magento-2-creating-a-custom-theme/magento-admin-select-theme.jpg&#34; alt=&#34;Selecting our theme&#34;&gt;&lt;/p&gt;
&lt;p&gt;Magento will search for new themes every time we log in to the admin area, so our new theme will appear on the list automatically.&lt;/p&gt;
&lt;h3 id=&#34;adding-custom-content&#34;&gt;Adding custom content&lt;/h3&gt;
&lt;p&gt;We have the basic structure for our theme, but when enabled, it will look the same as its parent theme (Luma, in this case), since we didn’t add any design rules or static files yet. Let’s do some more things with our theme to change how it’s displayed in the frontend:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Create a custom &lt;code&gt;etc/view.xml&lt;/code&gt; file&lt;/li&gt;
&lt;li&gt;Add a custom logo&lt;/li&gt;
&lt;li&gt;Add static files (JavaScript, CSS, images, fonts)&lt;/li&gt;
&lt;li&gt;Add a custom layout&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&#34;creating-a-custom-view-file&#34;&gt;Creating a custom view file&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;etc/view.xml&lt;/code&gt; controls many frontend configurations like the product thumbnail width, how the product image gallery is displayed, and the image magnifier tool, among other things. To add our custom view file to our theme, we need to copy the existing file from our parent theme. For Luma, it will be located at &lt;code&gt;vendor/magento/theme-frontend-luma/etc/view.xml&lt;/code&gt;. To copy the file, we need to run the following in our website’s root:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;mkdir -p app/design/frontend/EndPoint/MyTheme/etc
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;cp vendor/magento/theme-frontend-blank/etc/view.xml app/design/frontend/EndPoint/MyTheme/etc/view.xml&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And then we can use our preferred text editor to change the values we want, like setting a custom size for the images in the category page grid:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-html&#34; data-lang=&#34;html&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;image&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;id&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;category_page_grid&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;type&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;small_image&amp;#34;&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;width&lt;/span&gt;&amp;gt;300&amp;lt;/&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;width&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;height&lt;/span&gt;&amp;gt;300&amp;lt;/&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;height&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;/&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;image&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h4 id=&#34;adding-a-custom-logo&#34;&gt;Adding a custom logo&lt;/h4&gt;
&lt;p&gt;Adding a logo to our theme is really simple. We just need to save our picture in SVG format as &lt;code&gt;web/images/logo.svg&lt;/code&gt;. If we want to use a different filename or format for our logo, we will have to create a default layout file for our theme in the path &lt;code&gt;/Magento_Theme/layout/default.xml&lt;/code&gt; inside our theme root with content similar to this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-xml&#34; data-lang=&#34;xml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;page&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;xmlns:xsi=&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;http://www.w3.org/2001/XMLSchema-instance&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;xsi:noNamespaceSchemaLocation=&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;urn:magento:framework:View/Layout/etc/page_configuration.xsd&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;referenceBlock&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;name=&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;logo&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;arguments&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;argument&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;name=&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;logo_file&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;xsi:type=&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;string&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;gt;&lt;/span&gt;images/custom_logo.png&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;/argument&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;argument&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;name=&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;logo_width&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;xsi:type=&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;number&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;gt;&lt;/span&gt;300&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;/argument&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;argument&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;name=&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;logo_height&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;xsi:type=&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;number&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;gt;&lt;/span&gt;200&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;/argument&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;argument&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;name=&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;logo_alt&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;xsi:type=&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;string&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;gt;&lt;/span&gt;Custom logo name&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;/argument&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;/arguments&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;/referenceBlock&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;/page&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We can use different image formats such as SVG, PNG, or JPG. We can also use a custom width and height for the logo, and set a custom alternate text.&lt;/p&gt;
&lt;h4 id=&#34;adding-static-files-javascriptcssimagesfonts&#34;&gt;Adding static files (JavaScript/​CSS/​images/​fonts)&lt;/h4&gt;
&lt;p&gt;All the static files should be located inside the &lt;code&gt;web&lt;/code&gt; directory. Common static files include JavaScript files, stylesheets, images, and fonts. The JavaScript files should be located at &lt;code&gt;web/js&lt;/code&gt;, stylesheets at &lt;code&gt;web/css&lt;/code&gt;, images at &lt;code&gt;web/images&lt;/code&gt;, and our custom fonts should be located at &lt;code&gt;web/fonts&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;All the static files will be published as direct links, without any processing from Magento, at the &lt;code&gt;pub/static/frontend/EndPoint/MyTheme/en_US&lt;/code&gt; path. The default locale/​language is en_US; we can change it for our theme if needed.&lt;/p&gt;
&lt;h4 id=&#34;adding-a-custom-layout&#34;&gt;Adding a custom layout&lt;/h4&gt;
&lt;p&gt;Finally, if we want to use the new assets we added and have custom content on different sections of our website, we need to extend or override the existing layout files from our parent theme.&lt;/p&gt;
&lt;p&gt;For example, if we want to add a reference to new stylesheet and JavaScript files we added, we need to extend the existing header layout from our parent theme. To do this, we will create a new layout file located at &lt;code&gt;Magento_Theme/layout/default_head_blocks.xml&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-xml&#34; data-lang=&#34;xml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;page&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;xmlns:xsi=&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;http://www.w3.org/2001/XMLSchema-instance&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;xsi:noNamespaceSchemaLocation=&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;urn:magento:framework:View/Layout/etc/page_configuration.xsd&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;&amp;lt;!-- Custom stylesheet --&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;css&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;src=&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;css/mytheme.css&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;&amp;lt;!-- Custom JavaScript --&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;script&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;src=&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;js/mytheme.js&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;/page&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This way we will be adding a reference to a new stylesheet named &lt;code&gt;mytheme.css&lt;/code&gt; that we have inside the &lt;code&gt;web/css&lt;/code&gt; directory of our theme, and a new script named &lt;code&gt;mytheme.js&lt;/code&gt; inside the &lt;code&gt;web/js&lt;/code&gt; directory.&lt;/p&gt;
&lt;p&gt;After we make all the desired changes to our theme, we will need to tell Magento to update the frontend. We need to deploy the new changes and clear the cache. We can achieve that by running this from our website root:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;php bin/magento setup:static-content:deploy
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;php bin/magento cache:clean&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This process can take some minutes to complete. After it’s done, we can go to our website to see if the frontend changes are applied:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;magento-2-creating-a-custom-theme/magento-frontend.jpg&#34; alt=&#34;Website homepage&#34;&gt;&lt;/p&gt;
&lt;p&gt;Looks awesome! Of course, there’s a lot more we can do from there, from extending or overriding layouts from modules (Magento or third-party) to bundling scripts or using Less files for our custom styles. But, that is material for later posts, so that’s all for now! Please add any questions you might have below.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Linux Development in Windows 10 with Docker and WSL 2</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2020/06/linux-development-in-windows-10-docker-wsl-2/"/>
      <id>https://www.endpointdev.com/blog/2020/06/linux-development-in-windows-10-docker-wsl-2/</id>
      <published>2020-06-18T00:00:00+00:00</published>
      <author>
        <name>Kevin Campusano</name>
      </author>
      <content type="html">
        &lt;p&gt;&lt;img src=&#34;/blog/2020/06/linux-development-in-windows-10-docker-wsl-2/banner.png&#34; alt=&#34;Banner&#34;&gt;&lt;/p&gt;
&lt;p&gt;I’m first and foremost a Windows guy. But for a few years now, moving away from working mostly with .NET and into a plethora of open source technologies has given me the opportunity to change platforms and run a Linux-based system as my daily driver. Ubuntu, which I honestly love for work, has been serving me well by supporting my development workflow with languages like &lt;a href=&#34;https://www.php.net/&#34;&gt;PHP&lt;/a&gt;, &lt;a href=&#34;https://www.javascript.com/&#34;&gt;JavaScript&lt;/a&gt; and &lt;a href=&#34;https://www.ruby-lang.org/en/&#34;&gt;Ruby&lt;/a&gt;. And with the help of the excellent &lt;a href=&#34;https://code.visualstudio.com/&#34;&gt;Visual Studio Code&lt;/a&gt; editor, I’ve never looked back. There’s always been an inclination in the back of my mind though, to take some time and give Windows another shot.&lt;/p&gt;
&lt;p&gt;With the latest improvements coming to the Windows Subsystem for Linux with &lt;a href=&#34;https://docs.microsoft.com/en-us/windows/wsl/compare-versions#whats-new-in-wsl-2&#34;&gt;its second version&lt;/a&gt;, the new and exciting &lt;a href=&#34;https://github.com/microsoft/terminal&#34;&gt;Windows Terminal&lt;/a&gt;, and &lt;a href=&#34;https://docs.docker.com/docker-for-windows/wsl/&#34;&gt;Docker support for running containers inside WSL2&lt;/a&gt;, I think the time is now.&lt;/p&gt;
&lt;p&gt;In this post, we’ll walk through the steps I took to set up a PHP development environment in Windows, running in a Ubuntu Docker container running on WSL 2, and VS Code. Let’s go.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: You have to be on the latest version of Windows 10 Pro (Version 2004) in order to install WSL 2 by the usual methods. If not, you’d need to be part of the Windows Insider Program to have access to the software.&lt;/p&gt;&lt;/blockquote&gt;
&lt;h3 id=&#34;whats-new-with-wsl-2&#34;&gt;What’s new with WSL 2&lt;/h3&gt;
&lt;p&gt;This is best explained by the &lt;a href=&#34;https://docs.microsoft.com/en-us/windows/wsl/compare-versions#whats-new-in-wsl-2&#34;&gt;official documentation&lt;/a&gt;. However, being a WSL 1 veteran, I’ll mention a few improvements made which have sparked my interest in trying it again.&lt;/p&gt;
&lt;h4 id=&#34;1-its-faster-and-more-compatible&#34;&gt;1. It’s faster and more compatible&lt;/h4&gt;
&lt;p&gt;WSL 2 introduces a complete architectural overhaul. Now, Windows ships with a full Linux Kernel which WSL 2 distributions run on. This results in greatly improved file system performance and much better compatibility with Linux programs. It’s no longer running a Linux look-alike, but actual Linux.&lt;/p&gt;
&lt;h4 id=&#34;2-its-better-integrated-with-windows&#34;&gt;2. It’s better integrated with Windows&lt;/h4&gt;
&lt;p&gt;This is a small one: we can now use the Windows explorer to browse files within a WSL distribution. This is not a WSL 2 exclusive feature, it has been there for a while now. I think it’s worth mentioning though, because it truly is a great convenience and a far cry from WSL’s first release, where Microsoft specifically advised against manipulating WSL distribution file systems from Windows. If nothing else, this makes WSL feel like a first class citizen in the Windows ecosystem and shows that Microsoft actually cares about making it a good experience.&lt;/p&gt;
&lt;h4 id=&#34;3-it-can-run-docker&#34;&gt;3. It can run Docker&lt;/h4&gt;
&lt;p&gt;I’ve recently been learning more and more about Docker and it’s quickly becoming my preferred way of setting up development environments. Due to its lightweightness, ease of use, repeatability, and VM-like compartmentalization, I find it really convenient to develop against a purpose-built Docker container, rather than directly in my local machine. And with VS Code’s Remote development extension, the whole thing is very easy to set up. Docker for Windows now supports running containers within WSL, so I’m eager to try that out and see how it all works.&lt;/p&gt;
&lt;h4 id=&#34;4-a-newer-version-means-several-bugfixes&#34;&gt;4. A newer version means several bugfixes&lt;/h4&gt;
&lt;p&gt;Performance notwithstanding, WSL’s first release was pretty stable. I did, however, encounter some weird bugs and gotchas when working with the likes of SSH and Ruby during certain tasks. It was nothing major, as workarounds were readily available. I’ve already discussed some of them &lt;a href=&#34;/blog/2019/04/rails-development-in-windows-10-pro-with-visual-studio-code-and-wsl/&#34;&gt;here&lt;/a&gt;, so I won’t bother mentioning them here again. But thanks to the fact that the technology has matured since last time I saw it, and considering the architectural direction it is going in, I’m excited to not have to deal with any number of quirks.&lt;/p&gt;
&lt;h3 id=&#34;the-development-environment&#34;&gt;The development environment&lt;/h3&gt;
&lt;p&gt;Ok, now with some of the motivation out of the way, let’s try and build a quick PHP Hello World app running in a Docker container inside WSL 2, make sure we can edit and debug it with VS Code, and access it in a browser from Windows.&lt;/p&gt;
&lt;h4 id=&#34;step-1-install-wsl-2-and-ubuntu&#34;&gt;Step 1: Install WSL 2 and Ubuntu&lt;/h4&gt;
&lt;p&gt;Step 1 is obviously to install WSL and a Linux distribution that we like. &lt;a href=&#34;https://docs.microsoft.com/en-us/windows/wsl/install-win10&#34;&gt;Microsoft’s own documentation&lt;/a&gt; offers an excellent guide on how to do just that. But in summary, we need to:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Enable the “Windows Subsystem for Linux” and “Virtual Machine Platform” features by running these on an elevated PowerShell:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;dism.exe /online /enable-feature /featurename:Microsoft-Windows-Subsystem-Linux /all /norestart
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;dism.exe /online /enable-feature /featurename:VirtualMachinePlatform /all /norestart&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;ol start=&#34;2&#34;&gt;
&lt;li&gt;Restart your machine.&lt;/li&gt;
&lt;li&gt;Set WSL 2 as the default version with: &lt;code&gt;wsl --set-default-version 2&lt;/code&gt;, also from PowerShell.&lt;/li&gt;
&lt;li&gt;Install your desired distribution from the Microsoft Store. I chose &lt;a href=&#34;https://www.microsoft.com/en-us/p/ubuntu-2004-lts/9n6svws3rx71?rtc=2&amp;amp;activetab=pivot:overviewtab&#34;&gt;Ubuntu 20.04 LTS&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;After installing, open the “Ubuntu 20.04 LTS” app from the Start menu and it should come up with a command line console. Wait for it to finish installing. It should prompt for a username and password along the way. Choose something you won’t forget.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Optionally, you can install the &lt;a href=&#34;https://github.com/microsoft/terminal&#34;&gt;Windows Terminal&lt;/a&gt; app to get a better command line experience. Windows Terminal can be used to interact with PowerShell and the classic CMD, as well as with our WSL distributions.&lt;/p&gt;
&lt;h4 id=&#34;step-2-install-docker&#34;&gt;Step 2: Install Docker&lt;/h4&gt;
&lt;p&gt;Installing Docker is very straightforward. Just download the installer for &lt;a href=&#34;https://hub.docker.com/editions/community/docker-ce-desktop-windows/&#34;&gt;Docker Desktop for Windows&lt;/a&gt;, execute it, and follow the wizard’s steps. Make sure that during setup the “Use the WSL 2 based engine” option is selected. In most cases, the installer will detect WSL 2 and automatically have the option selected.&lt;/p&gt;
&lt;p&gt;Follow the &lt;a href=&#34;https://docs.docker.com/docker-for-windows/wsl/&#34;&gt;official instructions&lt;/a&gt; for more details on the process, but it really is that simple.&lt;/p&gt;
&lt;h4 id=&#34;step-3-install-some-useful-vs-code-extensions&#34;&gt;Step 3: Install some useful VS Code extensions&lt;/h4&gt;
&lt;p&gt;Our objective is to create a new development environment inside a Docker container and connect to it directly with VS Code. To do that, we use a few useful extensions:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&#34;https://marketplace.visualstudio.com/items?itemName=ms-azuretools.vscode-docker&#34;&gt;The Docker extension&lt;/a&gt; which allows us to browse and manage images and containers and other types of Docker assets.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-wsl&#34;&gt;The Remote - WSL extension&lt;/a&gt; which allows VS Code to connect to a WSL distribution.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers&#34;&gt;The Remote - Containers extension&lt;/a&gt; which allows VS Code to connect to a container.&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 id=&#34;step-4-create-the-development-container&#34;&gt;Step 4: Create the development container&lt;/h4&gt;
&lt;p&gt;The extensions that we installed will allow us to use VS Code to work on code from within our WSL Ubuntu as well as from the container. Specifically, we want to connect VS Code to a container. There are a few ways to do this, but I will describe the one I think is the easiest, most convenient and “automagic” by fully leveraging the tools.&lt;/p&gt;
&lt;p&gt;Let’s begin by opening a WSL Ubuntu terminal session, which will show something like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Welcome to Ubuntu 20.04 LTS (GNU/Linux 4.19.104-microsoft-standard x86_64)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; * Documentation:  https://help.ubuntu.com
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; * Management:     https://landscape.canonical.com
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; * Support:        https://ubuntu.com/advantage
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;...
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;kevin@kevin-thinkpad:/mnt/c/Users/kevin$&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h4 id=&#34;the-project-directory&#34;&gt;The project directory&lt;/h4&gt;
&lt;p&gt;Let’s change to our home, create a new directory for our new project, and change into it.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ &lt;span style=&#34;color:#038&#34;&gt;cd&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ mkdir php-in-docker-demo
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ &lt;span style=&#34;color:#038&#34;&gt;cd&lt;/span&gt; php-in-docker-demo&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Because we installed the Remote - WSL extension, we can open up this directory in VS Code with &lt;code&gt;code .&lt;/code&gt;. Opening a terminal (Ctrl + `) in this VS Code instance opens WSL console, not Windows.&lt;/p&gt;
&lt;h4 id=&#34;the-dockerfile&#34;&gt;The Dockerfile&lt;/h4&gt;
&lt;p&gt;Now let’s create a new file called &lt;code&gt;Dockerfile&lt;/code&gt; which will define what our development environment image will look like. For a no-frills PHP environment, mine looks like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;# The FROM statement says that our image will be based on the official Ubuntu Docker image from Docker Hub: https://hub.docker.com/_/ubuntu
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;FROM ubuntu
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;# Install packages, not allowing apt to ask any questions since we can&amp;#39;t answer.
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ENV DEBIAN_FRONTEND=noninteractive
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;RUN apt-get update &amp;amp;&amp;amp; apt-get install -y software-properties-common php php-xdebug composer
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;# Configure Xdebug so that the VS Code debugger can use it.
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;RUN echo &amp;#34;xdebug.remote_enable=on&amp;#34; &amp;gt;&amp;gt; /etc/php/7.4/mods-available/xdebug.ini
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;RUN echo &amp;#34;xdebug.remote_autostart=on&amp;#34; &amp;gt;&amp;gt; /etc/php/7.4/mods-available/xdebug.ini
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;# The CMD statement tells Docker which command to run when it starts up the container.
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;# Here, we just call bash
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;CMD [&amp;#34;bash&amp;#34;]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This script will later be used to create our development container. It will have PHP, &lt;a href=&#34;https://xdebug.org/&#34;&gt;Xdebug&lt;/a&gt; and &lt;a href=&#34;https://getcomposer.org/&#34;&gt;Composer&lt;/a&gt;. This is all we need for our simple Hello World app. For more complex scenarios, other software like database clients or PHP extensions can be easily installed with additional &lt;code&gt;RUN&lt;/code&gt; statements that call upon the &lt;code&gt;apt&lt;/code&gt; package manager.&lt;/p&gt;
&lt;p&gt;Consider reading through &lt;a href=&#34;https://docs.docker.com/engine/reference/builder/&#34;&gt;Docker’s official documentation&lt;/a&gt; on Dockerfiles to learn more.&lt;/p&gt;
&lt;h4 id=&#34;the-configuration-file&#34;&gt;The configuration file&lt;/h4&gt;
&lt;p&gt;Now, to leverage VS Code’s capabilities, let’s add a development container configuration file. In our current location, we need to create a new directory called &lt;code&gt;.devcontainer&lt;/code&gt; and, inside that, a new file called &lt;code&gt;devcontainer.json&lt;/code&gt;. I put these contents in mine:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// The name used by VS Code to identify this development environment
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;name&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;PHP in Docker Demo&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// Sets the run context to one level up instead of the .devcontainer folder.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;context&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;..&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// Update the &amp;#39;dockerFile&amp;#39; property if you aren&amp;#39;t using the standard &amp;#39;Dockerfile&amp;#39; filename.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;dockerFile&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;../Dockerfile&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// Add the IDs of extensions you want installed when the container is created.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// This is the VS Code PHP Debug extension.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// It needs to be installed in the container for us to have access to it.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;extensions&amp;#34;&lt;/span&gt;: [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;felixfbecker.php-debug&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// Use &amp;#39;forwardPorts&amp;#39; to make a list of ports inside the container available locally.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// When we run our PHP app, we will use this port.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;forwardPorts&amp;#34;&lt;/span&gt;: [&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;5000&lt;/span&gt;],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;A default version of this file can be automatically generated by running the “Remote-Containers: Add Development Container Configuration Files…” command in VS Code’s Command Palette (Ctrl + Shift + P).&lt;/p&gt;
&lt;h4 id=&#34;the-development-container&#34;&gt;The development container&lt;/h4&gt;
&lt;p&gt;Now that we have all that in place, we can create our image, run our container, and start coding our app. Bring up the VS Code Command Palette with Ctrl + Shift + P and run the “Remote-Containers: Reopen in Container” command. The command will:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Read the Dockerfile and create an image based on that. This is like running &lt;code&gt;docker build -t AUTOGENERATED_IMAGE_ID .&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Run a container based on that image with the settings specified in &lt;code&gt;.devcontainer/devcontainer.json&lt;/code&gt;. In our case, all it will do is enable the container’s port 5000 to be accessible by the host. This is more or less like running: &lt;code&gt;docker run -d -p 5000:5000 -v ${PWD}:/workspaces/php-in-docker-demo AUTOGENERATED_IMAGE_ID&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Open a new VS Code instance connected to the container with the &lt;code&gt;/workspaces/php-in-docker-demo&lt;/code&gt; directory open.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;It will take a while, but after it’s done, we will have a VS Code instance running directly in the container. Open the VS Code terminal with Ctrl + ` and see for yourself. It will show a prompt looking like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;root@ec5be7dd0b9b:/workspaces/php-in-docker-demo#&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You can for example, run &lt;code&gt;php -v&lt;/code&gt; in this terminal, and expect something along these lines:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;PHP 7.4.3 (cli) (built: May 26 2020 12:24:22) ( NTS )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Copyright (c) The PHP Group
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Zend Engine v3.4.0, Copyright (c) Zend Technologies
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    with Zend OPcache v7.4.3, Copyright (c), by Zend Technologies&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This is PHP running, not in Windows, not in our WSL Ubuntu, but in the Docker container.&lt;/p&gt;
&lt;h4 id=&#34;hello-windows--wsl-2--ubuntu--docker--php--vs-code&#34;&gt;Hello Windows + WSL 2 + Ubuntu + Docker + PHP + VS Code&lt;/h4&gt;
&lt;p&gt;Let’s now create our app. Add a new &lt;code&gt;index.php&lt;/code&gt; file containing something silly like:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;?php
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;echo&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Hello Windows + WSL 2 + Ubuntu + Docker + PHP + Visual Studio Code!&amp;#34;&lt;/span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Then, in the VS Code console (remember, Ctrl + `), start up an instance of the built in PHP development server wth &lt;code&gt;php -S 0.0.0.0:5000&lt;/code&gt;. It’s important that we use port 5000 because that’s the one that we configured our container to use.&lt;/p&gt;
&lt;p&gt;Navigate to &lt;code&gt;http://localhost:5000/&lt;/code&gt; in your browser and feel good about a job well done.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2020/06/linux-development-in-windows-10-docker-wsl-2/running.png&#34; alt=&#34;Running app&#34;&gt;&lt;/p&gt;
&lt;h4 id=&#34;interactive-debugging&#34;&gt;Interactive debugging&lt;/h4&gt;
&lt;p&gt;When configuring our development container, we added Xdebug and the PHP Debug VS Code extension. This means that VS Code can leverage Xdebug to provide an interactive debugging experience for PHP code.&lt;/p&gt;
&lt;p&gt;Almost everyting is set up at this point, we just need to do the usual VS Code configuration and add a &lt;code&gt;launch.json&lt;/code&gt; file. To do so, in VS Code, press Ctrl + Shift + D to bring up the “Run” panel, click on the “create a launch.json file” link, and in the resulting “Select Environment” menu, select “PHP”.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2020/06/linux-development-in-windows-10-docker-wsl-2/vscode-run.png&#34; alt=&#34;Running app&#34;&gt;&lt;/p&gt;
&lt;p&gt;After that, the “Run” panel will show a green triangular “Start Debugging” button next to a “Listen to XDebug” text. If you haven’t already, start up a dev web server with &lt;code&gt;php -S 0.0.0.0:5000&lt;/code&gt;, click on the “Start Debugging” button, put a breakpoint somewhere in your &lt;code&gt;index.php&lt;/code&gt; file, and finally open up &lt;code&gt;http://localhost:5000/&lt;/code&gt; in a browser.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2020/06/linux-development-in-windows-10-docker-wsl-2/debug.png&#34; alt=&#34;Running app&#34;&gt;&lt;/p&gt;
&lt;p&gt;We’re interactively debugging PHP code running on a Docker container in WSL from our Windows IDE/​editor. Pretty cool, huh?&lt;/p&gt;
&lt;p&gt;And that’s all for now. In this article we’ve learned how to set up a Linux development environment using Docker containers and WSL 2, with Windows 10 Pro. This is a nice approach for anybody who’s confortable on Windows and needs access to a Linux environment for development; and have that environment be easy to reproduce.&lt;/p&gt;
&lt;h3 id=&#34;resources&#34;&gt;Resources:&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://docs.microsoft.com/en-us/windows/wsl/install-win10&#34;&gt;Windows Subsystem for Linux Installation Guide for Windows 10&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://code.visualstudio.com/blogs/2020/03/02/docker-in-wsl2&#34;&gt;Using Docker in WSL 2&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://docs.docker.com/docker-for-windows/wsl/&#34;&gt;Docker Desktop WSL 2 backend&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://code.visualstudio.com/docs/remote/remote-overview&#34;&gt;VS Code Remote Development&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

      </content>
    </entry>
  
    <entry>
      <title>Magento 2: Creating a custom module</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2020/04/magento-2-creating-a-custom-module/"/>
      <id>https://www.endpointdev.com/blog/2020/04/magento-2-creating-a-custom-module/</id>
      <published>2020-04-01T00:00:00+00:00</published>
      <author>
        <name>Juan Pablo Ventoso</name>
      </author>
      <content type="html">
        &lt;p&gt;&lt;img src=&#34;/blog/2020/04/magento-2-creating-a-custom-module/bridge-wires.jpg&#34; alt=&#34;Bridge with wires&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://unsplash.com/photos/q4ZBGVzJskE&#34;&gt;Photo&lt;/a&gt; by &lt;a href=&#34;https://unsplash.com/@olajidetunde&#34;&gt;Babatunde Olajide&lt;/a&gt;, cropped from original&lt;/p&gt;
&lt;p&gt;A Magento module is a set of classes and routines that will depend on and interact with other Magento classes in order to add a specific feature to a Magento application. While a &lt;a href=&#34;https://devdocs.magento.com/guides/v2.3/frontend-dev-guide/themes/theme-overview.html&#34;&gt;theme&lt;/a&gt; is orientated towards the front-end and user experience, a &lt;a href=&#34;https://devdocs.magento.com/guides/v2.3/architecture/archi_perspectives/components/modules/mod_intro.html&#34;&gt;module&lt;/a&gt; is orientated towards backend logic and application flow.&lt;/p&gt;
&lt;p&gt;We will need to create a custom module if we want to add or change the existing logic at a level where Magento doesn’t provide a setting or option for it. For example, if our business has a specific feature or set of features or requirements that are not common to the market, a module can fill that gap for us.&lt;/p&gt;
&lt;h3 id=&#34;creating-a-basic-magento-2-module&#34;&gt;Creating a basic Magento 2 module&lt;/h3&gt;
&lt;p&gt;Creating a simple module in Magento 2 is not that hard. We will need to accomplish the following tasks:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Create a new directory for the module&lt;/li&gt;
&lt;li&gt;Create a &lt;code&gt;registration.php&lt;/code&gt; script&lt;/li&gt;
&lt;li&gt;Create a &lt;code&gt;etc/module.xml&lt;/code&gt; information file&lt;/li&gt;
&lt;li&gt;Install the new module&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&#34;creating-a-new-directory-for-the-module&#34;&gt;Creating a new directory for the module&lt;/h4&gt;
&lt;p&gt;Where should the new directory for our module be placed? We have two options to choose from:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;app/code/{vendor}/&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;vendor/{vendor}/&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If your module is intended for a specific website you’re working on, you can use the first option. If you’re creating a module with the intention of it being used on several websites, it’s best to choose the second option. We’ll use the first for this example.&lt;/p&gt;
&lt;p&gt;Let’s create a directory named &lt;code&gt;EndPoint&lt;/code&gt; (our vendor name) with a subdirectory inside it, &lt;code&gt;MyModule&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#038&#34;&gt;cd&lt;/span&gt; {website_root}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;mkdir -p app/code/EndPoint/MyModule&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h4 id=&#34;creating-the-registrationphp-script&#34;&gt;Creating the registration.php script&lt;/h4&gt;
&lt;p&gt;The &lt;code&gt;registration.php&lt;/code&gt; file tells Magento to register the new module under a specific name and location. Let’s create a file named &lt;code&gt;app/code/EndPoint/MyModule/registration.php&lt;/code&gt; with the folllowing content:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;?php
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;\Magento\Framework\Component\ComponentRegistrar::&lt;span style=&#34;color:#369&#34;&gt;register&lt;/span&gt;(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    \Magento\Framework\Component\ComponentRegistrar::&lt;span style=&#34;color:#369&#34;&gt;MODULE&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;EndPoint_MyModule&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;__DIR__&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We’re telling Magento that our module will be named EndPoint_MyModule.&lt;/p&gt;
&lt;h4 id=&#34;creating-the-etcmodulexml-information-file&#34;&gt;Creating the etc/module.xml information file&lt;/h4&gt;
&lt;p&gt;Now, let’s create our module information file, where we’ll specify the module version number. First, we need to create the &lt;code&gt;etc&lt;/code&gt; directory inside &lt;code&gt;app/code/EndPoint/MyModule&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;mkdir app/code/EndPoint/MyModule/etc&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;then create &lt;code&gt;module.xml&lt;/code&gt; with the following content:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-xml&#34; data-lang=&#34;xml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#c00;font-weight:bold&#34;&gt;&amp;lt;?xml version=&amp;#34;1.0&amp;#34;?&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;config&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;xmlns:xsi=&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;http://www.w3.org/2001/XMLSchema-instance&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;xsi:noNamespaceSchemaLocation=&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;urn:magento:framework:Module/etc/module.xsd&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;module&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;name=&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;EndPoint_MyModule&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;setup_version=&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;1.0.0&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;/module&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;/config&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h4 id=&#34;installing-the-new-module&#34;&gt;Installing the new module&lt;/h4&gt;
&lt;p&gt;That’s it! We have everything we need to install our new module. Now we need to tell Magento we want to install and enable our new module. So from our website root we need to run:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;php bin/magento setup:upgrade&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Magento will output a list of module names and configuration updates, and our new module &lt;code&gt;EndPoint_MyModule&lt;/code&gt; should be listed in that output.&lt;/p&gt;
&lt;h3 id=&#34;adding-a-custom-route-to-our-module&#34;&gt;Adding a custom route to our module&lt;/h3&gt;
&lt;p&gt;Now we have a working, enabled module, but it’s not doing anything yet! What’s a simple way to check that our module is enabled? Let’s set up a custom route, so if we hit a URL like &lt;code&gt;https://{our_website}/mymodule/test/helloworld&lt;/code&gt; we can return a custom response from a controller.&lt;/p&gt;
&lt;p&gt;Creating a custom route will need some steps on its own:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Create a new directory for the controller&lt;/li&gt;
&lt;li&gt;Create a &lt;code&gt;etc/routes.xml&lt;/code&gt; file&lt;/li&gt;
&lt;li&gt;Create the controller&lt;/li&gt;
&lt;li&gt;Upgrade the new module&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&#34;creating-a-new-directory-for-the-controller&#34;&gt;Creating a new directory for the controller&lt;/h4&gt;
&lt;p&gt;First we need to create a new directory where the new PHP controller for our custom route will live. The new directory path should be:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;app/code/EndPoint/MyModule/Controller&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;We can create as many directory levels we want, depending on our desired path. For example, if we create a class named &lt;code&gt;Index&lt;/code&gt; in &lt;code&gt;app/code/EndPoint/MyModule/Controller&lt;/code&gt;, the URL that will be routed to this controller will be &lt;code&gt;https://{our_website}/mymodule/index&lt;/code&gt; (the “Controller” directory is ignored). If we create a class named &lt;code&gt;HelloWorld&lt;/code&gt; in &lt;code&gt;app/code/EndPoint/MyModule/Controller/Test&lt;/code&gt;, the resulting URL will be &lt;code&gt;https://{our_website}/mymodule/test/helloworld&lt;/code&gt;.&lt;/p&gt;
&lt;h4 id=&#34;creating-the-etcroutesxml-file&#34;&gt;Creating the etc/routes.xml file&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;routes.xml&lt;/code&gt; will tell Magento what base URL will be used for our module. First, we need to create the “frontend” directory where the routes.xml file needs to be placed:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;mkdir app/code/EndPoint/MyModule/etc/frontend&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In this example, we want the base URL to be &lt;code&gt;MyModule&lt;/code&gt;, so we need to create an XML file inside the new directory that will route all requests made to the given URL to our module controllers:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-xml&#34; data-lang=&#34;xml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#c00;font-weight:bold&#34;&gt;&amp;lt;?xml version=&amp;#34;1.0&amp;#34; ?&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;config&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;xmlns:xsi=&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;http://www.w3.org/2001/XMLSchema-instance&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;xsi:noNamespaceSchemaLocation=&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;urn:magento:framework:App/etc/routes.xsd&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;router&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;id=&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;standard&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;route&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;frontName=&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;mymodule&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;id=&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;mymodule&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;module&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;name=&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;EndPoint_MyModule&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;/route&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;/router&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;/config&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h4 id=&#34;creating-the-controller&#34;&gt;Creating the controller&lt;/h4&gt;
&lt;p&gt;If we want to respond to requests for &lt;code&gt;https://{our_website}/mymodule/test/helloworld&lt;/code&gt; we first need to create the base &lt;code&gt;Controller&lt;/code&gt; directory and the &lt;code&gt;Test&lt;/code&gt; subdirectory:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;mkdir -p app/code/EndPoint/MyModule/Controller/Test&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Under this directory, we’ll create our custom Magento controller. All route controllers should extend &lt;code&gt;\Magento\Framework\App\Action\Action&lt;/code&gt;. We also need to have a public &lt;code&gt;construct()&lt;/code&gt; method to pass the context to our ancestor and an &lt;code&gt;execute()&lt;/code&gt; function that will be called when the URL is hit:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;?php
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;namespace&lt;/span&gt; EndPoint\MyModule\Controller\Test;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;HelloWorld&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;extends&lt;/span&gt; \Magento\Framework\App\Action\Action
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;__construct&lt;/span&gt;(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        \Magento\Framework\App\Action\Context &lt;span style=&#34;color:#369&#34;&gt;$context&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;parent&lt;/span&gt;::&lt;span style=&#34;color:#369&#34;&gt;__construct&lt;/span&gt;(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#369&#34;&gt;$context&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        );
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;execute&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;echo&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Hello world!&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h4 id=&#34;upgrading-the-new-module&#34;&gt;Upgrading the new module&lt;/h4&gt;
&lt;p&gt;We have everything in place to tell Magento we have new changes to be deployed. How we do that? First, we need to upgrade our Magento setup. But since we added a new controller that gets parameters from the dependency injector in the construct, we also need to compile the dependency injection engine (including factories, proxies, and interceptors). And finally, we need to clear the cache so new content will be served from our custom URL:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;php bin/magento setup:upgrade
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;php bin/magento setup:di:compile
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;php bin/magento cache:flush&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This process can take a few minutes to complete, but after it’s done we can try to reach our new custom URL. If we get a response like the one below:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2020/04/magento-2-creating-a-custom-module/magento-hello-world-response.jpg&#34; alt=&#34;Hello world!&#34;&gt;&lt;/p&gt;
&lt;p&gt;That means our module is working!&lt;/p&gt;
&lt;p&gt;That’s all for now. In upcoming posts, we’ll start complicating things a bit by overriding Magento classes with our custom ones and creating custom controllers that will return information from the Magento core classes. We will also explore how to customize the front-end by creating a theme. Don’t forget to add any questions, suggestions, or issues in the comments below!&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Cooking with CAS</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2020/03/cooking-with-cas/"/>
      <id>https://www.endpointdev.com/blog/2020/03/cooking-with-cas/</id>
      <published>2020-03-10T00:00:00+00:00</published>
      <author>
        <name>Josh Tolley</name>
      </author>
      <content type="html">
        &lt;img src=&#34;/blog/2020/03/cooking-with-cas/4696900602_77582d1d5d_c.jpg&#34; alt=&#34;Laptop on a desk showing a web browser open to a login form, with a Post-It note saying &#39;ADMIN / ADMIN&#39; attached to the screen&#34; /&gt;
&lt;p&gt;Photo by Flickr user &lt;a href=&#34;http://web.archive.org/web/20190305093832/https://www.flickr.com/photos/reidrac/&#34;&gt;reidrac&lt;/a&gt;, licensed
under &lt;a href=&#34;https://creativecommons.org/licenses/by-sa/2.0/&#34;&gt;CC BY-SA 2.0&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;One of our customers asked us to host a new suite of web-based applications for them and to protect them with a single sign-on (SSO) solution. Ok, easy enough;
these applications were in fact designed with a particular SSO system in mind already, but our situation required a different one, and we eventually chose
Apereo’s open source &lt;a href=&#34;https://www.apereo.org/projects/cas&#34;&gt;Central Authentication Server project&lt;/a&gt;, or CAS. I’d like to describe the conversion process we went
through.&lt;/p&gt;
&lt;h3 id=&#34;the-ingredients&#34;&gt;The ingredients&lt;/h3&gt;
&lt;p&gt;Our customer’s application suite included:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The principal Java application using &lt;a href=&#34;https://docs.oracle.com/javase/7/docs/technotes/guides/security/jaas/JAASRefGuide.html&#34;&gt;JAAS authentication&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Another Java application based on &lt;a href=&#34;https://spring.io/projects/spring-security&#34;&gt;Spring Security&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;A pair of PHP applications&lt;/li&gt;
&lt;li&gt;A few automated tasks that needed to authenticate.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The original SSO system sets a header on each request, identifying an authenticated user. This requires a gateway system to sanitize request headers to ensure
malicious users cannot forge a header themselves. It also requires each application inspect request headers and respond appropriately.&lt;/p&gt;
&lt;p&gt;CAS is a bit more complex: applications redirect unauthenticated requests to a CAS server, which authenticates the user through any of various configurable
methods. The CAS server then redirects the user back to the original application with a parameter called a “Service Ticket”, a seemingly random number
identifying an individual authentication request. The original application contacts the CAS server directly to validate the service ticket and to collect
information to identify the user. It can then establish a session for that user, and proceed normally.&lt;/p&gt;
&lt;p&gt;To CAS-enable an application, we incorporate one of the CAS &lt;a href=&#34;https://apereo.github.io/cas/6.1.x/integration/CAS-Clients.html#build-your-own-cas-client&#34;&gt;client
libraries&lt;/a&gt;, which exist for various languages. In fact we won’t use
the Java client directly, but rather we’ll incorporate components that extend it. When evaluating CAS, I was a bit concerned by what appeared to be a
surprisingly limited selection of actively supported client libraries, and of course your results may vary, but we found software to meet our own needs without
too much difficulty.&lt;/p&gt;
&lt;h3 id=&#34;configuring-wildfly-authentication&#34;&gt;Configuring Wildfly Authentication&lt;/h3&gt;
&lt;p&gt;The most important application in the suite depends on the JAAS-based security subsystem of the &lt;a href=&#34;https://www.wildfly.org/&#34;&gt;Wildfly application server&lt;/a&gt; it’s
deployed to. Originally it used a custom &lt;a href=&#34;https://docs.oracle.com/javase/7/docs/api/javax/security/auth/spi/LoginModule.html&#34;&gt;LoginModule&lt;/a&gt; that inspected
request headers to find the ID of the authenticated user. Our first task was to configure our proxy server to remove this header from every request. We planned
to disable the old authentication system entirely, of course, but this change ensured that even if we mistakenly left it enabled somewhere, malicious users
couldn’t exploit it for access.&lt;/p&gt;
&lt;p&gt;This application uses a declarative security policy: the deployment descriptor identifies a set of user roles, and another set of “security constraints”. Each
security constraint describes one or more URL patterns used in the application, and the set of user roles allowed to access URLs matching those patterns. Here
are two examples:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-xml&#34; data-lang=&#34;xml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&amp;lt;!-- clients --&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;security-constraint&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;web-resource-collection&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;web-resource-name&amp;gt;&lt;/span&gt;Secure Area&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;/web-resource-name&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;url-pattern&amp;gt;&lt;/span&gt;/views/clients/*&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;/url-pattern&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;url-pattern&amp;gt;&lt;/span&gt;/client/edit/*&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;/url-pattern&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;url-pattern&amp;gt;&lt;/span&gt;/client/edit_tree/*&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;/url-pattern&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;url-pattern&amp;gt;&lt;/span&gt;/client/view/*&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;/url-pattern&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;url-pattern&amp;gt;&lt;/span&gt;/client/view/id/*&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;/url-pattern&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;/web-resource-collection&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;auth-constraint&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;role-name&amp;gt;&lt;/span&gt;client_role&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;/role-name&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;/auth-constraint&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;/security-constraint&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&amp;lt;!-- places --&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;security-constraint&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;web-resource-collection&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;web-resource-name&amp;gt;&lt;/span&gt;Secure Area&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;/web-resource-name&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;url-pattern&amp;gt;&lt;/span&gt;/views/places/*&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;/url-pattern&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;/web-resource-collection&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;auth-constraint&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;role-name&amp;gt;&lt;/span&gt;manage_places&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;/role-name&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;/auth-constraint&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;/security-constraint&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We installed the &lt;a href=&#34;https://github.com/soulwing/cas-extension&#34;&gt;cas-extension library&lt;/a&gt; in our Wildfly server to handle the CAS protocol. When an unauthenticated
user attempts to access a URL matching a pattern in one of the application’s security constraints, the CAS extension automatically intercepts control and
redirects the user to the CAS server. Assuming the user authenticates successfully, our application will receive another request with a service ticket
parameter. The cas-extension intercepts this request as well, validates the service ticket, and creates an “identity assertion”, which it sends to the Wildfly
security system. Wildfly’s role mapper queries a database to find the user’s roles, after which the authentication process is complete.&lt;/p&gt;
&lt;p&gt;Configuration of the cas-extension begins with a CAS profile, a combination of the URL of the CAS server and the URL of the service the extension should
protect.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-xml&#34; data-lang=&#34;xml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;subsystem&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;xmlns=&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;urn:soulwing.org:cas:1.0&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;cas-profile&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;name=&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;default&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;service-url=&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;https://our.application.server/application&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;server-url=&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;https://our.cas.server&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;/subsystem&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This tells the extension where to send authentication requests, where to listen for requests returning from the CAS server, and where to validate service
tickets. Next we need to validate that identity assertion, and figure out what roles belong to the user. This happens in a &lt;a href=&#34;https://docs.wildfly.org/14/Admin_Guide.html#security-domains&#34;&gt;Wildfly security
domain&lt;/a&gt;.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-xml&#34; data-lang=&#34;xml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;security-domain&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;name=&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;MySecurityDomain&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;authentication&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;login-module&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;name=&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;IdentityAssertion&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;code=&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;org.soulwing.cas.jaas.IdentityAssertionLoginModule&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;flag=&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;required&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;module=&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;org.soulwing.cas&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;/authentication&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;mapping&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;mapping-module&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;code=&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;DatabaseRoles&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;type=&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;role&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;module-option&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;name=&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;dsJndiName&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;value=&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;java:/comp/env/jdbc/databaseConnection&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;module-option&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;name=&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;rolesQuery&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;value=&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;select role from user_roles where user_id = ?&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;/mapping-module&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;/mapping&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;/security-domain&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;authentication&lt;/code&gt; portion of the security domain refers to a JAAS LoginModule shipped with the CAS extension, which simply verifies that the identity
assertion comes from the CAS extension and not somewhere else. Then the &lt;code&gt;mapping&lt;/code&gt; portion (&lt;a href=&#34;https://docs.wildfly.org/14/Admin_Guide.html#mapping&#34;&gt;documented here&lt;/a&gt;)
looks up the given user in a database to find what roles it should be assigned.&lt;/p&gt;
&lt;p&gt;The last piece of the puzzle is a CAS deployment descriptor for our application, which activates cas-extension for that application. In its simplest form, this
is an empty file in the right place, but ours ended up a little more complex. It identifies both the CAS profile we want to use (unnecessary in this case, as
there’s only one CAS profile on the system, but it helped keep things more clear in our minds), and instructs the extension to load some other libraries into
our application.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-xml&#34; data-lang=&#34;xml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#c00;font-weight:bold&#34;&gt;&amp;lt;?xml version=&amp;#34;1.0&amp;#34; encoding=&amp;#34;UTF-8&amp;#34;?&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;cas&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;xmlns=&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;urn:soulwing.org:cas:1.0&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;profile&amp;gt;&lt;/span&gt;default&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;/profile&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;add-api-dependencies/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;/cas&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This configuration proved sufficient to let users log in and use the application, but as is sometimes the case with single sign-on, we needed a little more work
to let them log out properly. Each application in the suite sets a cookie in the user’s browser to identify its session. The CAS server likewise sets a cookie.
When a user logs out of the application, that application’s session cookie is destroyed, but we also need to destroy the CAS server’s session cookie as well as
the other applications’ cookies. Single log-out can be complicated, and I won’t go into the full setup here. One reason the CAS deployment descriptor loads
cas-extension libraries was so we could use cas-extension to generate the proper logout URL, and redirect our users to that URL once the application has
destroyed its own session.&lt;/p&gt;
&lt;h3 id=&#34;configuring-spring-authentication&#34;&gt;Configuring Spring Authentication&lt;/h3&gt;
&lt;p&gt;Another Java-based application in our suite uses Spring Security. &lt;a href=&#34;https://docs.spring.io/spring-security/site/docs/3.0.x/reference/cas.html&#34;&gt;This document&lt;/a&gt;
describes the bulk of the configuration, which seemed less straightforward than for cas-extension, but follows essentially the same mechanism. Here we
configured a Spring
&lt;a href=&#34;https://docs.spring.io/spring-security/site/docs/3.2.3.RELEASE/apidocs/org/springframework/security/core/userdetails/UserDetailsService.html&#34;&gt;UserDetailsService&lt;/a&gt;
to execute the same database query we used above to find the user’s roles. I’m not fully conversant in the large stack of beans Spring uses to manage the
process, and it took some time to get this configuration sorted out.&lt;/p&gt;
&lt;h3 id=&#34;enter-php&#34;&gt;Enter PHP&lt;/h3&gt;
&lt;p&gt;Two of these applications use PHP, which meant yet another configuration. Apereo maintains a &lt;a href=&#34;https://github.com/apereo/phpCAS&#34;&gt;PHP client&lt;/a&gt;, which includes
several helpful examples. I tracked down the part of the application that authenticates users, and replaced the existing code with calls to phpCAS:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;phpCAS::&lt;span style=&#34;color:#369&#34;&gt;client&lt;/span&gt;(CAS_VERSION_2_0, &lt;span style=&#34;color:#369&#34;&gt;$phpCASHost&lt;/span&gt;, &lt;span style=&#34;color:#369&#34;&gt;$phpCASPort&lt;/span&gt;, &lt;span style=&#34;color:#369&#34;&gt;$phpCASContext&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;phpCAS::&lt;span style=&#34;color:#369&#34;&gt;forceAuthentication&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#369&#34;&gt;$_SESSION&lt;/span&gt;[EXPORT_SERVERNAME][&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;umdid&amp;#39;&lt;/span&gt;] = phpCAS::&lt;span style=&#34;color:#369&#34;&gt;getUser&lt;/span&gt;();&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;forceAuthentication&lt;/code&gt; call determines the current phase of the authentication process this request is in, whether it’s unauthenticated, fully authenticated, or
requires service ticket validation, and responds appropriately. We then set a session variable to the ID of the authenticated user, which replicates what
the original authentication code would have done.&lt;/p&gt;
&lt;p&gt;One of these two applications requires authenticated access to a REST API exposed by the Wildfly application. CAS calls this “proxy” authentication, when one
application requests access to another. Here, CAS issues not only its usual service ticket, but also a “proxy granting ticket”. When the application wants to
use the API, it asks the CAS server for a service ticket, using the proxy granting ticket. The CAS server itself requires some new configuration in this case,
but for the PHP code, the only difference in the login phase from the simpler, non-proxy case is that we call &lt;code&gt;phpCAS::proxy&lt;/code&gt; instead of &lt;code&gt;phpCAS::client&lt;/code&gt; to
configure CAS. Later, when calling the service itself, we used more phpCAS services in place of the cURL library the original used.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#369&#34;&gt;$service&lt;/span&gt; = \phpCAS::&lt;span style=&#34;color:#369&#34;&gt;getProxiedService&lt;/span&gt;(PHPCAS_PROXIED_SERVICE_HTTP_GET);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#369&#34;&gt;$service&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;setUrl&lt;/span&gt;(&lt;span style=&#34;color:#369&#34;&gt;$serviceURL&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#369&#34;&gt;$service&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;send&lt;/span&gt;();&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3 id=&#34;the-return-of-header-authentication&#34;&gt;The return of header authentication&lt;/h3&gt;
&lt;p&gt;Finally, we have a few automated tasks which use various APIs, and need to authenticate. We can’t redirect requests to a CAS server and expect a user to provide
credentials, so we’ve taken our cue from the applications’ original form, and configured CAS to recognize a “trusted header”. We add this header to any requests
issued by these automated jobs. Of course, we’ve also configured the proxy to disallow this header from any systems outside our internal network.&lt;/p&gt;
&lt;h3 id=&#34;a-few-loose-ends&#34;&gt;A few loose ends&lt;/h3&gt;
&lt;p&gt;Of course, there were other considerations in this project that I’ve not covered here. Configuring CAS itself wasn’t necessarily straightforward, and included a
custom authentication module I hope to describe in a later article. Selecting among CAS server’s available
&lt;a href=&#34;https://apereo.github.io/cas/6.1.x/installation/Configuring-Servlet-Container.html&#34;&gt;deployment&lt;/a&gt;
&lt;a href=&#34;https://apereo.github.io/cas/6.1.x/installation/Docker-Installation.html&#34;&gt;options&lt;/a&gt; and fitting the winner into our existing infrastructure in a way that makes
it easy to manage and monitor was another task entirely. We needed to customize the server’s default user interface to prevent it from offering users an
imaginary method to recover forgotten passwords. The series of redirected browser requests involved in the CAS protocol presents a notable performance impact
under some circumstances. And it has taken me no small effort to learn to appreciate the CAS server’s sometimes distressingly circular
&lt;a href=&#34;https://apereo.github.io/cas/6.1.x/index.html&#34;&gt;documentation&lt;/a&gt;. But several months into the project, CAS seems to be working well enough for our purposes that
other users of the same application suite have begun to express interest.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Symfony Quickstart</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2020/03/symfony-quickstart/"/>
      <id>https://www.endpointdev.com/blog/2020/03/symfony-quickstart/</id>
      <published>2020-03-02T00:00:00+00:00</published>
      <author>
        <name>Árpád Lajos</name>
      </author>
      <content type="html">
        &lt;p&gt;&lt;img src=&#34;/blog/2020/03/symfony-quickstart/symphony.jpg&#34; alt=&#34;symphony&#34; /&gt;&lt;br&gt;
&lt;a href=&#34;https://unsplash.com/photos/VEOk8qUl9DU&#34;&gt;Photo&lt;/a&gt; by &lt;a href=&#34;https://unsplash.com/@arindam_mahanta&#34;&gt;Arindam Mahanta&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;This article is written for anyone who has experience working with PHP and is starting to work with Symfony. We won’t assume any prior knowledge you might have with Symfony, so if getting started with this framework is a high priority, then this article is for you. I know that it can be difficult and you may be unsure where to look; I was in the same situation when I first worked with Symfony.&lt;/p&gt;
&lt;p&gt;You might be pointed to consult the &lt;a href=&#34;https://symfony.com/doc/current/index.html#gsc.tab=0&#34;&gt;documentation&lt;/a&gt;, but even though the documentation is very detailed and nicely written, you might have a very urgent issue to solve, and thus not have time to read multiple articles about the framework before you start working. You might just need to quickly start, solve a few issues and worry about the details later.&lt;/p&gt;
&lt;h3 id=&#34;how-do-i-run-this-stuff&#34;&gt;How do I run this stuff?&lt;/h3&gt;
&lt;p&gt;If your project does not exist yet, you will need to set up Symfony, using the steps nicely outlined in &lt;a href=&#34;https://symfony.com/doc/current/setup.html&#34;&gt;Symfony’s setup guide&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Assuming that the project already exists and you need to quickly start working on it, you will need to run &lt;code&gt;composer install&lt;/code&gt; in the root folder of the project to make sure that the dependencies are properly set. This could result in errors; for example, PHP might be not installed in your development environment. In this case, of course, you need to install PHP, which should not be a big problem if we continue to assume that you have some experience with PHP.&lt;/p&gt;
&lt;p&gt;Another problem might be that Composer is not yet installed on your machine. If this is the case, install Composer by following the steps &lt;a href=&#34;https://getcomposer.org/download/&#34;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;You might be missing some PHP extensions at this point or have other problems. If so, read the error messages you get and solve them. If you don’t understand some error messages, don’t worry, you aren’t the first one struggling to make Symfony work. Search for the error message you got paired with the Symfony keyword and find others’ solutions.&lt;/p&gt;
&lt;h3 id=&#34;composer&#34;&gt;Composer&lt;/h3&gt;
&lt;p&gt;Composer is a server-side package manager which is frequently used by modern PHP applications. In the previous section we executed &lt;code&gt;composer install&lt;/code&gt;. Let’s understand this command now. This command reads &lt;strong&gt;composer.json&lt;/strong&gt;, resolves dependencies and installs them into the vendor folder.&lt;/p&gt;
&lt;p&gt;If you intend to update to the latest versions of the dependencies outlined in &lt;strong&gt;composer.lock&lt;/strong&gt;, run &lt;code&gt;composer update&lt;/code&gt; or its alias &lt;code&gt;composer upgrade&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;There are two very important files to know about: &lt;strong&gt;composer.json&lt;/strong&gt; and &lt;strong&gt;composer.lock&lt;/strong&gt;. In &lt;strong&gt;composer.json&lt;/strong&gt; the dependencies are specified as JSON. The actual value of a dependency is an expression to denote what kinds of versions are allowed. &lt;strong&gt;composer.lock&lt;/strong&gt; specifies which dependencies should &lt;em&gt;not&lt;/em&gt; be updated to the latest version and it contains the exact version number for the installed PHP packages, so if you have a &lt;strong&gt;composer.lock&lt;/strong&gt; file and you want to fetch the latest versions allowed by &lt;strong&gt;composer.json&lt;/strong&gt;, then you need to run &lt;code&gt;composer update&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Since our article is just a quick start, we’ll avoid delving too deep into the realms of Composer, but if you want to learn more, it’s very well &lt;a href=&#34;https://getcomposer.org/doc/03-cli.md&#34;&gt;documented&lt;/a&gt;. You’ll need an understanding of Composer to work on Symfony projects, but for the very short term you can get by with a minimal understanding.&lt;/p&gt;
&lt;p&gt;Whenever you are pulling work done by others, you will need to make sure that the dependencies are updated. If there is any change in the downloaded &lt;strong&gt;composer.json&lt;/strong&gt;, &lt;code&gt;composer install&lt;/code&gt; should be executed. If you check out a different branch you should run &lt;code&gt;composer install&lt;/code&gt; unless you are absolutely sure it’s not needed.&lt;/p&gt;
&lt;h3 id=&#34;yarn&#34;&gt;Yarn&lt;/h3&gt;
&lt;p&gt;Yarn is a package manager which we can use to manage client-side packages if we choose to (Symfony does not enforce its use). If you already have a Node.js server installed, installing yarn is as simple as &lt;code&gt;sudo npm install -g yarn&lt;/code&gt; with &lt;code&gt;-g&lt;/code&gt; specifying a global installation. If you don’t have Node.js installed, it’s worth &lt;a href=&#34;https://nodejs.org/en/download/&#34;&gt;downloading&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;yarn install&lt;/code&gt; installs the dependencies of a project. You will need to run this (if Yarn is used in the project) when you first work with the project at least once, as well as whenever there’s a change in &lt;strong&gt;package.json&lt;/strong&gt; (which serves the same purpose for Yarn as &lt;strong&gt;composer.json&lt;/strong&gt; for Composer).&lt;/p&gt;
&lt;p&gt;Consult Yarn’s &lt;a href=&#34;https://classic.yarnpkg.com/en/docs/&#34;&gt;documentation&lt;/a&gt; for more information.&lt;/p&gt;
&lt;h3 id=&#34;webpack&#34;&gt;webpack&lt;/h3&gt;
&lt;p&gt;&lt;a href=&#34;https://webpack.js.org/&#34;&gt;webpack&lt;/a&gt; bundles client-side files. You can configure webpack to pack file content from a source location and send the packed content to a target location. Read more &lt;a href=&#34;https://symfony.com/doc/current/frontend/encore/simple-example.html#configuring-encore-webpack&#34;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Encore can be restarted with &lt;code&gt;yarn run encore &amp;lt;env&amp;gt;&lt;/code&gt;, which packs files into the target. However, this is unfriendly to the developer, because now each change in client-side code requires us running &lt;code&gt;yarn run encore &amp;lt;env&amp;gt;&lt;/code&gt; to make sure that the newest client-side code will be used. This quickly becomes frustrating, since JavaScript and CSS, which never required deployment before, now need to be built. However, you can add the &lt;code&gt;--watch&lt;/code&gt; switch like &lt;code&gt;yarn run encore &amp;lt;env&amp;gt; --watch&lt;/code&gt; and from there on changes will be watched and detected, so you can be confident that client-side code will end up in the target path and you won’t have to worry about it.&lt;/p&gt;
&lt;p&gt;The drawback, of course, is that this will eat up resources and will still be time consuming if the client-side code is very large. As a result, it is much smarter to just use the source path while in development mode. Sometimes browser caching will still cause us trouble while working this way, but a seasoned frontend developer is already used to that.&lt;/p&gt;
&lt;p&gt;webpack is a nice tool and can be useful when used well, but it’s a good idea to limit its use. Avoiding webpack in development mode can be very helpful, allowing us to nicely debug and not worry about building client-side code. In production mode it adds a lot of value in obfuscating, minifying, and packing the code.&lt;/p&gt;
&lt;h3 id=&#34;mvc&#34;&gt;MVC&lt;/h3&gt;
&lt;img src=&#34;/blog/2020/03/symfony-quickstart/mvc.svg&#34; style=&#34;display: block; height: 300px; margin: auto&#34; /&gt;
&lt;p&gt;Model-View-Controller as a pattern is a standard for Symfony. Model means domain model, usually represented by entity and repository classes along with the business logic that uses them. View is the templating that eventually generates the HTML which will be served to the browser and displayed to the user. Controllers are the engines, the driving force, since the actions allowed to be performed can be found in them, putting everything together.&lt;/p&gt;
&lt;h3 id=&#34;doctrine&#34;&gt;Doctrine&lt;/h3&gt;
&lt;p&gt;&lt;a href=&#34;https://www.doctrine-project.org/projects/doctrine-orm/en/2.7/index.html&#34;&gt;Doctrine&lt;/a&gt; is an &lt;a href=&#34;https://en.wikipedia.org/wiki/Object-relational_mapping&#34;&gt;ORM&lt;/a&gt;, which is very helpful in general for working with databases. Symfony does not enforce its use. You can use Flourishlib instead, or write your own queries. You can create an entity class (which represents a given table in the database) manually, but there is an automatic way to achieve that as well:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;php bin/console make:entity&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This will start a Q&amp;amp;A, which will be a discussion between the Symfony console and the programmer. After the programmer answers all the questions, the entity class will be generated. Typically these questions will be asked:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Entity name&lt;/li&gt;
&lt;li&gt;Property names (until empty name is given)
&lt;ul&gt;
&lt;li&gt;Their field type&lt;/li&gt;
&lt;li&gt;Their field length&lt;/li&gt;
&lt;li&gt;Whether they are nullable&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The documentation gives us a &lt;a href=&#34;https://symfony.com/doc/current/doctrine.html#creating-an-entity-class&#34;&gt;very good example&lt;/a&gt;, resulting in:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;// src/Entity/Product.php
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;namespace&lt;/span&gt; App\Entity;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;use&lt;/span&gt; Doctrine\ORM\Mapping &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;as&lt;/span&gt; ORM;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;/**
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt; * @ORM\Entity(repositoryClass=&amp;#34;App\Repository\ProductRepository&amp;#34;)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt; */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;Product&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;/**
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;     * @ORM\Id
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;     * @ORM\GeneratedValue
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;     * @ORM\Column(type=&amp;#34;integer&amp;#34;)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;     */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;private&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$id&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;/**
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;     * @ORM\Column(type=&amp;#34;string&amp;#34;, length=255)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;     */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;private&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$name&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;/**
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;     * @ORM\Column(type=&amp;#34;integer&amp;#34;)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;     */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;private&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$price&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;getId&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$this&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;id&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// ... getter and setter methods
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We notice a few things here. First of all, there is an annotation.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;/**
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt; * @ORM\Entity(repositoryClass=&amp;#34;App\Repository\ProductRepository&amp;#34;)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt; */&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This means that this entity has a repository class called &lt;code&gt;ProductRepository&lt;/code&gt; in the namespace mentioned above.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;/**
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt; * @ORM\Id
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt; * @ORM\GeneratedValue
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt; * @ORM\Column(type=&amp;#34;integer&amp;#34;)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt; */&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This means that the &lt;code&gt;column&lt;/code&gt; is of type &lt;code&gt;integer&lt;/code&gt;, a &lt;code&gt;primary key&lt;/code&gt;, and it’s a generated value. We notice that the &lt;code&gt;length&lt;/code&gt; can be given as well. The data members are &lt;code&gt;private&lt;/code&gt; and setters and getters are automatically generated.&lt;/p&gt;
&lt;p&gt;The entity class is paired with a repository of its own, like:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;namespace&lt;/span&gt; App\Repository;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;use&lt;/span&gt; App\Entity\Product;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;use&lt;/span&gt; Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;use&lt;/span&gt; Doctrine\Persistence\ManagerRegistry;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;ProductRepository&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;extends&lt;/span&gt; ServiceEntityRepository
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;__construct&lt;/span&gt;(ManagerRegistry &lt;span style=&#34;color:#369&#34;&gt;$registry&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;parent&lt;/span&gt;::&lt;span style=&#34;color:#369&#34;&gt;__construct&lt;/span&gt;(&lt;span style=&#34;color:#369&#34;&gt;$registry&lt;/span&gt;, Product::&lt;span style=&#34;color:#369&#34;&gt;class&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Note that it extends &lt;a href=&#34;https://github.com/doctrine/DoctrineBundle/blob/master//Repository/ServiceEntityRepository.php&#34;&gt;ServiceEntityRepository&lt;/a&gt;. This offers quite a few features if we use it as a Doctrine repository. In the controller we can load a repository, like:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#369&#34;&gt;$this&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;getDoctrine&lt;/span&gt;()-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;getRepository&lt;/span&gt;(Product::&lt;span style=&#34;color:#369&#34;&gt;class&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You can search for objects via find, findBy or findOneBy, as described in the &lt;a href=&#34;https://www.doctrine-project.org/projects/doctrine-orm/en/2.7/reference/working-with-objects.html#by-simple-conditions&#34;&gt;API documentation&lt;/a&gt;. This way, we can load data from the database and use them as objects. Example:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#369&#34;&gt;$repository&lt;/span&gt; = &lt;span style=&#34;color:#369&#34;&gt;$this&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;getDoctrine&lt;/span&gt;()-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;getRepository&lt;/span&gt;(Product::&lt;span style=&#34;color:#369&#34;&gt;class&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;// look for a single Product by its primary key (usually &amp;#34;id&amp;#34;)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#369&#34;&gt;$product&lt;/span&gt; = &lt;span style=&#34;color:#369&#34;&gt;$repository&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;find&lt;/span&gt;(&lt;span style=&#34;color:#369&#34;&gt;$id&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;// look for a single Product by name
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#369&#34;&gt;$product&lt;/span&gt; = &lt;span style=&#34;color:#369&#34;&gt;$repository&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;findOneBy&lt;/span&gt;([&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;name&amp;#39;&lt;/span&gt; =&amp;gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;Keyboard&amp;#39;&lt;/span&gt;]);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;// or find by name and price
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#369&#34;&gt;$product&lt;/span&gt; = &lt;span style=&#34;color:#369&#34;&gt;$repository&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;findOneBy&lt;/span&gt;([
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;name&amp;#39;&lt;/span&gt; =&amp;gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;Keyboard&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;price&amp;#39;&lt;/span&gt; =&amp;gt; &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1999&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;]);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;// look for multiple Product objects matching the name, ordered by price
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#369&#34;&gt;$products&lt;/span&gt; = &lt;span style=&#34;color:#369&#34;&gt;$repository&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;findBy&lt;/span&gt;(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    [&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;name&amp;#39;&lt;/span&gt; =&amp;gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;Keyboard&amp;#39;&lt;/span&gt;],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    [&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;price&amp;#39;&lt;/span&gt; =&amp;gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;ASC&amp;#39;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;// look for *all* Product objects
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#369&#34;&gt;$products&lt;/span&gt; = &lt;span style=&#34;color:#369&#34;&gt;$repository&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;findAll&lt;/span&gt;();&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In general, the criteria to search by is an array of key-value pairs. The same can be said about sorting.&lt;/p&gt;
&lt;p&gt;You can load a repository from within another by calling&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#369&#34;&gt;$this&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;getEntityManager&lt;/span&gt;()-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;getRepository&lt;/span&gt;(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;name&amp;#34;&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;However, in general I would suggest that repositories should not have interdependency. It’s far better in my opinion to have a business layer, a Service between the controller and the repository classes, which would combine different repositories’ operations if needed.&lt;/p&gt;
&lt;p&gt;Getting or setting values of data members can be done via getters and setters very simply. As the &lt;a href=&#34;https://www.doctrine-project.org/projects/doctrine-orm/en/2.7/reference/working-with-objects.html&#34;&gt;documentation&lt;/a&gt; says, &lt;code&gt;persist&lt;/code&gt; and &lt;code&gt;remove&lt;/code&gt; are methods that notify the &lt;a href=&#34;https://www.programmingwithwolfgang.com/repository-and-unit-of-work-pattern/&#34;&gt;Unit Of Work&lt;/a&gt; that some write operations should occur, but the write operations are not executed yet at this point. The reason is simple: we may have multiple write operations to do and we might want to avoid doing them all as separate requests to the database, which might be on an entirely different machine on the other side of the globe. Requests to the database are costly operations and they accumulate. To avoid this, the application server first acknowledges what should be done, or, in other words, prepares some write operations. The &lt;code&gt;flush&lt;/code&gt; method does the actual request sending for the write operations.&lt;/p&gt;
&lt;p&gt;There is no ORM which solves everything, so sometimes we want to write our own scripts. We can of course do it, but Doctrine offers &lt;a href=&#34;https://www.doctrine-project.org/projects/doctrine-orm/en/2.7/reference/dql-doctrine-query-language.html&#34;&gt;DQL&lt;/a&gt; (Doctrine Query Language) as a compromise.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;?php
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#369&#34;&gt;$query&lt;/span&gt; = &lt;span style=&#34;color:#369&#34;&gt;$em&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;createQuery&lt;/span&gt;(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;SELECT u FROM ForumUser u WHERE (u.username = :name OR u.username = :name2) AND u.id = :id&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#369&#34;&gt;$query&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;setParameters&lt;/span&gt;(&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;array&lt;/span&gt;(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;name&amp;#39;&lt;/span&gt; =&amp;gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;Bob&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;name2&amp;#39;&lt;/span&gt; =&amp;gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;Alice&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;id&amp;#39;&lt;/span&gt; =&amp;gt; &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;321&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#369&#34;&gt;$users&lt;/span&gt; = &lt;span style=&#34;color:#369&#34;&gt;$query&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;getResult&lt;/span&gt;(); &lt;span style=&#34;color:#888&#34;&gt;// array of ForumUser objects
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The example above is a parameterized query, where we search by username and id, without knowing what the actual filter values are at the time of development. These values are determined at runtime.&lt;/p&gt;
&lt;p&gt;You can apply entity changes upon the database via&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;php bin/console doctrine:migrations:migrate&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Honestly I’m not very fond of changing the database schema based on entity classes; I consider this to be an anti-pattern, because we use a tool to generate a schema based on entities. Such a tool can have bugs or cause errors in the entities. It is much better to do it the other way around—that is, make a proper database schema, write SQL commands to change the schema whenever we need it and generate entity classes that way. Luckily Doctrine offers that feature as well:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;php bin/console doctrine:mapping:import &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;App\Entity&amp;#34;&lt;/span&gt; annotation --path=src/Entity&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Unfortunately it’s a trend to write entity classes and generate schema changes based on those, but that way we add a layer of complexity around schema planning, which should be as simple as possible. If something goes south with these migrations for any reason, that could result in very serious problems, while if we are to write schema-changing scripts in SQL rather than generating them from entity classes that we write (or generate), we at least have the means to acquire full understanding of what happens with the schema. Not everyone is a database specialist and most programmers are much more comfortable writing or even generating entity classes. But even if we generate them, it’s not much simpler in most cases than writing a small script which creates a table or alters it or drops it, so the objective gain from generating schema-changing scripts from entity classes is negligible at best.&lt;/p&gt;
&lt;p&gt;On the other hand, the complex tool that we add is difficult to understand unless one delves into the actual code which generates the schema and the way the schema is generated is out of our hands, unless we are content with the current version of doctrine or accept the price of hacking into the code each time a new version is downloaded.&lt;/p&gt;
&lt;p&gt;In short, my advice is to try to avoid generating schema changes based on entity classes whenever possible. We can always find exceptional cases, but in general, SQL is clearer than a tool that generates SQL.&lt;/p&gt;
&lt;h3 id=&#34;controller&#34;&gt;Controller&lt;/h3&gt;
&lt;p&gt;You can write or generate a controller. This is how you would generate one:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;php bin/console make:controller &amp;lt;NamedController&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The command above will actually generate a controller class and ensure that it can be used in the project. Read more &lt;a href=&#34;https://symfony.com/doc/current/controller.html#generating-controllers&#34;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;You can generate a whole CRUD (support for Create, Read, Update, Delete features) for an entity via&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;php bin/console make:crud &amp;lt;YourEntity&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The documentation provides a simple example:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;// src/Controller/LuckyController.php
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;namespace&lt;/span&gt; App\Controller;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;use&lt;/span&gt; Symfony\Component\HttpFoundation\Response;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;use&lt;/span&gt; Symfony\Component\Routing\Annotation\Route;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;LuckyController&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;/**
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;     * @Route(&amp;#34;/lucky/number/{max}&amp;#34;, name=&amp;#34;app_lucky_number&amp;#34;)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;     */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;number&lt;/span&gt;(&lt;span style=&#34;color:#369&#34;&gt;$max&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#369&#34;&gt;$number&lt;/span&gt; = random_int(&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#369&#34;&gt;$max&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;new&lt;/span&gt; Response(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;&amp;lt;html&amp;gt;&amp;lt;body&amp;gt;Lucky number: &amp;#39;&lt;/span&gt;.&lt;span style=&#34;color:#369&#34;&gt;$number&lt;/span&gt;.&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        );
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Note that there is annotation for this controller:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;/**
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; * @Route(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;/lucky/number/{max}&amp;#34;&lt;/span&gt;, name=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;app_lucky_number&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; /*&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The first parameter describes the path. Of course we can elaborate our routes more; let’s consider the example below:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;namespace&lt;/span&gt; App\Controller;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;use&lt;/span&gt; Symfony\Component\HttpFoundation\Request;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;use&lt;/span&gt; Symfony\Component\HttpFoundation\Response;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;use&lt;/span&gt; Symfony\Component\Routing\Annotation\Route;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;use&lt;/span&gt; Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;MyController&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;extends&lt;/span&gt; AbstractController
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;/**
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;     * @Route(&amp;#34;/home&amp;#34;)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;     */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;home&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;new&lt;/span&gt; Response(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;home&amp;#34;&lt;/span&gt;,  Response::&lt;span style=&#34;color:#369&#34;&gt;HTTP_OK&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            [&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;content-type&amp;#39;&lt;/span&gt; =&amp;gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;text/plain&amp;#39;&lt;/span&gt;]);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;/**
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;     * @Route(&amp;#34;/about&amp;#34;, methods={&amp;#34;GET&amp;#34;, &amp;#34;POST&amp;#34;})
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;     */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;about&lt;/span&gt;(Request &lt;span style=&#34;color:#369&#34;&gt;$request&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#369&#34;&gt;$method&lt;/span&gt; = &lt;span style=&#34;color:#369&#34;&gt;$request&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;getRealMethod&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#369&#34;&gt;$msg&lt;/span&gt; = &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;about: &amp;#34;&lt;/span&gt; . &lt;span style=&#34;color:#369&#34;&gt;$method&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;new&lt;/span&gt; Response(&lt;span style=&#34;color:#369&#34;&gt;$msg&lt;/span&gt;,  Response::&lt;span style=&#34;color:#369&#34;&gt;HTTP_OK&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            [&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;content-type&amp;#39;&lt;/span&gt; =&amp;gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;text/plain&amp;#39;&lt;/span&gt;]);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;/**
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;     * @Route(&amp;#34;/news/{id}&amp;#34;, requirements={&amp;#34;page&amp;#34;=&amp;#34;\d+&amp;#34;})
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;     */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;news&lt;/span&gt;(&lt;span style=&#34;color:#369&#34;&gt;$id&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#369&#34;&gt;$msg&lt;/span&gt; = &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;News &amp;#39;&lt;/span&gt; . &lt;span style=&#34;color:#369&#34;&gt;$id&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;new&lt;/span&gt; Response(&lt;span style=&#34;color:#369&#34;&gt;$msg&lt;/span&gt;,  Response::&lt;span style=&#34;color:#369&#34;&gt;HTTP_OK&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            [&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;content-type&amp;#39;&lt;/span&gt; =&amp;gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;text/plain&amp;#39;&lt;/span&gt;]);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Code taken from &lt;a href=&#34;http://zetcode.com/symfony/routeannotation/&#34;&gt;zetcode.com&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;As we can see, we can specify which HTTP methods we support or even specify regular expressions for the URL parameters. Generating a URL is not difficult at all, one just needs to call &lt;code&gt;$this-&amp;gt;generateURL&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&#34;symfony-and-http&#34;&gt;Symfony and HTTP&lt;/h3&gt;
&lt;p&gt;As we could see earlier, Symfony actions are returning a Response. You can send JSON as Response, or even a JSONResponse, as the &lt;a href=&#34;https://symfony.com/doc/current/components/http_foundation.html#creating-a-json-response&#34;&gt;documentation&lt;/a&gt; describes. You can even specify a JSONP callback via&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#369&#34;&gt;$response&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;setCallback&lt;/span&gt;(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;handleResponse&amp;#39;&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Don’t be afraid of this, it’s not very difficult and it’s well-documented.&lt;/p&gt;
&lt;p&gt;Requests are also handled &lt;a href=&#34;https://symfony.com/doc/current/introduction/http_fundamentals.html&#34;&gt;nicely&lt;/a&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#369&#34;&gt;$request&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;query&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;get&lt;/span&gt;(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;id&amp;#39;&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;gets a GET parameter, while&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#369&#34;&gt;$request&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;request&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;get&lt;/span&gt;(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;category&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;default value&amp;#39;&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;is getting a post value.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#369&#34;&gt;$request&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;query&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;all&lt;/span&gt;()&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;retrieves all GET parameters, while&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#369&#34;&gt;$request&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;request&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;all&lt;/span&gt;()&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;retrieves all POST parameters.&lt;/p&gt;
&lt;h3 id=&#34;caching&#34;&gt;Caching&lt;/h3&gt;
&lt;p&gt;Symfony supports caching through the Cache module, which, when switched on, will cache configuration and routes. If controller actions or routes are changed, then it is advisable to clear the cache via:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;bin/console cache:clear &amp;amp;&amp;amp; bin/console cache:warmup&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Whether or not to switch on caching for your application at dev mode depends on the frequency of a need to cache cleanup. Read more &lt;a href=&#34;https://symfony.com/doc/current/cache.html&#34;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&#34;configuration&#34;&gt;Configuration&lt;/h3&gt;
&lt;p&gt;Symfony configuration can be done via YAML, XML or PHP files. In the projects I have been working with, configuration was done in YAML files stored in the config folder inside the root folder of the project.&lt;/p&gt;
&lt;p&gt;Inside the &lt;strong&gt;src&lt;/strong&gt; folder we can find &lt;strong&gt;Kernel.php&lt;/strong&gt; which glues together the project.&lt;/p&gt;
&lt;h3 id=&#34;twig&#34;&gt;Twig&lt;/h3&gt;
&lt;p&gt;Symfony is well-paired with Twig, a popular template engine. You might ask why we don’t use PHP for this purpose. We can use PHP or Smarty or other options for view templating, Twig is not required.&lt;/p&gt;
&lt;p&gt;Using template engines instead of allowing the developers to use PHP as a template engine has the drawback of limiting the options. However, this could also be seen as a positive thing. Yes, it limits the options of the developers, but this way developers will not find it so easy and convenient to temporarily (?) implement their business logic as part of the view.&lt;/p&gt;
&lt;p&gt;I’m okay with using PHP as a template engine as long as we do not mix up the layers of the application and by convention the view remains “only” the view. I’m also okay with using template engines, even though it is less convenient to use when we need to do some experimenting and code-writing for a very short time, after which it is reverted. In that kind of experimental work enforcing this separation is unnecessary bureaucracy, but the gain is that we will not have to deal with views with unnecessary implementations. So, this is a harmless form of bureaucracy, if such a thing exists.&lt;/p&gt;
&lt;h3 id=&#34;summary&#34;&gt;Summary&lt;/h3&gt;
&lt;p&gt;Symfony is a Framework which provides us with a way to work in conformity with the MVC pattern. Models are Entity classes along with their Repository pair, representing database tables, while Doctrine watches over the whole process, if used. Views are templates that generate HTML, for which Twig is a convenient tool, but you can use Symfony without relying on the usage of Twig. Configuration can be written in YAML, XML or PHP. PHP dependencies are handled by Composer, client-side dependencies can be handled by Yarn. Symfony has its own console that can be used to run different commands.&lt;/p&gt;
&lt;p&gt;All in all, there are lots of features which Symfony offers, so using Symfony a good option to consider when planning a project. Getting started with Symfony can be difficult, but I hope this article will help you get started using Symfony in your projects.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Seeking a PHP developer</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2019/10/seeking-php-developer/"/>
      <id>https://www.endpointdev.com/blog/2019/10/seeking-php-developer/</id>
      <published>2019-10-17T00:00:00+00:00</published>
      <author>
        <name>Jon Jensen</name>
      </author>
      <content type="html">
        &lt;img src=&#34;/blog/2019/10/seeking-php-developer/20190701-164659-crop.jpg&#34; alt=&#34;decommissioned pay phone&#34; /&gt;
&lt;!-- Photo by Jon Jensen --&gt;
&lt;p&gt;We are looking for another PHP software engineer, to work with us during business hours somewhere in the UTC-7 to UTC-4 time zones (U.S. Pacific to Eastern Time). This is a contract-to-hire role, and can be full time or part time.&lt;/p&gt;
&lt;p&gt;End Point is a 23-​year-old Internet technology consulting company based in New York City, with about 50 employees, most working remotely from home offices. We collaborate using SSH, GitLab, GitHub, chat, video conferencing, and good old email and phones.&lt;/p&gt;
&lt;p&gt;We serve many clients ranging from small family businesses to large corporations.&lt;/p&gt;
&lt;h3 id=&#34;what-you-will-be-doing&#34;&gt;What you will be doing:&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Develop new web applications and support existing ones for our clients&lt;/li&gt;
&lt;li&gt;Work together with End Point co-workers and our clients’ in-house staff&lt;/li&gt;
&lt;li&gt;Use your desktop OS of choice: Linux, macOS, Windows&lt;/li&gt;
&lt;li&gt;Use open source tools and contribute back as opportunity arises&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;what-you-bring&#34;&gt;What you bring:&lt;/h3&gt;
&lt;p&gt;Professional experience with web application development and support:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;5–7 years of development with PHP (older 5.x and newer 7.x versions) and JavaScript&lt;/li&gt;
&lt;li&gt;Frameworks such as Symfony or Laravel&lt;/li&gt;
&lt;li&gt;Databases such as PostgreSQL, MySQL, MongoDB, Redis, Solr, Elasticsearch, etc.&lt;/li&gt;
&lt;li&gt;Security consciousness&lt;/li&gt;
&lt;li&gt;Git version control&lt;/li&gt;
&lt;li&gt;Automated testing&lt;/li&gt;
&lt;li&gt;HTTP, REST APIs, and/or SOAP&lt;/li&gt;
&lt;li&gt;Magento experience is a plus!&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And just as important:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Strong verbal and written communication skills&lt;/li&gt;
&lt;li&gt;A good remote work environment&lt;/li&gt;
&lt;li&gt;An eye for detail&lt;/li&gt;
&lt;li&gt;Tenacity in solving problems&lt;/li&gt;
&lt;li&gt;Ownership of projects to get things done well&lt;/li&gt;
&lt;li&gt;Work both independently and as part of a team&lt;/li&gt;
&lt;li&gt;Focus on customer needs&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;what-work-here-offers&#34;&gt;What work here offers:&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Flexible work hours&lt;/li&gt;
&lt;li&gt;Annual bonus opportunity&lt;/li&gt;
&lt;li&gt;Freedom from being tied to an office location&lt;/li&gt;
&lt;li&gt;Collaborate with knowledgeable, friendly, and diligent co-workers around the world&lt;/li&gt;
&lt;li&gt;For full-time staff: paid holidays and vacation&lt;/li&gt;
&lt;li&gt;For U.S. employees: health insurance subsidy and 401(k) retirement savings plan&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;getting-in-touch-with-us&#34;&gt;Getting in touch with us:&lt;/h3&gt;
&lt;p&gt;&lt;del&gt;Please email us an introduction to jobs@endpointdev.com to apply.&lt;/del&gt;
&lt;strong&gt;(This job has been filled.)&lt;/strong&gt;
Include your location, a resume/​CV, your GitHub or LinkedIn URLs, or whatever else helps us get to know you.&lt;/p&gt;
&lt;p&gt;We look forward to hearing from you! Direct work seekers only, please—​this role is not for agencies or subcontractors.&lt;/p&gt;
&lt;p&gt;We are an equal opportunity employer and value diversity at our company. We do not discriminate on the basis of sex/​gender, race, religion, color, national origin, sexual orientation, age, marital status, veteran status, or disability status.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>How to set up a local development environment for WordPress from scratch</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2019/08/set-up-local-development-environment-for-wordpress/"/>
      <id>https://www.endpointdev.com/blog/2019/08/set-up-local-development-environment-for-wordpress/</id>
      <published>2019-08-07T00:00:00+00:00</published>
      <author>
        <name>Kevin Campusano</name>
      </author>
      <content type="html">
        &lt;p&gt;&lt;img src=&#34;/blog/2019/08/set-up-local-development-environment-for-wordpress/banner.png&#34; alt=&#34;Banner&#34;&gt;&lt;/p&gt;
&lt;p&gt;I recently got pulled into a project for a client who wanted to have a new WordPress website developed for them. I started by setting up a development environment with the niceties that I’m used to from my other application development work. That is, a development server, interactive debugging, linting, and a good editor.&lt;/p&gt;
&lt;p&gt;Another thing that I wanted was not to have to deal with LAMP or WAMP or XAMPP or any of that. I wanted a clean, from scratch installation where I knew and controlled everything that was there. I’ve got nothing against those packages, but I think that, by setting up everything manually, I’d be able to better learn the technology as I would know exactly how everything is set up under the hood. The shortcuts could come later.&lt;/p&gt;
&lt;p&gt;Luckily for me, there aren’t many pieces when it comes to setting up a basic, running development environment for WordPress. You only need three things: 1. MySQL, 2. PHP, and 3. WordPress itself. I also wanted a few other goodies and we’ll get there.&lt;/p&gt;
&lt;p&gt;Let’s go through the steps that I took to set all of this up:&lt;/p&gt;
&lt;h3 id=&#34;1-set-up-php&#34;&gt;1. Set up PHP&lt;/h3&gt;
&lt;p&gt;In Ubuntu, installing PHP is easy enough. Just run the following command:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sudo apt-get install php&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;After that’s done, run &lt;code&gt;php -v&lt;/code&gt; to validate that it was successfully installed. It should result in something like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;PHP 7.2.19-0ubuntu0.18.04.1 (cli) (built: Jun  4 2019 14:48:12) ( NTS )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Copyright (c) 1997-2018 The PHP Group
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Zend Engine v3.2.0, Copyright (c) 1998-2018 Zend Technologies
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    with Zend OPcache v7.2.19-0ubuntu0.18.04.1, Copyright (c) 1999-2018, by Zend Technologies&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;There’s one particular PHP extension that we’re going to need. Let’s install it with:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sudo apt-get install php-mysql&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;php-mysql&lt;/code&gt; extension is necessary for our PHP installation to interact with MySQL. That’s all that’s needed to run WordPress as far as PHP is concerned.&lt;/p&gt;
&lt;h3 id=&#34;2-set-up-mysql&#34;&gt;2. Set up MySQL&lt;/h3&gt;
&lt;p&gt;WordPress uses MySQL for all of its data storage concerns. So, let’s install it. Again, in Ubuntu, installing and setting up MySQL is super easy. First, we need to run this command:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sudo apt-get install mysql-server&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This will install both the MySQL database engine and a command-line client for us to connect to it and do some initial configuration. We now need to log into our freshly installed MySQL instance. But first, make sure that it’s running with:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sudo service mysql start&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now that our instance is running, log into it as &lt;code&gt;root&lt;/code&gt; with:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sudo mysql -u root&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This will open up the MySQL command-line client where we can do some initial configuration to support WordPress.&lt;/p&gt;
&lt;p&gt;We now need to create a new MySQL user that will be used by WordPress to log into the database. You can do so with a command like this from within the MySQL CLI client:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;CREATE&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;USER&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;wordpress_user&amp;#39;&lt;/span&gt;@&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;localhost&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;IDENTIFIED&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;BY&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;password&amp;#39;&lt;/span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Obviously, choose a username and password that work for you. I like to keep things simple and obvious so that’s what I use. Also obviously, use a strong, unique password in a production environment.&lt;/p&gt;
&lt;p&gt;Now, create a new database that will be used by WordPress with:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;CREATE&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;DATABASE&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;wordpress_dev;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Again, feel free to choose a name that suits your needs.&lt;/p&gt;
&lt;p&gt;Now, we need to allow the user that we created a few steps ago to access and control that new database. Since this is only a dev environment, let’s just give our WordPress user access to everything. This can be done with:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;GRANT&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;ALL&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;ON&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;*.*&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;TO&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;wordpress_user&amp;#39;&lt;/span&gt;@&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;localhost&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;With that, we’re done with MySQL, we can close the CLI client with the exit command.&lt;/p&gt;
&lt;h3 id=&#34;3-set-up-wordpress&#34;&gt;3. Set up WordPress&lt;/h3&gt;
&lt;p&gt;Now that we have our prerequisites ready, we can proceed to setting up the actual WordPress site. It turns out, this is pretty easy as well. Get a new directory ready and let’s get started.&lt;/p&gt;
&lt;p&gt;First, we need to download the package of WordPress files from the official site. In Ubuntu, this can be done with this command:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;wget https://wordpress.org/latest.tar.gz&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This will result in a new &lt;code&gt;latest.tar.gz&lt;/code&gt; file being created in your directory. Now, extract it with:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;tar xvzf latest.tar.gz&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Then, enter the new &lt;code&gt;wordpress&lt;/code&gt; directory that gets created as a result of the last operation. Explore the &lt;code&gt;wordpress&lt;/code&gt; directory and you should be able to see a bunch of &lt;code&gt;wp-*&lt;/code&gt; files and directories. These are all the files that WordPress needs to run.&lt;/p&gt;
&lt;p&gt;Before running WordPress though, we need to configure it so that it uses the MySQL database that we just created. We do this by specifying that configuration in a &lt;code&gt;wp-config.php&lt;/code&gt; file. This file does not exist yet, but we have a &lt;code&gt;wp-config-sample.php&lt;/code&gt; file that we can use as a template. Create the new &lt;code&gt;wp-config.php&lt;/code&gt; file based on &lt;code&gt;wp-config-sample.php&lt;/code&gt; with the following command:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;cp wp-config-sample.php wp-config.php&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now, in the new &lt;code&gt;wp-config.php&lt;/code&gt; file, put in your MySQL database information. Starting around line 23, it should look like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;/** The name of the database for WordPress */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;define( &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;DB_NAME&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;wordpress_dev&amp;#39;&lt;/span&gt; );
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;/** MySQL database username */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;define( &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;DB_USER&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;wordpress_user&amp;#39;&lt;/span&gt; );
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;/** MySQL database password */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;define( &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;DB_PASSWORD&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;password&amp;#39;&lt;/span&gt; );
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;/** MySQL hostname */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;define( &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;DB_HOST&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;localhost&amp;#39;&lt;/span&gt; );
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;/** Database Charset to use in creating database tables. */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;define( &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;DB_CHARSET&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;utf8&amp;#39;&lt;/span&gt; );
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;/** The Database Collate type. Don&amp;#39;t change this if in doubt. */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;define( &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;DB_COLLATE&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;&amp;#39;&lt;/span&gt; );&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3 id=&#34;4-run-wordpress&#34;&gt;4. Run WordPress&lt;/h3&gt;
&lt;p&gt;Now we’re finally ready to actually run WordPress. WordPress, as a web application, needs a web server like Apache to run. We don’t have Apache though; what we have is PHP’s built-in development web server. From within our &lt;code&gt;wordpress&lt;/code&gt; directory, we can fire up the built-in web server with:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;php -S localhost:3000&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now it’s just a matter of navigating to the &lt;code&gt;localhost:3000/wp-admin/install.php&lt;/code&gt; page in your browser of choice. This page should show up on screen:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2019/08/set-up-local-development-environment-for-wordpress/wordpress-language-select.png&#34; alt=&#34;WordPress Language Select&#34;&gt;&lt;/p&gt;
&lt;p&gt;Just follow the steps within the wizard at &lt;code&gt;install.php&lt;/code&gt; and your new development WordPress site will be ready to go in no time.&lt;/p&gt;
&lt;h3 id=&#34;bonus-1-set-up-a-linter-php-code-sniffer&#34;&gt;Bonus 1: Set up a linter: PHP Code Sniffer&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;Prerequisites: Composer and the &lt;code&gt;php-xml&lt;/code&gt; extension&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;You can install Composer by following &lt;a href=&#34;https://getcomposer.org/download/&#34;&gt;these instructions&lt;/a&gt;. In Ubuntu, installing the &lt;code&gt;php-xml&lt;/code&gt; extension can be done with:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sudo apt-get install php-xml&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now, to set up the PHP Code Sniffer linter, just follow these steps, from your &lt;code&gt;wordpress&lt;/code&gt; directory:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Install PHP Code Sniffer with &lt;code&gt;composer require --dev squizlabs/php_codesniffer&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Download the WordPress PHP Code Sniffer standards with &lt;code&gt;composer require --dev wp-coding-standards/wpcs&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Install it into &lt;code&gt;phpcs&lt;/code&gt; with: &lt;code&gt;vendor/bin/phpcs --config-set installed_paths vendor/wp-coding-standards/wpcs&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Sniff something with &lt;code&gt;vendor/bin/phpcs --standard=WordPress index.php&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;If all goes well, you should be able to see reports like this (which is exaggerated for demonstration purposes; your default index.php file will not show as many warnings):&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;FILE: /home/kevin/projects/random/wordpress/index.php
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;-----------------------------------------------------------------------------------------
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;FOUND 5 ERRORS AND 1 WARNING AFFECTING 2 LINES
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;-----------------------------------------------------------------------------------------
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; 14 | ERROR   | [x] Expected 1 spaces after opening bracket; 0 found
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; 14 | ERROR   | [x] Expected 1 spaces before closing bracket; 0 found
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; 17 | WARNING | [x] &amp;#34;require&amp;#34; is a statement not a function; no parentheses are required
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; 17 | ERROR   | [x] Expected 1 spaces after opening bracket; 0 found
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; 17 | ERROR   | [x] Expected 1 spaces before closing bracket; 0 found
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; 17 | ERROR   | [x] Concat operator must be surrounded by a single space
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;-----------------------------------------------------------------------------------------
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;PHPCBF CAN FIX THE 6 MARKED SNIFF VIOLATIONS AUTOMATICALLY
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;-----------------------------------------------------------------------------------------
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Time: 103ms; Memory: 10MB&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3 id=&#34;bonus-2-setup-interactive-debugging-with-vs-code&#34;&gt;Bonus 2: Setup interactive debugging with VS Code&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;Prerequisites: VS Code, XDebug and the PHP Debug VS Code extension&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;For writing PHP, my editor of choice is VS Code. You can get the editor from the &lt;a href=&#34;https://code.visualstudio.com/download&#34;&gt;official download site&lt;/a&gt;. VS Code has a huge extensions ecosystem where you can find almost anything. Naturally, there’s a debugger for PHP. It’s aptly called PHP Debug. See the &lt;a href=&#34;https://marketplace.visualstudio.com/items?itemName=felixfbecker.php-debug&#34;&gt;instructions on how to set it up&lt;/a&gt;. The PHP Debug extension works on top of XDebug, a debugger for PHP. Luckily for us, PHP Debug’s instructions page includes all the details on how to install and set up both itself and XDebug.&lt;/p&gt;
&lt;p&gt;If you followed the instructions, you should now have a new &lt;code&gt;.vscode/launch.json&lt;/code&gt; file within your &lt;code&gt;wordpress&lt;/code&gt; directory with the following contents:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;version&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;0.2.0&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;configurations&amp;#34;&lt;/span&gt;: [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;           &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;name&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Listen for XDebug&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;           &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;type&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;php&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;           &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;request&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;launch&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;           &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;#34;port&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;9000&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   ]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This is what’s called a &lt;code&gt;launch configuration&lt;/code&gt; in VS Code speak. This tells VS Code’s debugger all the info it needs to attach to a running PHP process. To see it in action, fire up the built-in web development server with:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;php -S localhost:3000&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Then, click the &lt;code&gt;Start Debugging&lt;/code&gt; button. That’s the green triangle icon near the top of the screen, when you select the Debug sidebar in VS Code. It should have the &lt;code&gt;Listen for XDebug&lt;/code&gt; option selected.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2019/08/set-up-local-development-environment-for-wordpress/vscode-start-debugger.png&#34; alt=&#34;VS Code Start Debugger&#34;&gt;&lt;/p&gt;
&lt;p&gt;Now it’s just a matter of putting a breakpoint anywhere within the source code and request the page from your browser. You can set a breakpoint by clicking right next to the line number indicator, within any file. Here, I’ve put a breakpoint on line 14 in &lt;code&gt;index.php&lt;/code&gt;:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2019/08/set-up-local-development-environment-for-wordpress/vscode-breakpoint.png&#34; alt=&#34;VS Code Breakpoint&#34;&gt;&lt;/p&gt;
&lt;p&gt;When the code execution hits the breakpoint, it should stop right there and allow you to inspect variables and the like, just like any other debugger.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2019/08/set-up-local-development-environment-for-wordpress/vscode-debugging.png&#34; alt=&#34;VS Code Breakpoint&#34;&gt;&lt;/p&gt;
&lt;p&gt;And that’s all for now! Hopefully this little write-up can help you jumpstart your next WordPress development project.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Running Magento 2 in Windows with XAMPP</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2019/03/running-magento-2-windows-xampp/"/>
      <id>https://www.endpointdev.com/blog/2019/03/running-magento-2-windows-xampp/</id>
      <published>2019-03-22T00:00:00+00:00</published>
      <author>
        <name>Juan Pablo Ventoso</name>
      </author>
      <content type="html">
        &lt;p&gt;&lt;img src=&#34;/blog/2019/03/running-magento-2-windows-xampp/e-commerce-safe.jpg&#34; alt=&#34;Ecommerce&#34; /&gt;&lt;br&gt;&lt;a href=&#34;https://burst.shopify.com/photos/computer-security-lock-and-payment?q=e-commerce&#34;&gt;Photo by Nicole De Khors&lt;/a&gt; · &lt;a href=&#34;https://burst.shopify.com/licenses/shopify-some-rights-reserved&#34;&gt;Burst, Some Rights Reserved&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Magento is an open source ecommerce platform, written in PHP and relying on MySQL/​MariaDB for persistence. According to &lt;a href=&#34;https://trends.builtwith.com/shop&#34;&gt;BuiltWith&lt;/a&gt;, Magento is the third most used platform in ecommerce websites. It began its life in 2008 with its first general release, and a major update (Magento 2) was released in 2015.&lt;/p&gt;
&lt;p&gt;And now, more than three years after, Magento 1 is slowly dying: There won’t be any more quality fixes or security updates from June 2020, and there won’t be extended support for fixes or new payment methods. So the obvious choice will be Magento 2 from now on.&lt;/p&gt;
&lt;p&gt;But is it fully tested yet? Is it stable enough? If we already have a website running with Magento 1, what should we do? Migrating to Magento 2 is not just hitting an “Update” button: Themes are incompatible, most extensions won’t work, and of course, there’s a big set of changes to get familiar with.&lt;/p&gt;
&lt;p&gt;So a good approach might be to get a clean Magento 2 version deployed locally, to look what we need to do to get our website updated and running, test the backend, find where the configuration sections are located, and so on. And many business users, and even some developers like myself, have Microsoft Windows installed on our computers.&lt;/p&gt;
&lt;h3 id=&#34;environment-setup&#34;&gt;Environment setup&lt;/h3&gt;
&lt;p&gt;The environment I used for this testing installation was Windows 10 Professional. As a first step, we’ll need to make sure that &lt;code&gt;localhost&lt;/code&gt; is published in our local hosts file:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Navigate to the folder &lt;code&gt;%SystemRoot%\system32\drivers\etc&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Backup the existing hosts file&lt;/li&gt;
&lt;li&gt;Open a text editor with administrator rights&lt;/li&gt;
&lt;li&gt;Open the hosts file&lt;/li&gt;
&lt;li&gt;Make sure the first line after the commented (#) lines is &lt;code&gt;127.0.0.1 localhost&lt;/code&gt; and the second is &lt;code&gt;::1 localhost&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Open a cmd window with administrator rights and run the command &lt;code&gt;ipconfig /flushdns&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Now we’re ready to install the environment needed to run Magento. I recommend using &lt;a href=&#34;https://www.apachefriends.org/&#34;&gt;XAMPP&lt;/a&gt;, a free Apache distribution for Windows that includes MariaDB, PHP, and Perl in a single package. Magento 2 currently runs with PHP 7.2 and it will not work with newer versions.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Install XAMPP 7.2 with default settings: &lt;a href=&#34;https://www.apachefriends.org/xampp-files/7.2.15/xampp-win32-7.2.15-0-VC15-installer.exe&#34;&gt;apachefriends.org/xampp-files/7.2.15/xampp-win32-7.2.15-0-VC15-installer.exe&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Download Magento 2. You will need to register on the website first: &lt;a href=&#34;https://magento.com/tech-resources/download&#34;&gt;magento.com/tech-resources/download&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Create a new “magento” subfolder (or whatever name you prefer) inside the htdocs folder in the XAMPP installation (usually &lt;code&gt;C:\xampp&lt;/code&gt;) and uncompress the Magento 2 archive there.&lt;/li&gt;
&lt;li&gt;Start the XAMPP Control Panel from the Windows start menu. In the “Apache” section, click the “Config” button and, on the menu that appears, select “PHP (php.ini)”. Remove the semicolon before the &lt;code&gt;extension=intl&lt;/code&gt;, &lt;code&gt;extension=soap&lt;/code&gt;, and &lt;code&gt;extension=xsl&lt;/code&gt; texts to enable the intl, soap and xsl extensions.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2019/03/running-magento-2-windows-xampp/xampp-control-panel.jpg&#34; /&gt;&lt;br&gt;&lt;small&gt;Screenshot of the XAMPP Control Panel with the PHP config menu displayed.&lt;/small&gt;&lt;/p&gt;
&lt;h3 id=&#34;mysql-and-magento-setup&#34;&gt;MySQL and Magento setup&lt;/h3&gt;
&lt;p&gt;We have all the files in place and the environment ready to start configuring the database and install Magento 2.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Start the Apache and MySQL services from the XAMPP Control Panel. Wait for the green status texts to appear.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create a “magento” (or whatever name you prefer) database in MySQL from phpMyAdmin, installed already on XAMPP: &lt;a href=&#34;http://localhost/phpmyadmin&#34;&gt;localhost/phpmyadmin&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Run the Magento 2 setup from &lt;a href=&#34;http://localhost/magento&#34;&gt;localhost/magento&lt;/a&gt; (replace the “magento” part of the URL with whatever name you have chosen to host Magento). If the setup program requires to do any additional configuration change, do it as instructed. Do a screenshot or save the final page contents for later use.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2019/03/running-magento-2-windows-xampp/magento-2-installation-success.jpg&#34; /&gt;&lt;br&gt;&lt;small&gt;Example of the page that will be displayed when the Magento 2 installation finishes.&lt;/small&gt;&lt;/p&gt;
&lt;h3 id=&#34;fixing-known-issues&#34;&gt;Fixing known issues&lt;/h3&gt;
&lt;p&gt;When we finish the installation process, we will have a Magento 2 instance running on our host. But we’re not ready yet! There are a couple known bugs with Magento 2 and XAMPP at the moment I’m writing this post:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;The Magento admin page may not work (it shows a blank page): Fix this issue by updating the “isPathInDirectories” function inside the &lt;code&gt;Validator.php&lt;/code&gt; file as instructed in &lt;a href=&#34;https://magento.stackexchange.com/questions/252188/magento-2-2-7-admin-panel-blank-page&#34;&gt;this article.&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;We might not be able to upload images (like a custom logo or product pictures): Fix this issue by updating the &lt;code&gt;design_config_form.xml&lt;/code&gt; file as shown &lt;a href=&#34;https://community.magento.com/t5/Magento-2-x-Technical-Issues/A-technical-problem-with-the-server-created-an-error-Try-again/m-p/115085#M7549&#34;&gt;here.&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;After we finish making these changes, we will need to &lt;b&gt;restart the Apache service&lt;/b&gt; from the XAMPP control panel. And that’s it! We should be ready to open the Magento front page, and login into the backend.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2019/03/running-magento-2-windows-xampp/magento-2-front-end.jpg&#34; /&gt;&lt;br&gt;&lt;small&gt;An empty home page with the default theme enabled in Magento 2.&lt;/small&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2019/03/running-magento-2-windows-xampp/magento-2-back-end.jpg&#34; /&gt;&lt;br&gt;&lt;small&gt;This is how the Magento 2 back-end dashboard looks like once we login.&lt;/small&gt;&lt;/p&gt;
&lt;h3 id=&#34;setting-up-the-store-a-roadmap&#34;&gt;Setting up the store: A roadmap&lt;/h3&gt;
&lt;p&gt;The purpose of this article is to get Magento 2 up and running on Windows computer, and that’s what I hope we’ve achieved so far. But just to point in a direction on what to do next, below is my usual roadmap to set up the store, add a few products and publish the catalog on the home page.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Change the default store name and logo from the section Content &amp;gt; Design &amp;gt; Configuration and choose “Edit” in the “Default store view”.&lt;/li&gt;
&lt;li&gt;Set up the default store configuration from the section Stores &amp;gt; Configuration.&lt;/li&gt;
&lt;li&gt;Create the product categories from the section Catalog &amp;gt; Categories. Set up a category name and its dependency from another category, or from the default master category.&lt;/li&gt;
&lt;li&gt;Create/edit the product attributes from the section Stores &amp;gt; Attributes (check the option “visible in storefront” to show the products in the product page). For example, custom attributes for book products would be Author, Publisher&amp;hellip;&lt;/li&gt;
&lt;li&gt;Add the attributes to the default attribute set from the section Stores &amp;gt; Attribute. This is required for the attributes to show up when adding a new product by default.&lt;/li&gt;
&lt;li&gt;Create a set of products from the section Catalog &amp;gt; Products. Set the custom variations for each product from the “Customizable options” section. Set up a short and long description, a custom SKU for each customizable option, and a tax mode.&lt;/li&gt;
&lt;li&gt;Add a widget to the homepage to show the product catalog from the section Content &amp;gt; Homepage &amp;gt; Edit &amp;gt; Show/Hide Editor &amp;gt; Add widget &amp;gt; Products list. Here we can also add any content we like, including HTML tags, images, and third-party content.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;It’s possible—​and pretty straightforward—​to run a Magento 2 instance in Windows with XAMPP. But this is just a kick-off, a simple overview: there’s a lot more to do if we want to get a full ecommerce website that is ready to scale up and support enterprise-​level traffic, like caching optimization, load balancing, and advanced server-​side monitoring.&lt;/p&gt;
&lt;p&gt;At End Point, we have professionals with a strong background in deploying powerful, reliable, fully responsive, fast, and SEO-​optimized ecommerce websites. We have experience in migrating Magento to newer versions, and even creating full Magento 2 websites from scratch. &lt;a href=&#34;/contact/&#34;&gt;Drop us a line if you want to hear more.&lt;/a&gt;&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>How to use Zend Framework components in a console app</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2018/11/how-to-use-zend-framework-components-in-a-console-app/"/>
      <id>https://www.endpointdev.com/blog/2018/11/how-to-use-zend-framework-components-in-a-console-app/</id>
      <published>2018-11-21T00:00:00+00:00</published>
      <author>
        <name>Kevin Campusano</name>
      </author>
      <content type="html">
        &lt;p&gt;&lt;img src=&#34;how-to-use-zend-framework-components-in-a-console-app/banner.png&#34; alt=&#34;Banner&#34;&gt;&lt;/p&gt;
&lt;p&gt;When Rasmus Lerdorf created &lt;a href=&#34;https://php.net/&#34;&gt;PHP&lt;/a&gt; in the ’90s, I bet he never thought that his language would become the engine that powers much of the web, even today, 23 years later.&lt;/p&gt;
&lt;p&gt;PHP is indeed super popular. Part of that popularity stems for the fact that it can run pretty much anywhere. You’d be hard pressed to find a hosting solution that doesn’t support PHP out of the box, even the cheapest ones. It is also fully functional in pretty much any version of the most used operating systems so development and deployment have a very low barrier of entry.&lt;/p&gt;
&lt;p&gt;Both because and as a result of this ubiquity and popularity, PHP also boasts an expansive, active community and a rich ecosystem of &lt;a href=&#34;https://php.net/docs.php&#34;&gt;documentation&lt;/a&gt;, tools, libraries and frameworks. &lt;a href=&#34;https://framework.zend.com/&#34;&gt;Zend Framework&lt;/a&gt; is a great example of the latter.&lt;/p&gt;
&lt;p&gt;I started using Zend Framework a good while ago now, when it became clear to me that writing vanilla PHP wasn’t going to cut it anymore for moderately sized projects. I needed help and Zend Framework extended a very welcomed helping hand. Zend Framework is basically a big collection of libraries and frameworks that cover most of the needs that we would have as web developers building PHP applications.&lt;/p&gt;
&lt;p&gt;While most parts of Zend Framework are standalone components that we can plug into an existing code base, picking and choosing what’s needed, there’s also the likes of &lt;a href=&#34;https://docs.zendframework.com/tutorials/getting-started/overview/&#34;&gt;Zend MVC&lt;/a&gt;. Zend MVC is, in its own right, a framework for developing web applications using the MVC design pattern (shocker, I know). A Zend MVC application is itself comprised of several other components from Zend Framework’s library like Zend Form (which helps with developing HTML forms), Zend DB (which helps with database access), Zend Validator (which helps with input validation), and many more.&lt;/p&gt;
&lt;p&gt;Obviously, these are very useful within the context of web applications but they can also be valuable on their own. One could put together a PHP console app and make one’s life much easier by leveraging the features provided by these libraries. Coming back to the point of PHP’s ubiquity, it is very easy to write such a script that runs in a server where a PHP web app is already deployed. No installation of an additional language interpreter needed, which means no need for special server permissions. No fuss, just good old PHP, which is already running on the server, being used to perform some backend task, some database maintenance, some file downloading, etc.&lt;/p&gt;
&lt;p&gt;So, the question becomes, how do we leverage the power of Zend Framework components outside the context of a Zend MVC application? Composer is the key.&lt;/p&gt;
&lt;p&gt;Composer is a widely used package manager and module loader for PHP, and it is what powers Zend MVC applications’ module resolution engine.&lt;/p&gt;
&lt;p&gt;In vanilla PHP, the &lt;code&gt;include&lt;/code&gt; and &lt;code&gt;require&lt;/code&gt; statements are the main forms of code reuse. When we want to bring functions or classes defined in other files to our script, these are what we use. PHP also supports namespaces. In their most abstract definition, namespaces are things used for organizing other things. In a programming language, they are little more than names (series of characters) that group together program constructs like classes, functions, variables, etc.&lt;/p&gt;
&lt;p&gt;Some PHP file in our source code could look like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;// calc.php
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;&amp;lt;?php
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;namespace&lt;/span&gt; Application\MathTools;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;ArithmeticCalculator&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;add&lt;/span&gt;(&lt;span style=&#34;color:#369&#34;&gt;$x&lt;/span&gt;, &lt;span style=&#34;color:#369&#34;&gt;$y&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$x&lt;/span&gt; + &lt;span style=&#34;color:#369&#34;&gt;$y&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;namespace&lt;/code&gt; statement near the top says that our &lt;code&gt;ArithmeticCalculator&lt;/code&gt; is in the &lt;code&gt;Application\MathTools&lt;/code&gt; namespace.&lt;/p&gt;
&lt;p&gt;Elsewhere in our project, we could have something like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;// index.php
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;&amp;lt;?php
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;use&lt;/span&gt; Application\MathTools\ArithmeticCalculator;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#369&#34;&gt;$calc&lt;/span&gt; = &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;new&lt;/span&gt; ArithmeticCalculator();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#369&#34;&gt;$result&lt;/span&gt; = &lt;span style=&#34;color:#369&#34;&gt;$calc&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;add&lt;/span&gt;(&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;5&lt;/span&gt;, &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;6&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;echo&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$result&lt;/span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In this file, we’re getting a reference to the &lt;code&gt;ArithmeticCalculator&lt;/code&gt; via its namespace. That is, we’re calling it with its fully qualified name.&lt;/p&gt;
&lt;p&gt;The funny thing, though, is that namespaces do not serve to resolve dependencies. That is, just because we write &lt;code&gt;use Application\MathTools\ArithmeticCalculator;&lt;/code&gt; does not mean that PHP knows where the file that actually contains the class is located. In other words, the &lt;code&gt;use&lt;/code&gt; statement doesn’t know how to go out of the current file and look for the corresponding &lt;code&gt;namespace&lt;/code&gt; statements in other files to figure out what to include. Given that, the savvy reader would be able to deduce that the previous code doesn’t actually work. In fact, if we run this as it is, it will most surely throw an exception with PHP complaining about just what we discussed.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;how-to-use-zend-framework-components-in-a-console-app/error.png&#34; alt=&#34;Class not found error&#34;&gt;&lt;/p&gt;
&lt;p&gt;Not fun. We’re asking PHP to use a namespace that’s not in the current executing context. To fix the code above we just need to add our trusty &lt;code&gt;include&lt;/code&gt; (or &lt;code&gt;require&lt;/code&gt;) statement. Like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;// index.php
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;&amp;lt;?php
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;include&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;calc.php&amp;#39;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;// ... rest of the code ...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now it does work. Well, hurray, I guess. It’s still kind of a bummer because we now have to use BOTH &lt;code&gt;include&lt;/code&gt;/&lt;code&gt;require&lt;/code&gt; AND &lt;code&gt;use&lt;/code&gt; if we want to use namespaces. Imagine how quickly will that become a mess in a project with a good number of files. Thankfully, Composer can help us with that.&lt;/p&gt;
&lt;p&gt;Alright, after that long tangent about requiring and including scripts and using namespaces, finally Composer comes back into the picture. Composer includes what’s called an autoloader. An autoloader is precisely what we need here because we can use it to make it so our scripts don’t need to use &lt;code&gt;require&lt;/code&gt;/&lt;code&gt;include&lt;/code&gt; statements anymore. The autoloader takes notice of the &lt;code&gt;use&lt;/code&gt; statements and then includes the actual files where the source code lives as it sees fit. Alright, let’s see how we can create a Composer managed PHP project and use the files that we already have.&lt;/p&gt;
&lt;p&gt;First of all, make sure to install Composer from &lt;a href=&#34;https://getcomposer.org/&#34;&gt;their official website&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Let’s create a new directory to work in. I’m going to call mine &lt;code&gt;my-console-app&lt;/code&gt;. Go into it and run &lt;code&gt;composer init&lt;/code&gt; in your console.&lt;/p&gt;
&lt;p&gt;This will result in Composer’s config generator showing up. It is an interactive prompt that helps you through the setup of your project’s &lt;code&gt;composer.json&lt;/code&gt; file. That file’s purpose is to store references to all of your dependencies as well as some general project information like name, description, author, license and the like. For now, just hit enter a few times until asked whether you’d like to interactively define your dependencies for both dev and prod. Say &lt;code&gt;no&lt;/code&gt; to those and then finally say &lt;code&gt;yes&lt;/code&gt; when asked to confirm generation. When all is said and done, the whole thing should look like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ composer init
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  Welcome to the Composer config generator
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;This command will guide you through creating your composer.json config.
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Package name (&amp;lt;vendor&amp;gt;/&amp;lt;name&amp;gt;) [kevin/my-console-app]:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Description []:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Author [Kevin Campusano &amp;lt;kcampusano@endpointdev.com&amp;gt;, n to skip]:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Minimum Stability []:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Package Type (e.g. library, project, metapackage, composer-plugin) []:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;License []:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Define your dependencies.
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Would you like to define your dependencies (require) interactively [yes]? n
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Would you like to define your dev dependencies (require-dev) interactively [yes]? n
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;#34;name&amp;#34;: &amp;#34;kevin/my-console-app&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;#34;authors&amp;#34;: [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &amp;#34;name&amp;#34;: &amp;#34;Kevin Campusano&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &amp;#34;email&amp;#34;: &amp;#34;kcampusano@endpointdev.com&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;#34;require&amp;#34;: {}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Do you confirm generation [yes]? y&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You should have a brand new &lt;code&gt;composer.json&lt;/code&gt; file created with that final JSON content that &lt;code&gt;composer init&lt;/code&gt; showed us in the console.&lt;/p&gt;
&lt;p&gt;Alright, now let’s, in that same directory, create two PHP files with the contents that we saw above. Here they are again for your convenience:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;// calc.php
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;&amp;lt;?php
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;namespace&lt;/span&gt; Application\MathTools;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;ArithmeticCalculator&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;add&lt;/span&gt;(&lt;span style=&#34;color:#369&#34;&gt;$x&lt;/span&gt;, &lt;span style=&#34;color:#369&#34;&gt;$y&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$x&lt;/span&gt; + &lt;span style=&#34;color:#369&#34;&gt;$y&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;// index.php
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;&amp;lt;?php
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;use&lt;/span&gt; Application\MathTools\ArithmeticCalculator;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#369&#34;&gt;$calc&lt;/span&gt; = &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;new&lt;/span&gt; ArithmeticCalculator();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#369&#34;&gt;$result&lt;/span&gt; = &lt;span style=&#34;color:#369&#34;&gt;$calc&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;add&lt;/span&gt;(&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;5&lt;/span&gt;, &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;6&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;echo&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$result&lt;/span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The directory now looks like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ ls
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;calc.php  composer.json  index.php&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This, right now, still doesn’t work. We need to wire up the autoloading. First, let’s tell Composer which files we want to autoload by adding this to our &lt;code&gt;composer.json&lt;/code&gt; file.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;// composer.json
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// ...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;autoload&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;classmap&amp;#34;&lt;/span&gt;: [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;calc.php&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        ]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// ...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Here, we just use a file name directly but it also supports directories.&lt;/p&gt;
&lt;p&gt;Next, we need to generate the actual runtime component that will do the autoloading. We do this with this command:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;composer dump-autoload -o&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This creates a series of files that contain the actual autoloading logic to fulfill what we described in our &lt;code&gt;composer.json&lt;/code&gt; file. They live inside a new &lt;code&gt;vendor&lt;/code&gt; directory created at the root of our project. A quick &lt;code&gt;ls&lt;/code&gt; reveals the current state of our directory:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ ls
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;calc.php  composer.json  index.php  vendor&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This new &lt;code&gt;vendor&lt;/code&gt; directory is also where Composer stores all of the dependencies that we download and add to our project. More on that later though.&lt;/p&gt;
&lt;p&gt;Ok, so back to our code. The only thing remaining before we can enjoy Composer’s autoloading feature is to include the file generated in the previous step in our script. Let’s add the following line at the top of our &lt;code&gt;index.php&lt;/code&gt; file:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;// index.php
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;&amp;lt;?php
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;include&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;vendor/autoload.php&amp;#39;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;// ...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now, run &lt;code&gt;index.php&lt;/code&gt; in your favorite web server or in the console and bask in the glory of hardcoded simple arithmetics.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ php index.php
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;11&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Pretty sweet, huh? So, what have we done so far? We’ve created a Composer project by setting up a &lt;code&gt;composer.json&lt;/code&gt; file. We’ve developed a simple application that adds two hardcoded numbers using a separate calculator class. That class is defined in a separate file which is annotated with a namespace. It gets included in our main application entry point (i.e. &lt;code&gt;index.php&lt;/code&gt;) using Composer’s autoloading feature. That is, instead of including the file directly with something like &lt;code&gt;include &#39;calc.php&#39;;&lt;/code&gt;, we’ve included it using namespaces and &lt;code&gt;use&lt;/code&gt; statements like &lt;code&gt;use Application\MathTools\ArithmeticCalculator;&lt;/code&gt;. The only gotcha is that, in order to enable autoloading in our main file, we need to include the autoloader using &lt;code&gt;include &#39;vendor/autoload.php&#39;;&lt;/code&gt;. We generated this file by configuring the &lt;code&gt;composer.json&lt;/code&gt; file with the list of files that contain classes that we want to autoload and then running Composer’s own &lt;code&gt;composer dump-autoload -o&lt;/code&gt; command.&lt;/p&gt;
&lt;p&gt;Ok, now that we’ve learned all that, we’re ready to get back to our initial question: “How do we leverage the power of Zend Framework components outside the context of a Zend MVC application?”&lt;/p&gt;
&lt;p&gt;As we’ve seen already, we can run our application in multiple ways. We can serve our directory in a web server and access &lt;code&gt;index.php&lt;/code&gt; from a browser. We can also just run it from the console with something like &lt;code&gt;php index.php&lt;/code&gt;. The result will be the same: the addition of 5 plus 6 printed to either the console or the response buffer. This is a console application so let’s stick with running it via the console.&lt;/p&gt;
&lt;p&gt;Now, as we’ve discussed before, Zend Framework offers many libraries that we can use. For the purposes of our demonstration, let’s use the &lt;a href=&#34;https://docs.zendframework.com/zend-validator/&#34;&gt;Zend Validator&lt;/a&gt; library. We can install it in our project by running:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;composer require zendframework/zend-validator&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;After a few seconds of Composer printing out stuff to the console, the package will be installed. The &lt;code&gt;composer.json&lt;/code&gt; file will be updated and will include the new package in the &lt;code&gt;require&lt;/code&gt; section. Like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;// composer.json
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// ...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;require&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;zendframework/zend-validator&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;^2.10&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The required files were downloaded into the &lt;code&gt;vendor&lt;/code&gt; folder but we don’t really care too much about that right now. It’s important to note though, that we should never be changing anything in there, just let Composer do its job. Now, back in our &lt;code&gt;index.php&lt;/code&gt; file we can “use” our new validator package and start validating some stuff. Our file could look like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;?php
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;include&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;vendor/autoload.php&amp;#39;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;use&lt;/span&gt; Zend\Validator\EmailAddress;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;use&lt;/span&gt; Zend\Validator\CreditCard;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;// Imagine these come from a file, database or http request or something
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#369&#34;&gt;$emailsToValidate&lt;/span&gt; = [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;kcampusano@endpointdev.com&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;what is this?&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;312321&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;null&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#369&#34;&gt;$creditCardsToValidate&lt;/span&gt; = [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;4111111111111111&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;what is this?&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;9128173&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;null&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;echo&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Validating emails... &lt;/span&gt;&lt;span style=&#34;color:#04d;background-color:#fff0f0&#34;&gt;\n\n&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#369&#34;&gt;$emailValidator&lt;/span&gt; = &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;new&lt;/span&gt; EmailAddress();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;foreach&lt;/span&gt; (&lt;span style=&#34;color:#369&#34;&gt;$emailsToValidate&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;as&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$email&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#369&#34;&gt;$emailValidator&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;isValid&lt;/span&gt;(&lt;span style=&#34;color:#369&#34;&gt;$email&lt;/span&gt;)) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;echo&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;✔ the &lt;/span&gt;&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;{&lt;/span&gt;&lt;span style=&#34;color:#369&#34;&gt;$email&lt;/span&gt;&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt; email is valid&lt;/span&gt;&lt;span style=&#34;color:#04d;background-color:#fff0f0&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    } &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;else&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;echo&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;✕ the &lt;/span&gt;&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;{&lt;/span&gt;&lt;span style=&#34;color:#369&#34;&gt;$email&lt;/span&gt;&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt; email is not valid&lt;/span&gt;&lt;span style=&#34;color:#04d;background-color:#fff0f0&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;echo&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#04d;background-color:#fff0f0&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;echo&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Validating credit cards... &lt;/span&gt;&lt;span style=&#34;color:#04d;background-color:#fff0f0&#34;&gt;\n\n&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#369&#34;&gt;$creditCardValidator&lt;/span&gt; = &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;new&lt;/span&gt; CreditCard();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;foreach&lt;/span&gt; (&lt;span style=&#34;color:#369&#34;&gt;$creditCardsToValidate&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;as&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$card&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#369&#34;&gt;$creditCardValidator&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;isValid&lt;/span&gt;(&lt;span style=&#34;color:#369&#34;&gt;$card&lt;/span&gt;)) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;echo&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;✔ the &lt;/span&gt;&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;{&lt;/span&gt;&lt;span style=&#34;color:#369&#34;&gt;$card&lt;/span&gt;&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt; credit card is valid&lt;/span&gt;&lt;span style=&#34;color:#04d;background-color:#fff0f0&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    } &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;else&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;echo&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;✕ the &lt;/span&gt;&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;{&lt;/span&gt;&lt;span style=&#34;color:#369&#34;&gt;$card&lt;/span&gt;&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt; credit card is not valid&lt;/span&gt;&lt;span style=&#34;color:#04d;background-color:#fff0f0&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;echo&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#04d;background-color:#fff0f0&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&lt;/span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Demonstrating the breadth of functionality that we can take advantage of by using the Zend Validator component is obviously well beyond the scope of this post. However, in the example above, we can see what including such a component in our scripts could look like. We put together a simple credit card and email validator that works on incoming arrays of data. All we had to do was run &lt;code&gt;composer require zendframework/zend-validator&lt;/code&gt; to install our package and put this line at the beginning of our script &lt;code&gt;include &#39;vendor/autoload.php&#39;;&lt;/code&gt;. We didn’t even have to generate the autoloader like we did with our custom &lt;code&gt;ArithmeticCalculator&lt;/code&gt; class. This is because Composer knows how to deal with dependencies installed with &lt;code&gt;composer require&lt;/code&gt; without us needing to tell it so.&lt;/p&gt;
&lt;p&gt;And that concludes today’s post about how to use Zend Framework components in a console app in PHP. As a bonus, we also learned about code reuse with &lt;code&gt;require&lt;/code&gt; and &lt;code&gt;include&lt;/code&gt;, what namespaces are, how to use them, what they can and cannot do, what is Composer, how to set up a project using it and how to use it for autoloading modules. That’s a pretty good bang for your buck.&lt;/p&gt;
&lt;p&gt;You can find all the source code that we wrote for this post in &lt;a href=&#34;https://github.com/megakevin/end-point-blog-zf-console-app&#34;&gt;this GitHub repo&lt;/a&gt;.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Start basic application with Vue.js 2 and Drupal 8</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2018/04/start-basic-application-with-vue-drupal/"/>
      <id>https://www.endpointdev.com/blog/2018/04/start-basic-application-with-vue-drupal/</id>
      <published>2018-04-05T00:00:00+00:00</published>
      <author>
        <name>Piotr Hankiewicz</name>
      </author>
      <content type="html">
        &lt;img src=&#34;/blog/2018/04/start-basic-application-with-vue-drupal/vue-and-drupal.jpg&#34; width=&#34;1200&#34; alt=&#34;Vue.js 2 and Drupal 8&#34; /&gt;
&lt;h3 id=&#34;introduction&#34;&gt;Introduction&lt;/h3&gt;
&lt;p&gt;The purpose of creating this post is to show how fast can you build web applications with Vue.js on the front-end and Drupal on the back-end side.&lt;/p&gt;
&lt;p&gt;Let’s call our project “Awesome Nerds”.&lt;/p&gt;
&lt;h3 id=&#34;what-do-we-need&#34;&gt;What do we need?&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Debian/Ubuntu system&lt;/li&gt;
&lt;li&gt;Internet&lt;/li&gt;
&lt;li&gt;30 minutes&lt;/li&gt;
&lt;li&gt;Vagrant &amp;amp; VirtualBox&lt;/li&gt;
&lt;li&gt;Git&lt;/li&gt;
&lt;li&gt;Vim&lt;/li&gt;
&lt;li&gt;Yarn&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;step-by-step&#34;&gt;Step by step&lt;/h3&gt;
&lt;p&gt;Here’s what we’re going to do:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Install Vagrant, VirtualBox, and Git&lt;/li&gt;
&lt;li&gt;Setup a new Drupal 8 project that will be our back-end project&lt;/li&gt;
&lt;li&gt;Setup a new Vue.js project that will be our front-end application&lt;/li&gt;
&lt;li&gt;Let’s code&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;install-vagrant-virtualbox-and-git&#34;&gt;Install Vagrant, VirtualBox, and Git&lt;/h3&gt;
&lt;p&gt;Open your console and run:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;$ sudo apt-get install software-properties-common&lt;/code&gt; - getting some common libraries&lt;/p&gt;
&lt;p&gt;&lt;code&gt;$ sudo apt-add-repository ppa:ansible/ansible&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;$ sudo apt-get update&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;$ sudo apt-get install ansible&lt;/code&gt; - installing Ansible&lt;/p&gt;
&lt;p&gt;&lt;code&gt;$ wget ‘https://releases.hashicorp.com/vagrant/2.0.2/vagrant_2.0.2_x86_64.deb’ &amp;amp;&amp;amp; dpkg -i vagrant_2.0.2_x86_64.deb&lt;/code&gt; - installing Vagrant&lt;/p&gt;
&lt;p&gt;&lt;code&gt;$ sudo apt-get install dkms&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;$ deb https://download.virtualbox.org/virtualbox/debian &amp;lt;mydist&amp;gt; contrib&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;$ wget -q https://www.virtualbox.org/download/oracle_vbox_2016.asc -O- | sudo apt-key add -&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;$ wget -q https://www.virtualbox.org/download/oracle_vbox.asc -O- | sudo apt-key add -&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;$ sudo apt-get update&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;$ sudo apt-get install virtualbox-5.2 git vim nfs-kernel-server&lt;/code&gt; - installing VirtualBox&lt;/p&gt;
&lt;p&gt;&lt;code&gt;$ vagrant plugin install vagrant-vbguest&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;$ curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;$ echo &amp;quot;deb https://dl.yarnpkg.com/debian/ stable main&amp;quot; | sudo tee /etc/apt/sources.list.d/yarn.list&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;$ sudo apt-get update &amp;amp;&amp;amp; sudo apt-get install yarn&lt;/code&gt; - installing Yarn&lt;/p&gt;
&lt;p&gt;Now we can continue to the back-end project installation.&lt;/p&gt;
&lt;h3 id=&#34;install-and-setup-back-end-project&#34;&gt;Install and setup back-end project&lt;/h3&gt;
&lt;p&gt;Create a new folder for the project:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;$ mkdir awesome_nerds &amp;amp;&amp;amp; cd awesome_nerds&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;$ mkdir frontend backend&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;We need to clone the DrupalVM repository. DrupalVM is a Drupal setup that helps to encapsulate services with Vagrant. Run:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;$ cd backend&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;$ git clone git@github.com:geerlingguy/drupal-vm.git .&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Let’s name our project:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;$ vim default.config.yml&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Look and set these two settings so they look like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;vagrant_hostname: awesomenerds.backend                                   
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;vagrant_machine_name: awesomenerds_backend&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Quit Vim and run:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;$ vagrant up&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;It will take a while to set up everything, you can get a coffee or browse some memes or go to the next chapter and start creating our front-end project.&lt;/p&gt;
&lt;p&gt;When it’s ready we will have a running Drupal 8 setup with MySQL, PHP 7, and Apache (you can configure this stack in &lt;code&gt;default.config.yml&lt;/code&gt; if you prefer nginx for example).&lt;/p&gt;
&lt;p&gt;Drupal project files are in the &lt;code&gt;drupal&lt;/code&gt; directory and that’s the only folder that you would want to add to a project Git repository.&lt;/p&gt;
&lt;h3 id=&#34;setup-new-vuejs-project&#34;&gt;Setup new Vue.js project&lt;/h3&gt;
&lt;p&gt;We will use a minimal project skeleton from &lt;a href=&#34;https://github.com/vuejs-templates/webpack&#34;&gt;https://github.com/vuejs-templates/webpack&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Run:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;$ cd awesome_nerds/frontend&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;$ git clone https://github.com/vuejs-templates/webpack .&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;$ yarn install -g vue-cli&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;$ vue init webpack awesome_nerds&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Name the project “awesome_nerds” (yes!) and just hit enter to install with defaults.&lt;/p&gt;
&lt;p&gt;When you run:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;$ yarn run dev&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;you will get a fresh Vue.js application running on http://localhost:8080.&lt;/p&gt;
&lt;h3 id=&#34;lets-code&#34;&gt;Let’s code!&lt;/h3&gt;
&lt;p&gt;Now we are ready for development. It can be really rapid, both Vue.js 2 and Drupal 8 are impressively good and it’s just a matter of finding a good idea for your new start-up.&lt;/p&gt;
&lt;p&gt;In my next post I will continue and code a simple social application using the REST API of Drupal and our Vue.js front-end.&lt;/p&gt;
&lt;p&gt;Thank you and good luck!&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Regular Expression Inconsistencies With Unicode</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2018/01/regular-expression-inconsistencies-with-unicode/"/>
      <id>https://www.endpointdev.com/blog/2018/01/regular-expression-inconsistencies-with-unicode/</id>
      <published>2018-01-23T00:00:00+00:00</published>
      <author>
        <name>Phineas Jensen</name>
      </author>
      <content type="html">
        &lt;p&gt;&lt;img src=&#34;/blog/2018/01/regular-expression-inconsistencies-with-unicode/mud-run.jpg&#34; alt=&#34;A mud run&#34;&gt;&lt;br/&gt;
&lt;small&gt;A casual stroll through the world of Unicode and regular expressions—​&lt;a href=&#34;https://www.flickr.com/photos/presidioofmonterey/7025086135&#34;&gt;Photo&lt;/a&gt; by Presidio of Monterey&lt;/small&gt;&lt;/p&gt;
&lt;p&gt;Character classes in regular expressions are an extremely useful and widespread feature, but there are some relatively recent changes that you might not know of.&lt;/p&gt;
&lt;p&gt;The issue stems from how different programming languages, locales, and character encodings treat predefined character classes. Take, for example, the expression &lt;code&gt;\w&lt;/code&gt; which was introduced in Perl around the year 1990 (along with &lt;code&gt;\d&lt;/code&gt; and &lt;code&gt;\s&lt;/code&gt; and their inverted sets &lt;code&gt;\W&lt;/code&gt;, &lt;code&gt;\D&lt;/code&gt;, and &lt;code&gt;\S&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;\w&lt;/code&gt; shorthand is a character class that matches “word characters” as the C language understands them: &lt;code&gt;[a-zA-Z0-9_]&lt;/code&gt;. At least when ASCII was the main player in the character encoding scene that simple fact was true. With the standardization of Unicode and UTF-8, the meaning of &lt;code&gt;\w&lt;/code&gt; has become a more foggy.&lt;/p&gt;
&lt;h4 id=&#34;perl&#34;&gt;Perl&lt;/h4&gt;
&lt;p&gt;Take this example in a recent Perl version:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-perl&#34; data-lang=&#34;perl&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;use&lt;/span&gt; &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;5.012&lt;/span&gt;; &lt;span style=&#34;color:#888&#34;&gt;# use 5.012 or higher includes Unicode support&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;use&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;utf8&lt;/span&gt;;  &lt;span style=&#34;color:#888&#34;&gt;# necessary for Unicode string literals&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;print&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;username&amp;#34;&lt;/span&gt; =~&lt;span style=&#34;color:#080;background-color:#fff0ff&#34;&gt; /^\w+$/&lt;/span&gt;; &lt;span style=&#34;color:#888&#34;&gt;# 1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;print&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;userاسم&amp;#34;&lt;/span&gt;  =~&lt;span style=&#34;color:#080;background-color:#fff0ff&#34;&gt; /^\w+$/&lt;/span&gt;; &lt;span style=&#34;color:#888&#34;&gt;# 1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Perl is treating &lt;code&gt;\w&lt;/code&gt; differently here because the characters “اسم” (“ism” meaning “name” in Arabic) definitely don’t fall within &lt;code&gt;[a-zA-Z0-9_]&lt;/code&gt;!&lt;/p&gt;
&lt;p&gt;Beginning with Perl 5.12 from the year 2010, character classes are handled differently. Documentation on the topic is found in &lt;a href=&#34;https://perldoc.perl.org/perlrecharclass.html#Backslash-sequences&#34;&gt;perlrecharclass&lt;/a&gt;. The rules aren’t as simple as with some languages, but can be generalized as such:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;\w&lt;/code&gt; will match Unicode characters with the “Word” property (equivalent to &lt;code&gt;\p{Word}&lt;/code&gt;), unless the &lt;code&gt;/a&lt;/code&gt; (ASCII) flag is enabled, in which case it will be equivalent to the original &lt;code&gt;[a-zA-Z0-9_]&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Let’s see the &lt;code&gt;/a&lt;/code&gt; flag in action.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-perl&#34; data-lang=&#34;perl&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;use&lt;/span&gt; &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;5.012&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;use&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;utf8&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;print&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;username&amp;#34;&lt;/span&gt; =~&lt;span style=&#34;color:#080;background-color:#fff0ff&#34;&gt; /^\w+$/&lt;/span&gt;a; &lt;span style=&#34;color:#888&#34;&gt;# 1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;print&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;userاسم&amp;#34;&lt;/span&gt;  =~&lt;span style=&#34;color:#080;background-color:#fff0ff&#34;&gt; /^\w+$/&lt;/span&gt;a; &lt;span style=&#34;color:#888&#34;&gt;# 0&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;However, you should know that for code points below 256, these rules can change depending on whether Unicode or locale rules are on, so if you’re unsure, consult the &lt;a href=&#34;https://perldoc.perl.org/perlre.html&#34;&gt;perlre&lt;/a&gt; and &lt;a href=&#34;https://perldoc.perl.org/perlrecharclass.html&#34;&gt;perlrecharclass&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Keep in mind that these same questions of what the character classes include can apply to every predefined character class in whatever language you’re using, so remember to check language-specific implementations for other character class shorthands, such as &lt;code&gt;\s&lt;/code&gt; and &lt;code&gt;\d&lt;/code&gt;, not just &lt;code&gt;\w&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Every language seems to do regular expressions a little bit differently, so here’s a short, incomplete guide for several other languages we use frequently.&lt;/p&gt;
&lt;h4 id=&#34;python&#34;&gt;Python&lt;/h4&gt;
&lt;p&gt;Take this example in Python 3.6.2:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;gt;&amp;gt;&amp;gt; re.&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;match&lt;/span&gt;(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;r&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;^\w+$&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;username&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;_sre.SRE_Match &lt;span style=&#34;color:#038&#34;&gt;object&lt;/span&gt;; span=(&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;8&lt;/span&gt;), &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;match&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;username&amp;#39;&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;gt;&amp;gt;&amp;gt; re.&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;match&lt;/span&gt;(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;r&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;^\w+$&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;userاسم&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;_sre.SRE_Match &lt;span style=&#34;color:#038&#34;&gt;object&lt;/span&gt;; span=(&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;7&lt;/span&gt;), &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;match&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;userاسم&amp;#39;&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Python is also treating &lt;code&gt;\w&lt;/code&gt; differently here. Let’s take a look at &lt;a href=&#34;https://docs.python.org/3/library/re.html#regular-expression-syntax&#34;&gt;the Python docs&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;\w&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;For Unicode (str) patterns:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Matches Unicode word characters; this includes most characters that can be part of a word in any language, as well as numbers and the underscore. If the ASCII flag is used, only [a-zA-Z0-9_] is matched (but the flag affects the entire regular expression, so in such cases using an explicit [a-zA-Z0-9_] may be a better choice).
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For 8-bit (bytes) patterns:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Matches characters considered alphanumeric in the ASCII character set; this is equivalent to [a-zA-Z0-9_]. If the LOCALE flag is used, matches characters considered alphanumeric in the current locale and the underscore.
&lt;/code&gt;&lt;/pre&gt;&lt;/blockquote&gt;
&lt;p&gt;So &lt;code&gt;\w&lt;/code&gt; includes “most characters that can be part of a word in any language, as well as numbers and the underscore”. A list of the characters that includes is difficult to pin down, so it would be best to use the &lt;code&gt;re.ASCII&lt;/code&gt; flag as suggested when you’re unsure if you want letters from other languages matched:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;gt;&amp;gt;&amp;gt; re.&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;match&lt;/span&gt;(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;r&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;^\w+$&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;userاسم&amp;#39;&lt;/span&gt;,  flags=re.ASCII)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;gt;&amp;gt;&amp;gt; re.&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;match&lt;/span&gt;(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;r&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;^\w+$&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;username&amp;#39;&lt;/span&gt;, flags=re.ASCII)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;_sre.SRE_Match &lt;span style=&#34;color:#038&#34;&gt;object&lt;/span&gt;; span=(&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;8&lt;/span&gt;), &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;match&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;username&amp;#39;&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h4 id=&#34;ruby&#34;&gt;Ruby&lt;/h4&gt;
&lt;p&gt;Ruby’s &lt;a href=&#34;https://ruby-doc.org/core-2.5.0/Regexp.html#class-Regexp-label-Character+Classes&#34;&gt;Regexp class&lt;/a&gt; documentation gives a simple and useful explanation: backslash character classes (e.g. &lt;code&gt;\w&lt;/code&gt;, &lt;code&gt;\s&lt;/code&gt;, &lt;code&gt;\d&lt;/code&gt;) are ASCII-only, while POSIX-style bracket expressions (e.g. &lt;code&gt;[[:alnum:]]&lt;/code&gt;) include other Unicode characters.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-ruby&#34; data-lang=&#34;ruby&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;irb(main):&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;001&lt;/span&gt;:&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0&lt;/span&gt;&amp;gt; &lt;span style=&#34;color:#080;background-color:#fff0ff&#34;&gt;/^\w+$/&lt;/span&gt;         =~ &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;userاسم&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;=&amp;gt; &lt;span style=&#34;color:#080&#34;&gt;nil&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;irb(main):&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;002&lt;/span&gt;:&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0&lt;/span&gt;&amp;gt; &lt;span style=&#34;color:#080;background-color:#fff0ff&#34;&gt;/^[[:word:]]+$/&lt;/span&gt; =~ &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;userاسم&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;=&amp;gt; &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h4 id=&#34;javascript&#34;&gt;JavaScript&lt;/h4&gt;
&lt;p&gt;JavaScript doesn’t support POSIX-style bracket expressions, and its backslash character classes are simple, straightforward lists of ASCII characters. The &lt;a href=&#34;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#Using_special_characters&#34;&gt;MDN&lt;/a&gt; has simple explanations for each one.&lt;/p&gt;
&lt;p&gt;JavaScript regular expressions do accept a &lt;code&gt;/u&lt;/code&gt; flag, but it does not affect shorthand character classes. Consider these examples in Node.js:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;gt; &lt;span style=&#34;color:#080;background-color:#fff0ff&#34;&gt;/^\w+$/&lt;/span&gt;.test(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;username&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;gt; &lt;span style=&#34;color:#080;background-color:#fff0ff&#34;&gt;/^\w+$/&lt;/span&gt;.test(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;userﺎﺴﻣ&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;gt; &lt;span style=&#34;color:#080;background-color:#fff0ff&#34;&gt;/^\w+$/u&lt;/span&gt;.test(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;username&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;gt; &lt;span style=&#34;color:#080;background-color:#fff0ff&#34;&gt;/^\w+$/u&lt;/span&gt;.test(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;userﺎﺴﻣ&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;false&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We can see that the &lt;code&gt;/u&lt;/code&gt; flag has no effect on what &lt;code&gt;\w&lt;/code&gt; matches. Now let’s look at Unicode character lengths in JavaScript:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;❤&amp;#39;&lt;/span&gt;.length
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;👩&amp;#39;&lt;/span&gt;.length
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;🀄️&amp;#39;&lt;/span&gt;.length
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;3&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Because of the way Unicode is implemented in JavaScript, strings with Unicode characters outside the BMP (Basic Multilingual Plane) will appear to be longer than they are.&lt;/p&gt;
&lt;p&gt;This can be accounted for in regular expressions with the &lt;code&gt;/u&lt;/code&gt; flag, which only corrects character parsing, and does not affect shorthand character classes:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;let&lt;/span&gt; mystr = &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;hi👩there&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;undefined&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;gt; mystr.length
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;9&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;gt; &lt;span style=&#34;color:#080;background-color:#fff0ff&#34;&gt;/hi.there/&lt;/span&gt;.test(mystr);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;gt; &lt;span style=&#34;color:#080;background-color:#fff0ff&#34;&gt;/hi..there/&lt;/span&gt;.test(mystr);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;gt; &lt;span style=&#34;color:#080;background-color:#fff0ff&#34;&gt;/hi.there/u&lt;/span&gt;.test(mystr);  &lt;span style=&#34;color:#a61717;background-color:#e3d2d2&#34;&gt;#&lt;/span&gt; note the /u from here on
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;gt; &lt;span style=&#34;color:#080;background-color:#fff0ff&#34;&gt;/hi..there/u&lt;/span&gt;.test(mystr);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;gt; &lt;span style=&#34;color:#080;background-color:#fff0ff&#34;&gt;/hi..there/u&lt;/span&gt;.test(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;hi👩👩there&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;true&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The excellent article &lt;a href=&#34;http://blog.jonnew.com/posts/poo-dot-length-equals-two&#34;&gt;&amp;quot;💩&amp;quot;.length === 2&lt;/a&gt; by Jonathan New goes into detail about the why this is, and explores various solutions. It also addresses some legacy inconsistencies, like how the old HEAVY BLACK HEART character and other older Unicode symbols might be represented differently.&lt;/p&gt;
&lt;h4 id=&#34;php&#34;&gt;PHP&lt;/h4&gt;
&lt;p&gt;PHP’s documentation explains that &lt;code&gt;\w&lt;/code&gt; matches letters, digits, and the underscore as defined by your locale. It’s not totally clear about how Unicode is treated, but it uses the PCRE (Perl Compatible Regular Expressions) library which supports a &lt;code&gt;/u&lt;/code&gt; flag that can be used to enable Unicode matching in character classes:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;?php
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;echo&lt;/span&gt; preg_match(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;/^&lt;/span&gt;&lt;span style=&#34;color:#04d;background-color:#fff0f0&#34;&gt;\\&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;w+$/&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;username&amp;#34;&lt;/span&gt;), &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#04d;background-color:#fff0f0&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&lt;/span&gt;;  &lt;span style=&#34;color:#888&#34;&gt;# 1
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;echo&lt;/span&gt; preg_match(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;/^&lt;/span&gt;&lt;span style=&#34;color:#04d;background-color:#fff0f0&#34;&gt;\\&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;w+$/&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;userاسم&amp;#34;&lt;/span&gt;),  &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#04d;background-color:#fff0f0&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&lt;/span&gt;;  &lt;span style=&#34;color:#888&#34;&gt;# 0
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;echo&lt;/span&gt; preg_match(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;/^&lt;/span&gt;&lt;span style=&#34;color:#04d;background-color:#fff0f0&#34;&gt;\\&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;w+$/u&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;username&amp;#34;&lt;/span&gt;), &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#04d;background-color:#fff0f0&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&lt;/span&gt;; &lt;span style=&#34;color:#888&#34;&gt;# 1
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;echo&lt;/span&gt; preg_match(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;/^&lt;/span&gt;&lt;span style=&#34;color:#04d;background-color:#fff0f0&#34;&gt;\\&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;w+$/u&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;userاسم&amp;#34;&lt;/span&gt;),  &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#04d;background-color:#fff0f0&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&lt;/span&gt;; &lt;span style=&#34;color:#888&#34;&gt;# 1
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h4 id=&#34;net&#34;&gt;.NET&lt;/h4&gt;
&lt;p&gt;The &lt;a href=&#34;https://docs.microsoft.com/en-us/dotnet/standard/base-types/character-classes-in-regular-expressions&#34;&gt;.NET Quick Reference&lt;/a&gt; has a comprehensive guide to character classes. For word characters, it defines a specific group of Unicode categories including letters, modifiers, and connectors from many languages, but also points out that setting the &lt;a href=&#34;https://docs.microsoft.com/en-us/dotnet/standard/base-types/regular-expression-options#ECMAScript&#34;&gt;ECMAScript Matching Behavior&lt;/a&gt; option will limit &lt;code&gt;\w&lt;/code&gt; to &lt;code&gt;[a-zA-Z_0-9]&lt;/code&gt;, among other things. Microsoft’s documentation is clear and comprehensive with great examples, so I recommend referring to it frequently.&lt;/p&gt;
&lt;h4 id=&#34;go&#34;&gt;Go&lt;/h4&gt;
&lt;p&gt;Go follows the regular expression syntax used by &lt;a href=&#34;https://github.com/google/re2/wiki/Syntax&#34;&gt;Google’s RE2 engine&lt;/a&gt;, which has easy syntax for specifying whether you want Unicode characters to be captured or not:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;package&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;main&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;import&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;(&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;	&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;fmt&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;	&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;regexp&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;)&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;func&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;main&lt;/span&gt;()&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;{&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;	&lt;/span&gt;&lt;span style=&#34;color:#888&#34;&gt;// Perl-style&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;	&lt;/span&gt;fmt.&lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;Println&lt;/span&gt;(regexp.&lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;MatchString&lt;/span&gt;(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;`^\w+$`&lt;/span&gt;,&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;username&amp;#34;&lt;/span&gt;))&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#888&#34;&gt;// true&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;	&lt;/span&gt;fmt.&lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;Println&lt;/span&gt;(regexp.&lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;MatchString&lt;/span&gt;(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;`^\w+$`&lt;/span&gt;,&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;userاسم&amp;#34;&lt;/span&gt;))&lt;span style=&#34;color:#bbb&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#888&#34;&gt;// false&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;	&lt;/span&gt;&lt;span style=&#34;color:#888&#34;&gt;// POSIX-style&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;	&lt;/span&gt;fmt.&lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;Println&lt;/span&gt;(regexp.&lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;MatchString&lt;/span&gt;(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;`^[[:word:]]+$`&lt;/span&gt;,&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;username&amp;#34;&lt;/span&gt;))&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#888&#34;&gt;// true&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;	&lt;/span&gt;fmt.&lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;Println&lt;/span&gt;(regexp.&lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;MatchString&lt;/span&gt;(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;`^[[:word:]]+$`&lt;/span&gt;,&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;userاسم&amp;#34;&lt;/span&gt;))&lt;span style=&#34;color:#bbb&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#888&#34;&gt;// false&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;	&lt;/span&gt;&lt;span style=&#34;color:#888&#34;&gt;// Unicode character class&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;	&lt;/span&gt;fmt.&lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;Println&lt;/span&gt;(regexp.&lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;MatchString&lt;/span&gt;(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;`^\pL+$`&lt;/span&gt;,&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;username&amp;#34;&lt;/span&gt;))&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#888&#34;&gt;// true&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;	&lt;/span&gt;fmt.&lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;Println&lt;/span&gt;(regexp.&lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;MatchString&lt;/span&gt;(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;`^\pL+$`&lt;/span&gt;,&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;userاسم&amp;#34;&lt;/span&gt;))&lt;span style=&#34;color:#bbb&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#888&#34;&gt;// true&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;&lt;/span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You can see this code in action &lt;a href=&#34;https://play.golang.org/p/Y0HEhWXgXYa&#34;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;h4 id=&#34;grep&#34;&gt;grep&lt;/h4&gt;
&lt;p&gt;Implementations of grep vary widely across platforms and versions. On my personal computer with GNU grep 3.1, &lt;code&gt;\w&lt;/code&gt; doesn&amp;rsquo;t work at all with default settings, matches only ASCII characters with the &lt;code&gt;-P&lt;/code&gt; (PCRE) option, and matches Unicode characters with &lt;code&gt;-E&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[phin@caballero ~]$ grep    &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;^\w+&lt;/span&gt;$&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&lt;/span&gt; &amp;lt;(&lt;span style=&#34;color:#038&#34;&gt;echo&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;username&amp;#34;&lt;/span&gt;)  &lt;span style=&#34;color:#888&#34;&gt;# no match&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[phin@caballero ~]$ grep -P &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;^\w+&lt;/span&gt;$&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&lt;/span&gt; &amp;lt;(&lt;span style=&#34;color:#038&#34;&gt;echo&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;username&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;username
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[phin@caballero ~]$ grep -P &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;^\w+&lt;/span&gt;$&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&lt;/span&gt; &amp;lt;(&lt;span style=&#34;color:#038&#34;&gt;echo&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;userاسم&amp;#34;&lt;/span&gt;)   &lt;span style=&#34;color:#888&#34;&gt;# no match&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[phin@caballero ~]$ grep -E &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;^\w+&lt;/span&gt;$&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&lt;/span&gt; &amp;lt;(&lt;span style=&#34;color:#038&#34;&gt;echo&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;username&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;username
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[phin@caballero ~]$ grep -E &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;^\w+&lt;/span&gt;$&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&lt;/span&gt; &amp;lt;(&lt;span style=&#34;color:#038&#34;&gt;echo&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;userاسم&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;userاسم&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Again, implementations vary a lot, so double check on your system before doing anything important.&lt;/p&gt;
&lt;h3 id=&#34;other-links&#34;&gt;Other links&lt;/h3&gt;
&lt;p&gt;As great as Unicode and regular expressions are, their implementations vary widely across various languages and tools, and that introduces far more unexpected behavior than I can write about in this post. Whenever you&amp;rsquo;re going to use something with Unicode and regular expressions, make sure to check language specifications to make sure everything will work as expected.&lt;/p&gt;
&lt;p&gt;Of course, this topic has already been discussed and written about at great length. Here are some links worth checking out:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://www.joelonsoftware.com/2003/10/08/the-absolute-minimum-every-software-developer-absolutely-positively-must-know-about-unicode-and-character-sets-no-excuses/&#34;&gt;The Absolute Minimum Every Software Developer Absolutely, Positively Must Know About Unicode and Character Sets (No Excuses!)&lt;/a&gt; - This is an oft-referenced article by Joel Spolsky. It was written in 2003 but the wealth of valuable information within is still very relevant and it helps greatly in going from Unicode noob to having a comfortable, useful knowledge of many common issues.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://mathiasbynens.be/notes/es-regexp-proposals&#34;&gt;ECMAScript regular expressions are getting better!&lt;/a&gt; - This article by a V8 developer at Google shows some nice JavaScript regular expression improvements planned for ES2018, including Unicode property escapes.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/LuminosoInsight/python-ftfy&#34;&gt;ftfy for Python&lt;/a&gt; - ftfy is a Python library that takes corrupt Unicode text and attempts to fix it as best it can. I haven’t yet had a chance to use it, but the examples are compelling and it’s definitely worth knowing about.&lt;/li&gt;
&lt;/ul&gt;

      </content>
    </entry>
  
    <entry>
      <title>Drupal — rapid development</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2017/05/drupal-rapid-development/"/>
      <id>https://www.endpointdev.com/blog/2017/05/drupal-rapid-development/</id>
      <published>2017-05-26T00:00:00+00:00</published>
      <author>
        <name>Piotr Hankiewicz</name>
      </author>
      <content type="html">
        &lt;div class=&#34;separator&#34; style=&#34;clear: both; text-align: center;&#34;&gt;&lt;a href=&#34;/blog/2017/05/drupal-rapid-development/image-0.gif&#34; imageanchor=&#34;1&#34; style=&#34;margin-left: 1em; margin-right: 1em;&#34;&gt;&lt;img border=&#34;0&#34; data-original-height=&#34;279&#34; data-original-width=&#34;640&#34; src=&#34;/blog/2017/05/drupal-rapid-development/image-0.gif&#34;/&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Here at End Point, we had the pleasure to be a part of multiple &lt;strong&gt;Drupal&lt;/strong&gt; &lt;strong&gt;6&lt;/strong&gt;, &lt;strong&gt;7&lt;/strong&gt; and &lt;strong&gt;8&lt;/strong&gt; projects. Most of our clients wanted to use the latest Drupal version, to have a long term support, stable platform.&lt;/p&gt;
&lt;p&gt;A few years ago, I already had big experience with PHP itself and other, various PHP frameworks like WordPress, Joomla! or TYPO3. I was happy to use all of them, but then one of our clients asked us for a simple Drupal 6 task. That’s how I started my Drupal journey which continues until now.&lt;/p&gt;
&lt;p&gt;To be honest, I had a difficult start, it was different, new and pretty inscrutable for me. After a few days of reading documentation and playing with the system I was ready to do some simple work. Here, I wanted to share my thoughts about Drupal and tell you why &lt;strong&gt;I LOVE!&lt;/strong&gt; it.&lt;/p&gt;
&lt;h3 id=&#34;low-learning-curve&#34;&gt;Low learning curve&lt;/h3&gt;
&lt;p&gt;It took, of course, a few months until I was ready to build something more complex, but it really takes a few days only to be ready for simple development. It’s not only about Drupal, but also PHP, it’s much cheaper to maintain and extend a project. Maybe it’s not so important with smaller projects, but definitely important for massive code bases. Programmers can jump in and start being &lt;strong&gt;productive&lt;/strong&gt; really quick.&lt;/p&gt;
&lt;h3 id=&#34;great-documentation&#34;&gt;Great documentation&lt;/h3&gt;
&lt;p&gt;Drupal documentation is well structured and constantly developed, usually you can find what you need within a few minutes. It’s critical and must have for any other framework and not so common unfortunately.&lt;/p&gt;
&lt;h3 id=&#34;big-community&#34;&gt;Big community&lt;/h3&gt;
&lt;p&gt;The Drupal community is one of the biggest IT communities I have ever encountered. They extend, fix and document the Drupal core regularly. Most of them have their other jobs and work on this project just for fun and with passion.&lt;/p&gt;
&lt;h3 id=&#34;its-free&#34;&gt;It’s free&lt;/h3&gt;
&lt;p&gt;It’s an open source project, that’s one of the biggest pros here. You can get it for free, you can get support &lt;strong&gt;for free&lt;/strong&gt;, you can join the community for free too (:)).&lt;/p&gt;
&lt;div class=&#34;separator&#34; style=&#34;clear: both; text-align: center;&#34;&gt;&lt;a href=&#34;/blog/2017/05/drupal-rapid-development/image-1-big.png&#34; imageanchor=&#34;1&#34; style=&#34;clear: right; float: right; margin-bottom: 1em; margin-left: 1em;&#34;&gt;&lt;img border=&#34;0&#34; data-original-height=&#34;318&#34; data-original-width=&#34;318&#34; height=&#34;200&#34; src=&#34;/blog/2017/05/drupal-rapid-development/image-1.png&#34; width=&#34;200&#34;/&gt;&lt;/a&gt;&lt;/div&gt;
&lt;h3 id=&#34;modules&#34;&gt;Modules&lt;/h3&gt;
&lt;p&gt;On the official Drupal website you can find tons of free plugins/modules. It’s a time and money saver, you don’t need to reinvent the wheel for every new widget on your website and focus on fireworks.&lt;/p&gt;
&lt;p&gt;Usually you can just go there and find a proper component. E-commerce shop? Slideshow? Online classifieds website? No problem! It’s all there.&lt;/p&gt;
&lt;h3 id=&#34;php7-support&#34;&gt;PHP7 support&lt;/h3&gt;
&lt;p&gt;I can often hear from other developers that PHP is slow, well, it’s not the Road Runner, but come on, unless you are Facebook (and I think that they, correct me if I’m wrong, still use PHP :)) it’s just OK to use PHP.&lt;/p&gt;
&lt;p&gt;Drupal fully supports PHP7.&lt;/p&gt;
&lt;p&gt;With PHP7 it’s much faster, better and safer. To learn more: &lt;a href=&#34;https://pages.zend.com/rs/zendtechnologies/images/PHP7-Performance%20Infographic.pdf&#34;&gt;https://pages.zend.com/rs/zendtechnologies/images/PHP7-Performance%20Infographic.pdf&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In the infographic you can see that &lt;strong&gt;PHP7&lt;/strong&gt; is much faster than &lt;strong&gt;Ruby&lt;/strong&gt;, &lt;strong&gt;Perl&lt;/strong&gt; and &lt;strong&gt;Python&lt;/strong&gt; when you try to render a Mandelbrot fractal. In general, you definitely can’t say that PHP is slow, same as Drupal.&lt;/p&gt;
&lt;h3 id=&#34;rest-api-support&#34;&gt;REST API support&lt;/h3&gt;
&lt;p&gt;Drupal has the built in, ready to use API system. In a few moments you can spawn a new API endpoint for you application. You don’t need to implement a whole API by yourself, I did it a few times in multiple languages, believe me, it’s problematic.&lt;/p&gt;
&lt;h3 id=&#34;perfect-for-a-backend-system&#34;&gt;Perfect for a backend system&lt;/h3&gt;
&lt;p&gt;Drupal is a perfect candidate for a backend system. Let’s imagine that you want to build a beautiful, mobile application. You want to let editors, other people to edit content. You want to grab this content through the API. It’s easy as pie with Drupal.&lt;/p&gt;
&lt;p&gt;Drupal’s web interface is stable and easy to use.&lt;/p&gt;
&lt;h3 id=&#34;power-of-taxonomies&#34;&gt;Power of taxonomies&lt;/h3&gt;
&lt;p&gt;Taxonomies are, really basically, just dictionaries. The best thing about taxonomies is that you don’t need to touch code to play with them.&lt;/p&gt;
&lt;p&gt;Let’s say that on your website you want to create a list of states in the USA. Using most of the frameworks you need to ask your developer/technical person to do so. With taxonomies you just need a few clicks and that’s it, you can put in on your website. That’s sweet, not only for non technical person, but for us, developers as well. Again, you can focus on actually making the website attractive, rather than spending time on things that can be automated.&lt;/p&gt;
&lt;h3 id=&#34;summary&#34;&gt;Summary&lt;/h3&gt;
&lt;p&gt;Of course, Drupal is not perfect, but it’s undeniably a great tool. Mobile application, single page application, corporate website—​there are no limits for this content management system. And actually, it is, in my opinion, the best tool to manage your content and it does not mean that you need to use Drupal to present it. You can create a &lt;strong&gt;mobile&lt;/strong&gt;, &lt;strong&gt;ReactJS&lt;/strong&gt;, &lt;strong&gt;AngularJS&lt;/strong&gt;, &lt;strong&gt;VueJS&lt;/strong&gt; application and combine it with Drupal easily.&lt;/p&gt;
&lt;p&gt;I hope that you’ve had a good reading and wish to hear back from you! Thanks.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Making cross-blogs queries in multi-site WordPress performant</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2016/11/making-cross-blogs-queries-in-multi/"/>
      <id>https://www.endpointdev.com/blog/2016/11/making-cross-blogs-queries-in-multi/</id>
      <published>2016-11-02T00:00:00+00:00</published>
      <author>
        <name>Kamil Ciemniewski</name>
      </author>
      <content type="html">
        &lt;p&gt;Some time ago I was working on customizing a WordPress system for a client. The system was running in a multi-site mode, being a host of a large number of blogs.&lt;/p&gt;
&lt;p&gt;Because some blogs had not been updated in a long while, we wanted to pull information about recent posts from all of the blogs. This in turn was going to be used for pruning any blogs that weren’t considered “active”.&lt;/p&gt;
&lt;p&gt;While the above description may sound simple, the scale of the system made the task a bit more involving that it would be usually.&lt;/p&gt;
&lt;h3 id=&#34;how-wordpress-handles-the-multi-site-scenario&#34;&gt;How WordPress handles the “multi-site” scenario&lt;/h3&gt;
&lt;p&gt;The goal of computing the summary of posts for many blogs residing in the hypotethical blogging platform, &lt;strong&gt;in the same database&lt;/strong&gt; doesn’t seem so complicated. Tasks like that are being performed all the time using relational databases.&lt;/p&gt;
&lt;p&gt;The problem in WordPress arises though because of the very &lt;strong&gt;unusual&lt;/strong&gt; way that it organises blogs data. Let’s see how the database tables look like in the &amp;ldquo;normal&amp;rdquo; mode first:&lt;/p&gt;
&lt;div class=&#34;separator&#34; style=&#34;clear: both; text-align: center;&#34;&gt;&lt;a href=&#34;/blog/2016/11/making-cross-blogs-queries-in-multi/image-0.png&#34; imageanchor=&#34;1&#34; style=&#34;margin-left: 1em; margin-right: 1em;&#34;&gt;&lt;img border=&#34;0&#34; src=&#34;/blog/2016/11/making-cross-blogs-queries-in-multi/image-0.png&#34;/&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;It has a number of tables that start with user configurable prefix. In the case of the screenshot above, the prefix was wp_.&lt;/p&gt;
&lt;p&gt;We can see there’s a wp_posts table which contains rows related to blog posts. Thinking about the multi-blog setup, one would expect some kind of a blog_id column in the wp_posts column. Selecting data for the given blog would still be a cinch. It would also be very performant after adding an index on that column.&lt;/p&gt;
&lt;p&gt;Instead, this is what we’re getting when setting up WordPress in a multi-site mode:&lt;/p&gt;
&lt;div class=&#34;separator&#34; style=&#34;clear: both; text-align: center;&#34;&gt;&lt;a href=&#34;/blog/2016/11/making-cross-blogs-queries-in-multi/image-1.png&#34; imageanchor=&#34;1&#34; style=&#34;margin-left: 1em; margin-right: 1em;&#34;&gt;&lt;img border=&#34;0&#34; src=&#34;/blog/2016/11/making-cross-blogs-queries-in-multi/image-1.png&#34;/&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;WordPress just creates a new set of tables we seen before appending the index of the blog to the tables prefix! Instead of having a nice, clean and easy to use wp_posts with the blog_id column, we get a number of tables—​one for each blog: wp_1_posts, wp_2_posts etc. Why does it matter that much? Just try to get the counts of posts in each blog in one query—​it’s impossible with such a tables setup. Getting such info involves querying &lt;strong&gt;each table separately&lt;/strong&gt;. This means that with each new blog within the system, the cost of running such sequence of queries adds up dramatically. This is also known as a N+1 problem—​bad WordPress! bad!&lt;/p&gt;
&lt;h3 id=&#34;the-approach-around-it&#34;&gt;The approach around it&lt;/h3&gt;
&lt;p&gt;The counts of posts for each blog was needed to be computed very quickly in my case. The system consisted of hundreds of different mini-blogs and the stats were to be shown in the admin dashboard. Obviously making admins wait for the page to load for a long time wasn’t an option.&lt;/p&gt;
&lt;p&gt;I went the route of creating an additional database table, holding the info about the number of posts for each blog. This table was then being updated upon each post creation, update and deletion. It was also updated upon the blog removal.&lt;/p&gt;
&lt;p&gt;WordPress has a helper function for ensuring the table is created in the database. You feed it the DDL containing the definition of the table and it makes sure it is present in the database.&lt;/p&gt;
&lt;p&gt;I created a function that was being fired on each plugin class instantiation, while making that class a singleton. Here’s the code that makes the plugin class a singleton:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;BlogsPruner&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;private&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;static&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$instance&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;private&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;__construct&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#369&#34;&gt;$this&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;ensureDatabaseSetup&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#369&#34;&gt;$this&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;ensureBlogStatsGetUpdated&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;// ... other initialization here
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;static&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;singleton&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;if&lt;/span&gt;(!isset(self::&lt;span style=&#34;color:#369&#34;&gt;$instance&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#369&#34;&gt;$_class&lt;/span&gt; = &lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;__CLASS__&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      self::&lt;span style=&#34;color:#369&#34;&gt;$instance&lt;/span&gt; = &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;new&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$_class&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;return&lt;/span&gt; self::&lt;span style=&#34;color:#369&#34;&gt;$instance&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;__clone&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    trigger_error(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;Cannot clone the pruner plugin&amp;#39;&lt;/span&gt;, E_USER_ERROR);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#888&#34;&gt;// ... rest of the class
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;BlogsPruner::&lt;span style=&#34;color:#369&#34;&gt;singleton&lt;/span&gt;();&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The next step was to implement the function ensuring there’s a stats table in the database:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;ensureDatabaseSetup&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;global&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$wpdb&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#369&#34;&gt;$tableName&lt;/span&gt; = &lt;span style=&#34;color:#369&#34;&gt;$this&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;blogStatsTableName&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#369&#34;&gt;$charsetCollate&lt;/span&gt; = &lt;span style=&#34;color:#369&#34;&gt;$wpdb&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;get_charset_collate&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#369&#34;&gt;$sql&lt;/span&gt; = &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;CREATE TABLE &lt;/span&gt;&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;$tableName&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt; (
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;            blog_id bigint(20),
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;            count_posts int(2),
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;            UNIQUE KEY blog_id (blog_id)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;    ) &lt;/span&gt;&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;$charsetCollate&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;;&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;require_once&lt;/span&gt;( ABSPATH . &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;wp-admin/includes/upgrade.php&amp;#39;&lt;/span&gt; );
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    dbDelta( &lt;span style=&#34;color:#369&#34;&gt;$sql&lt;/span&gt; );
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#369&#34;&gt;$this&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;ensureBlogStatsUpdated&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This code uses a helper function to correctly construct the name for the table:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;blogStatsTableName&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;global&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$wpdb&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$wpdb&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;base_prefix&lt;/span&gt; . &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;blog_stats&amp;#39;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This made sure the table was using the correct prefix, just like all the other tables in the database.&lt;/p&gt;
&lt;p&gt;Now, I needed to ensure the stats were updated upon each post change:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;ensureBlogStatsGetUpdated&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  add_action(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;save_post&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;array&lt;/span&gt;(&lt;span style=&#34;color:#369&#34;&gt;$this&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;onPostUpdated&amp;#39;&lt;/span&gt;));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  add_action(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;delete_post&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;array&lt;/span&gt;(&lt;span style=&#34;color:#369&#34;&gt;$this&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;onPostDeleted&amp;#39;&lt;/span&gt;));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;onPostUpdated&lt;/span&gt;(&lt;span style=&#34;color:#369&#34;&gt;$postId&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;global&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$blog_id&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#369&#34;&gt;$post&lt;/span&gt; = get_post(&lt;span style=&#34;color:#369&#34;&gt;$postId&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;if&lt;/span&gt;(wp_is_post_revision(&lt;span style=&#34;color:#369&#34;&gt;$postId&lt;/span&gt;) || &lt;span style=&#34;color:#369&#34;&gt;$post&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;post_status&lt;/span&gt; == &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;auto-draft&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#369&#34;&gt;$this&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;updateBlogStats&lt;/span&gt;(&lt;span style=&#34;color:#369&#34;&gt;$blog_id&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;onPostDeleted&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;global&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$blog_id&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#369&#34;&gt;$this&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;updateBlogStats&lt;/span&gt;(&lt;span style=&#34;color:#369&#34;&gt;$blog_id&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;updateBlogStats&lt;/span&gt;(&lt;span style=&#34;color:#369&#34;&gt;$blogId&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#369&#34;&gt;$count&lt;/span&gt; = &lt;span style=&#34;color:#369&#34;&gt;$this&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;getBlogUserCreatedPostsCount&lt;/span&gt;(&lt;span style=&#34;color:#369&#34;&gt;$blogId&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#369&#34;&gt;$this&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;updateBlogPostsCount&lt;/span&gt;(&lt;span style=&#34;color:#369&#34;&gt;$blogId&lt;/span&gt;, &lt;span style=&#34;color:#369&#34;&gt;$count&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;// Here we&amp;#39;re specifically not including the post that is auto-created
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;// upon the blog creation:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;getBlogUserCreatedPostsCount&lt;/span&gt;(&lt;span style=&#34;color:#369&#34;&gt;$blogId&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;global&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$wpdb&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#369&#34;&gt;$sql&lt;/span&gt; = &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;SELECT
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;            COUNT(DISTINCT `wp_&amp;#34;&lt;/span&gt; . &lt;span style=&#34;color:#369&#34;&gt;$blogId&lt;/span&gt; . &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;_posts`.`id`) AS count_user_posts
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;          FROM `wp_blogs`
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;          INNER JOIN `wp_&amp;#34;&lt;/span&gt; . &lt;span style=&#34;color:#369&#34;&gt;$blogId&lt;/span&gt; . &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;_posts`
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;                  ON `wp_blogs`.`blog_id` = &lt;/span&gt;&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;$blogId&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;          WHERE
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;            `wp_&amp;#34;&lt;/span&gt; . &lt;span style=&#34;color:#369&#34;&gt;$blogId&lt;/span&gt; . &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;_posts`.`post_type` = &amp;#39;post&amp;#39; AND
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;            `wp_&amp;#34;&lt;/span&gt; . &lt;span style=&#34;color:#369&#34;&gt;$blogId&lt;/span&gt; . &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;_posts`.`post_status` = &amp;#39;publish&amp;#39; AND
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;            TIMESTAMPDIFF(SECOND, `wp_&amp;#34;&lt;/span&gt; . &lt;span style=&#34;color:#369&#34;&gt;$blogId&lt;/span&gt; . &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;_posts`.`post_date`, `wp_blogs`.`last_updated`) &amp;gt; 60&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#369&#34;&gt;$row&lt;/span&gt; = &lt;span style=&#34;color:#369&#34;&gt;$wpdb&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;get_row&lt;/span&gt;(&lt;span style=&#34;color:#369&#34;&gt;$sql&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;return&lt;/span&gt; intval(&lt;span style=&#34;color:#369&#34;&gt;$row&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;count_user_posts&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;updateBlogPostsCount&lt;/span&gt;(&lt;span style=&#34;color:#369&#34;&gt;$blogId&lt;/span&gt;, &lt;span style=&#34;color:#369&#34;&gt;$count&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;global&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$wpdb&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#369&#34;&gt;$data&lt;/span&gt; = &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;array&lt;/span&gt;(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;count_posts&amp;#39;&lt;/span&gt; =&amp;gt; &lt;span style=&#34;color:#369&#34;&gt;$count&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#369&#34;&gt;$where&lt;/span&gt; = &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;array&lt;/span&gt;(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;blog_id&amp;#39;&lt;/span&gt; =&amp;gt; &lt;span style=&#34;color:#369&#34;&gt;$blogId&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#369&#34;&gt;$wpdb&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;update&lt;/span&gt;(&lt;span style=&#34;color:#369&#34;&gt;$this&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;blogStatsTableName&lt;/span&gt;(), &lt;span style=&#34;color:#369&#34;&gt;$data&lt;/span&gt;, &lt;span style=&#34;color:#369&#34;&gt;$where&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The actual production plugin implemented many more features than this sample code demonstrates. It was listing the blogs that could be considered stale, automatically pruning them after specified in the admin screen time and allowing admins to configure it via the WordPress interface. The full set of features is beyond the scope of this post.&lt;/p&gt;
&lt;p&gt;The overall result made getting the statistics about very large set of blogs very fast. The cost of querying for the number of posts of each blog was moved to incremental, small updates upon each post being created, modified or removed. For the end user, this cost was imperceptable.&lt;/p&gt;
&lt;h3 id=&#34;final-thoughts&#34;&gt;Final thoughts&lt;/h3&gt;
&lt;p&gt;WordPress is loved by many users. If you’re not just a user but also working with the code, there’s a number of traps you may fall into though. If I were to employ techniques that get advised as “WordPress usual/default/preferred”—​I’d end up with a very unhappy client who’s be owning a very broken WordPress system. Fortunately, the set of WordPress tables isn’t casted in stone and you can freely extend it—​as long as you’re cautious and know what you’re doing. Provided that these two prerequisites are met—​WordPress is just a database backed platform—​like any other.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Create categories in Virtuemart 3 with Joomla 2.5 and 3.5 programmatically</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2016/08/create-categories-in-virtuemart-3-with/"/>
      <id>https://www.endpointdev.com/blog/2016/08/create-categories-in-virtuemart-3-with/</id>
      <published>2016-08-18T00:00:00+00:00</published>
      <author>
        <name>Piotr Hankiewicz</name>
      </author>
      <content type="html">
        &lt;h3 id=&#34;introduction&#34;&gt;Introduction&lt;/h3&gt;
&lt;p&gt;&lt;em&gt;Code that I’m going to show you is ready to download here: &lt;a href=&#34;https://github.com/peter-hank/com_morevirtuemart&#34;&gt;https://github.com/peter-hank/com_morevirtuemart&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://virtuemart.net/&#34;&gt;Virtuemart&lt;/a&gt; is an open-source e-commerce application written in PHP. It’s pretty popular with a 4% share of the whole e-commerce market (&lt;a href=&#34;https://blog.aheadworks.com/2015/05/ecommerce-platforms-popularity-may-2015-two-platforms-take-half/&#34;&gt;https://blog.aheadworks.com/2015/05/ecommerce-platforms-popularity-may-2015-two-platforms-take-half/&lt;/a&gt;). Today I will show you how to extend it and use its functionality from an external &lt;a href=&#34;https://www.joomla.org&#34;&gt;Joomla&lt;/a&gt; (free and open-source content management system) component.&lt;/p&gt;
&lt;h3 id=&#34;creating-a-component&#34;&gt;Creating a component&lt;/h3&gt;
&lt;p&gt;We are going to create a new component to show the code in a nice and clear form. I’m using a component generator from here: &lt;a href=&#34;https://www.component-creator.com&#34;&gt;https://www.component-creator.com&lt;/a&gt;. We don’t need any tables, models and views for the purpose of this blog post. Plain and simple component is what we need. After creating the component download it and install in a Joomla administration interface.&lt;/p&gt;
&lt;h3 id=&#34;component-overview&#34;&gt;Component overview&lt;/h3&gt;
&lt;p&gt;The component structure looks like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;# tree components/com_morevirtuemart&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;components/com_morevirtuemart
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;├── controller.php
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;├── controllers
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;│   └── index.html
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;├── helpers
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;│   ├── index.html
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;│   └── morevirtuemart.php
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;├── index.html
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;├── models
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;│   ├── fields
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;│   │   ├── createdby.php
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;│   │   ├── filemultiple.php
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;│   │   ├── foreignkey.php
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;│   │   ├── index.html
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;│   │   ├── modifiedby.php
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;│   │   ├── submit.php
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;│   │   ├── timecreated.php
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;│   │   └── timeupdated.php
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;│   ├── forms
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;│   │   └── index.html
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;│   └── index.html
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;├── morevirtuemart.php
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;├── router.php
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;└── views
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    └── index.html&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We don’t need to use more than a generic controller in a main directory. Its content is not very exciting for now:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;?php
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;// No direct access
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;defined(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;_JEXEC&amp;#39;&lt;/span&gt;) &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;or&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;die&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;jimport(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;joomla.application.component.controller&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;/**
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt; * Class MorevirtuemartController
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt; *
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt; * @since  1.6
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt; */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;MorevirtuemartController&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;extends&lt;/span&gt; JControllerLegacy
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;/**
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;  * Method to display a view.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;  *
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;  * @param   boolean $cachable  If true, the view output will be cached
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;  * @param   mixed   $urlparams An array of safe url parameters and their variable types, for valid values see {@link JFilterInput::clean()}.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;  *
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;  * @return  JController   This object to support chaining.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;  *
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;  * @since    1.5
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;  */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;display&lt;/span&gt;(&lt;span style=&#34;color:#369&#34;&gt;$cachable&lt;/span&gt; = &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;false&lt;/span&gt;, &lt;span style=&#34;color:#369&#34;&gt;$urlparams&lt;/span&gt; = &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;false&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#369&#34;&gt;$view&lt;/span&gt; = JFactory::&lt;span style=&#34;color:#369&#34;&gt;getApplication&lt;/span&gt;()-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;input&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;getCmd&lt;/span&gt;(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;view&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  JFactory::&lt;span style=&#34;color:#369&#34;&gt;getApplication&lt;/span&gt;()-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;input&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;set&lt;/span&gt;(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;view&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#369&#34;&gt;$view&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;parent&lt;/span&gt;::&lt;span style=&#34;color:#369&#34;&gt;display&lt;/span&gt;(&lt;span style=&#34;color:#369&#34;&gt;$cachable&lt;/span&gt;, &lt;span style=&#34;color:#369&#34;&gt;$urlparams&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$this&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We can remove a display function and replace it with a function called &lt;strong&gt;createCategory&lt;/strong&gt; so a MorevirtuemartController class will look like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;MorevirtuemartController&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;extends&lt;/span&gt; JControllerLegacy
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;createCategory&lt;/span&gt; ()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;echo&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;I\&amp;#39;m alive!&amp;#39;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;die&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;To test that our component is fully working right now try to open Joomla with this url: &lt;strong&gt;index.php?option=com_morevirtuemart&amp;amp;task=createCategory&lt;/strong&gt;. Of course you need to prepend it with your domain name to make it work. The result should be just an empty page with our text: “I’m alive!”&lt;/p&gt;
&lt;p&gt;Now you know how to call a controller task within a browser.&lt;/p&gt;
&lt;h3 id=&#34;creating-a-category&#34;&gt;Creating a category&lt;/h3&gt;
&lt;p&gt;To use Virtuemart classes we need to initialize all of its logic and configuration. Look here:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;?php
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;// No direct access
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;defined(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;_JEXEC&amp;#39;&lt;/span&gt;) &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;or&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;die&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;jimport(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;joomla.application.component.controller&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;// loading Virtuemart classes and configuration
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;if&lt;/span&gt; (!class_exists( &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;VmConfig&amp;#39;&lt;/span&gt; )) &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;require&lt;/span&gt;(JPATH_ADMINISTRATOR . DS . &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;components&amp;#39;&lt;/span&gt; . DS . &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;com_virtuemart&amp;#39;&lt;/span&gt;.DS.&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;helpers&amp;#39;&lt;/span&gt;.DS.&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;config.php&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#369&#34;&gt;$config&lt;/span&gt;= VmConfig::&lt;span style=&#34;color:#369&#34;&gt;loadConfig&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;if&lt;/span&gt; (!class_exists( &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;VirtueMartModelVendor&amp;#39;&lt;/span&gt; )) &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;require&lt;/span&gt;(JPATH_VM_ADMINISTRATOR.DS.&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;models&amp;#39;&lt;/span&gt;.DS.&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;vendor.php&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;if&lt;/span&gt;(!class_exists(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;TableMedias&amp;#39;&lt;/span&gt;)) &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;require&lt;/span&gt;(JPATH_VM_ADMINISTRATOR.DS.&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;tables&amp;#39;&lt;/span&gt;.DS.&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;medias.php&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;if&lt;/span&gt;(!class_exists(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;TableCategories&amp;#39;&lt;/span&gt;)) &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;require&lt;/span&gt;(JPATH_VM_ADMINISTRATOR.DS.&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;tables&amp;#39;&lt;/span&gt;.DS.&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;categories.php&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;if&lt;/span&gt; (!class_exists( &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;VirtueMartModelCategory&amp;#39;&lt;/span&gt; )) &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;require&lt;/span&gt;(JPATH_VM_ADMINISTRATOR.DS.&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;models&amp;#39;&lt;/span&gt;.DS.&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;category.php&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;/**
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt; * Class MorevirtuemartController
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt; *
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt; * @since  1.6
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt; */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;MorevirtuemartController&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;extends&lt;/span&gt; JControllerLegacy
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;createCategory&lt;/span&gt; ()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;echo&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;I\&amp;#39;m alive!&amp;#39;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;die&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;What we are doing here is importing needed Virtuemart classes and initializing the configuration of each. We are all set now and ready for an implementation.&lt;/p&gt;
&lt;p&gt;The most difficult thing here is how to work with a Virtuemart internal authorization system. Before an every CRUD action there is a check for an authorization access. We need to find a workaround for this, there are multiple way of doing this, I will show you how I did that. Create a new file in &lt;strong&gt;components/com_morevirtuemart/model and call it category.php.&lt;/strong&gt; Inside this file put this piece of code:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;?php
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;// No direct access
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;defined(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;_JEXEC&amp;#39;&lt;/span&gt;) &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;or&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;die&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;VirtueMartModelCategoryLocal&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;extends&lt;/span&gt; VirtueMartModelCategory {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;__construct&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;parent&lt;/span&gt;::&lt;span style=&#34;color:#369&#34;&gt;__construct&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;I hope it’s pretty clear what we are doing here: extending a Virtuemart category model. What I’m going to do now is to copy a store function from a VirtueMartModelCategory class and put it in our local class of a category model and remove an authentication check from it (you need to secure it by yourself but using a different method). The result of this action is:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;?php
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;// No direct access
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;defined(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;_JEXEC&amp;#39;&lt;/span&gt;) &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;or&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;die&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;VirtueMartModelCategoryLocal&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;extends&lt;/span&gt; VirtueMartModelCategory {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;__construct&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;parent&lt;/span&gt;::&lt;span style=&#34;color:#369&#34;&gt;__construct&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;store&lt;/span&gt;(&amp;amp;&lt;span style=&#34;color:#369&#34;&gt;$data&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#369&#34;&gt;$table&lt;/span&gt; = &lt;span style=&#34;color:#369&#34;&gt;$this&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;getTable&lt;/span&gt;(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;categories&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;if&lt;/span&gt; ( !array_key_exists (&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;category_template&amp;#39;&lt;/span&gt; , &lt;span style=&#34;color:#369&#34;&gt;$data&lt;/span&gt; ) ){
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#369&#34;&gt;$data&lt;/span&gt;[&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;category_template&amp;#39;&lt;/span&gt;] = &lt;span style=&#34;color:#369&#34;&gt;$data&lt;/span&gt;[&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;category_layout&amp;#39;&lt;/span&gt;] = &lt;span style=&#34;color:#369&#34;&gt;$data&lt;/span&gt;[&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;category_product_layout&amp;#39;&lt;/span&gt;] = &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0&lt;/span&gt; ;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;if&lt;/span&gt;(VmConfig::&lt;span style=&#34;color:#369&#34;&gt;get&lt;/span&gt;(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;categorytemplate&amp;#39;&lt;/span&gt;) == &lt;span style=&#34;color:#369&#34;&gt;$data&lt;/span&gt;[&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;category_template&amp;#39;&lt;/span&gt;] ){
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#369&#34;&gt;$data&lt;/span&gt;[&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;category_template&amp;#39;&lt;/span&gt;] = &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;if&lt;/span&gt;(VmConfig::&lt;span style=&#34;color:#369&#34;&gt;get&lt;/span&gt;(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;categorylayout&amp;#39;&lt;/span&gt;) == &lt;span style=&#34;color:#369&#34;&gt;$data&lt;/span&gt;[&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;category_layout&amp;#39;&lt;/span&gt;]){
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#369&#34;&gt;$data&lt;/span&gt;[&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;category_layout&amp;#39;&lt;/span&gt;] = &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;if&lt;/span&gt;(VmConfig::&lt;span style=&#34;color:#369&#34;&gt;get&lt;/span&gt;(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;productlayout&amp;#39;&lt;/span&gt;) == &lt;span style=&#34;color:#369&#34;&gt;$data&lt;/span&gt;[&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;category_product_layout&amp;#39;&lt;/span&gt;]){
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#369&#34;&gt;$data&lt;/span&gt;[&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;category_product_layout&amp;#39;&lt;/span&gt;] = &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#369&#34;&gt;$table&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;bindChecknStore&lt;/span&gt;(&lt;span style=&#34;color:#369&#34;&gt;$data&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;if&lt;/span&gt;(!&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;empty&lt;/span&gt;(&lt;span style=&#34;color:#369&#34;&gt;$data&lt;/span&gt;[&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;virtuemart_category_id&amp;#39;&lt;/span&gt;])){
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#369&#34;&gt;$xdata&lt;/span&gt;[&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;category_child_id&amp;#39;&lt;/span&gt;] = (int)&lt;span style=&#34;color:#369&#34;&gt;$data&lt;/span&gt;[&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;virtuemart_category_id&amp;#39;&lt;/span&gt;];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#369&#34;&gt;$xdata&lt;/span&gt;[&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;category_parent_id&amp;#39;&lt;/span&gt;] = &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;empty&lt;/span&gt;(&lt;span style=&#34;color:#369&#34;&gt;$data&lt;/span&gt;[&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;category_parent_id&amp;#39;&lt;/span&gt;])? &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0&lt;/span&gt;:(int)&lt;span style=&#34;color:#369&#34;&gt;$data&lt;/span&gt;[&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;category_parent_id&amp;#39;&lt;/span&gt;];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#369&#34;&gt;$xdata&lt;/span&gt;[&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;ordering&amp;#39;&lt;/span&gt;] = &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;empty&lt;/span&gt;(&lt;span style=&#34;color:#369&#34;&gt;$data&lt;/span&gt;[&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;ordering&amp;#39;&lt;/span&gt;])? &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0&lt;/span&gt;: (int)&lt;span style=&#34;color:#369&#34;&gt;$data&lt;/span&gt;[&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;ordering&amp;#39;&lt;/span&gt;];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#369&#34;&gt;$table&lt;/span&gt; = &lt;span style=&#34;color:#369&#34;&gt;$this&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;getTable&lt;/span&gt;(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;category_categories&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#369&#34;&gt;$table&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;bindChecknStore&lt;/span&gt;(&lt;span style=&#34;color:#369&#34;&gt;$xdata&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#369&#34;&gt;$this&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;clearCategoryRelatedCaches&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$data&lt;/span&gt;[&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;virtuemart_category_id&amp;#39;&lt;/span&gt;] ;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In addition, I removed call to media creation. If you need to create media along with the categories you need to extend it as well, the same as we did with a category model. We are all set now to create a new category from a component controller, we just need to include an extended model there and call a &lt;strong&gt;store&lt;/strong&gt; function. Our controller should look like this now:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;!--?php
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;// No direct access
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;defined(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;_JEXEC&amp;#39;&lt;/span&gt;) &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;or&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;die&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;jimport(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;joomla.application.component.controller&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;// loading Virtuemart classes and configuration
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;defined(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;VMPATH_ROOT&amp;#39;&lt;/span&gt;) &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;or&lt;/span&gt; define(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;VMPATH_ROOT&amp;#39;&lt;/span&gt;, JPATH_ROOT);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;defined(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;VMPATH_ADMIN&amp;#39;&lt;/span&gt;) &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;or&lt;/span&gt; define(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;VMPATH_ADMIN&amp;#39;&lt;/span&gt;, VMPATH_ROOT.DS.&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;administrator&amp;#39;&lt;/span&gt; . DS . &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;components&amp;#39;&lt;/span&gt; . DS . &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;com_virtuemart&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;if&lt;/span&gt; (!class_exists( &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;VmConfig&amp;#39;&lt;/span&gt; )) &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;require&lt;/span&gt;(JPATH_ROOT.DS.&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;administrator&amp;#39;&lt;/span&gt; . DS . &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;components&amp;#39;&lt;/span&gt; . DS . &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;com_virtuemart&amp;#39;&lt;/span&gt; . DS . &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;helpers&amp;#39;&lt;/span&gt; . DS . &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;config.php&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;VmConfig::&lt;span style=&#34;color:#369&#34;&gt;loadConfig&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;if&lt;/span&gt; (!class_exists( &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;VmController&amp;#39;&lt;/span&gt; )) &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;require&lt;/span&gt;(VMPATH_ADMIN.DS.&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;helpers&amp;#39;&lt;/span&gt; . DS . &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;vmcontroller.php&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;if&lt;/span&gt; (!class_exists( &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;VmModel&amp;#39;&lt;/span&gt; )) &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;require&lt;/span&gt;(VMPATH_ADMIN.DS.&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;helpers&amp;#39;&lt;/span&gt; . DS . &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;vmmodel.php&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;require&lt;/span&gt;(JPATH_ROOT . DS . &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;administrator&amp;#39;&lt;/span&gt; . DS . &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;components&amp;#39;&lt;/span&gt; . DS . &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;com_virtuemart&amp;#39;&lt;/span&gt; . DS . &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;models&amp;#39;&lt;/span&gt; . DS . &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;category.php&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;require&lt;/span&gt;(JPATH_ROOT . DS . &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;administrator&amp;#39;&lt;/span&gt; . DS . &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;components&amp;#39;&lt;/span&gt; . DS . &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;com_virtuemart&amp;#39;&lt;/span&gt; . DS . &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;models&amp;#39;&lt;/span&gt; . DS . &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;product.php&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;require&lt;/span&gt;(JPATH_ROOT . DS . &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;administrator&amp;#39;&lt;/span&gt; . DS . &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;components&amp;#39;&lt;/span&gt; . DS . &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;com_virtuemart&amp;#39;&lt;/span&gt; . DS . &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;models&amp;#39;&lt;/span&gt; . DS . &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;media.php&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;// loading an exteneded category model
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;require&lt;/span&gt;(&lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;__DIR__&lt;/span&gt; . DS . &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;models&amp;#39;&lt;/span&gt; . DS . &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;category.php&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;/**
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt; * Class MorevirtuemartController
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt; *
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt; * @since  1.6
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt; */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;MorevirtuemartController&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;extends&lt;/span&gt; JControllerLegacy
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;__construct&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;parent&lt;/span&gt;::&lt;span style=&#34;color:#369&#34;&gt;__construct&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#369&#34;&gt;$this&lt;/span&gt;---&amp;gt;categoryModel = &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;new&lt;/span&gt; VirtueMartModelCategoryLocal();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;createCategory&lt;/span&gt; ()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#369&#34;&gt;$catData&lt;/span&gt; = [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;category_name&amp;#39;&lt;/span&gt; =&amp;gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;Brand new Virtuemart category&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;category_parent_id&amp;#39;&lt;/span&gt; =&amp;gt; &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;published&amp;#39;&lt;/span&gt; =&amp;gt; &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   ];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#369&#34;&gt;$catId&lt;/span&gt; = &lt;span style=&#34;color:#369&#34;&gt;$this&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;categoryModel&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;store&lt;/span&gt;(&lt;span style=&#34;color:#369&#34;&gt;$catData&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;echo&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;Created a new category&amp;#39;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;die&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Wow! The new category should be there. There are many more available attributes to be set, we’ve used a needed minimum: category name, its parent and status.&lt;/p&gt;
&lt;h3 id=&#34;the-end&#34;&gt;The end&lt;/h3&gt;
&lt;p&gt;There are a few things I should mention:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ensure to add an authentication to your code, you can use a Joomla authentication system,&lt;/li&gt;
&lt;li&gt;be aware that Virtuemart code can change in time so be careful with updates,&lt;/li&gt;
&lt;li&gt;I recommend to use a CLI script for this kind of operations.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Thanks for reading.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Scrape web content with PHP (no API? no problem)</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2016/07/scrape-web-content-with-php-no-api-no/"/>
      <id>https://www.endpointdev.com/blog/2016/07/scrape-web-content-with-php-no-api-no/</id>
      <published>2016-07-07T00:00:00+00:00</published>
      <author>
        <name>Piotr Hankiewicz</name>
      </author>
      <content type="html">
        &lt;h3 id=&#34;introduction&#34;&gt;Introduction&lt;/h3&gt;
&lt;p&gt;There is a lot of data flowing everywhere. Not structured, not useful pieces of data moving here and there. Getting this data and structuring, processing can make it really expensive. There are companies making billions of dollars just (huh?) for scraping web content and showing in a nice form.&lt;/p&gt;
&lt;p&gt;Another reason for doing such things can be for example, lack of an API from a source website. In this case, it’s the only way to get data that you need to process.&lt;/p&gt;
&lt;p&gt;Today I will show you how to get web data using PHP and that it can be as easy as pie.&lt;/p&gt;
&lt;h3 id=&#34;just-do-it&#34;&gt;Just do it&lt;/h3&gt;
&lt;p&gt;There are multiple scraping scripts ready to use. I can recommend one of them: PHP Simple HTML DOM Parser. It’s extremely easy to start with and initial cost is almost nothing, it’s open sourced also.&lt;/p&gt;
&lt;p&gt;First, download a library from an official site: &lt;a href=&#34;https://sourceforge.net/project/showfiles.php?group_id=218559&#34;&gt;https://sourceforge.net/project/showfiles.php?group_id=218559&lt;/a&gt;. You can use a composer version too, it’s here: &lt;a href=&#34;https://github.com/sunra/php-simple-html-dom-parser&#34;&gt;https://github.com/sunra/php-simple-html-dom-parser&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Let’s say that you have downloaded this file already. It’s just a one PHP file called simple_html_dom.php. Create a new PHP file called scraper.php and include mentioned library like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;?php
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;require&lt;/span&gt;(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;simple_html_dom.php&amp;#39;&lt;/span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In our example, we will scrape top 10 trending YouTube videos and create a nice array of links and names out of it. We will use this link: &lt;a href=&#34;https://www.youtube.com/feed/trending?gl=GB&#34;&gt;https://www.youtube.com/feed/trending?gl=GB&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;We need to grab this page first. Using PHP it’s just a one additional line in our script:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;?php
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;require&lt;/span&gt;(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;simple_html_dom.php&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;// Create DOM from URL or file
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#369&#34;&gt;$html&lt;/span&gt; = file_get_html(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;https://www.youtube.com/feed/trending?gl=GB&amp;#39;&lt;/span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;A PHP object was just created with the YouTube page structure.&lt;/p&gt;
&lt;p&gt;Look at the YouTube page structure to find a repeating structure for a list of videos. It’s best to use Chrome developer tools and its HTML browser. At the time of writing this post (it can change in the future of course) it’s:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-html&#34; data-lang=&#34;html&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;ul&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;class&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;expanded-shelf-content-list has-multiple-items&amp;#34;&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &amp;lt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;li&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;class&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;expanded-shelf-content-item-wrapper&amp;#34;&lt;/span&gt;&amp;gt;...&amp;lt;/&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;li&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &amp;lt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;li&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;class&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;expanded-shelf-content-item-wrapper&amp;#34;&lt;/span&gt;&amp;gt;...&amp;lt;/&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;li&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &amp;lt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;li&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;class&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;expanded-shelf-content-item-wrapper&amp;#34;&lt;/span&gt;&amp;gt;...&amp;lt;/&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;li&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; ...
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;/&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;ul&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Thanks Google! This time it will be easy. Sometimes a structure of the page lacks of classes and ids and it’s more difficult to select exactly what we need.&lt;/p&gt;
&lt;p&gt;Now, for each item of &lt;strong&gt;expanded-shelf-content-item-wrapper&lt;/strong&gt; we need to find its title and url. Using developer tools again, it’s easy to achieve:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-html&#34; data-lang=&#34;html&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;a&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#369&#34;&gt;class&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;yt-uix-sessionlink yt-uix-tile-link yt-ui-ellipsis yt-ui-ellipsis-2 spf-link &amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#369&#34;&gt;dir&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;ltr&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#369&#34;&gt;aria-describedby&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;description-id-284683&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#369&#34;&gt;title&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;KeemStar Swatted My Friend.&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#369&#34;&gt;href&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;/watch?v=oChvoP8zEBw&amp;#34;&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; KeemStar Swatted My Friend
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;/&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;a&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Jackpot! We have both things that we need in the same HTML tag. Now, let’s grab this data:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;?php
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;require&lt;/span&gt;(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;simple_html_dom.php&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;// Create DOM from URL or file
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#369&#34;&gt;$html&lt;/span&gt; = file_get_html(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;https://www.youtube.com/feed/trending&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;// creating an array of elements
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#369&#34;&gt;$videos&lt;/span&gt; = [];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;// Find top ten videos
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#369&#34;&gt;$i&lt;/span&gt; = &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;foreach&lt;/span&gt; (&lt;span style=&#34;color:#369&#34;&gt;$html&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;find&lt;/span&gt;(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;li.expanded-shelf-content-item-wrapper&amp;#39;&lt;/span&gt;) &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;as&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$video&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#369&#34;&gt;$i&lt;/span&gt; &amp;gt; &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;10&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;break&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#888&#34;&gt;// Find item link element
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;        &lt;span style=&#34;color:#369&#34;&gt;$videoDetails&lt;/span&gt; = &lt;span style=&#34;color:#369&#34;&gt;$video&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;find&lt;/span&gt;(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;a.yt-uix-tile-link&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#888&#34;&gt;// get title attribute
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;        &lt;span style=&#34;color:#369&#34;&gt;$videoTitle&lt;/span&gt; = &lt;span style=&#34;color:#369&#34;&gt;$videoDetails&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;title&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#888&#34;&gt;// get href attribute
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;        &lt;span style=&#34;color:#369&#34;&gt;$videoUrl&lt;/span&gt; = &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;https://youtube.com&amp;#39;&lt;/span&gt; . &lt;span style=&#34;color:#369&#34;&gt;$videoDetails&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;href&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#888&#34;&gt;// push to a list of videos
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;        &lt;span style=&#34;color:#369&#34;&gt;$videos&lt;/span&gt;[] = [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;title&amp;#39;&lt;/span&gt; =&amp;gt; &lt;span style=&#34;color:#369&#34;&gt;$videoTitle&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;url&amp;#39;&lt;/span&gt; =&amp;gt; &lt;span style=&#34;color:#369&#34;&gt;$videoUrl&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        ];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#369&#34;&gt;$i&lt;/span&gt;++;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;var_dump(&lt;span style=&#34;color:#369&#34;&gt;$videos&lt;/span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Look, it’s simple as using CSS. What we just did? First, we extracted all videos and started looping through them here:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;foreach&lt;/span&gt; (&lt;span style=&#34;color:#369&#34;&gt;$html&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;find&lt;/span&gt;(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;li.expanded-shelf-content-item-wrapper&amp;#39;&lt;/span&gt;) &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;as&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$video&lt;/span&gt;) {&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Then, just extracted a title and url per each video item here:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;// Find item link element
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#369&#34;&gt;$videoDetails&lt;/span&gt; = &lt;span style=&#34;color:#369&#34;&gt;$video&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;find&lt;/span&gt;(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;a.yt-uix-tile-link&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;// get title attribute
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#369&#34;&gt;$videoTitle&lt;/span&gt; = &lt;span style=&#34;color:#369&#34;&gt;$videoDetails&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;title&lt;/span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;At the end, we just push an array object with scraped data to the array and dump it. The result looks like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;array&lt;/span&gt;(&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;10&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  [&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0&lt;/span&gt;]=&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;array&lt;/span&gt;(&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;2&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    [&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;title&amp;#34;&lt;/span&gt;]=&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    string(&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;90&lt;/span&gt;) &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Enzo Amore &amp;amp; Big Cass help John Cena even the odds against The Club: Raw, July 4, 2016&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    [&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;url&amp;#34;&lt;/span&gt;]=&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    string(&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;39&lt;/span&gt;) &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;https://youtube.com/watch?v=940-maoRY3c&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  [&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1&lt;/span&gt;]=&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;array&lt;/span&gt;(&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;2&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    [&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;title&amp;#34;&lt;/span&gt;]=&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    string(&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;77&lt;/span&gt;) &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Loose Women Reveal Sex Toys Confessions In Hilarious Discussion | Loose Women&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    [&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;url&amp;#34;&lt;/span&gt;]=&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    string(&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;39&lt;/span&gt;) &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;https://youtube.com/watch?v=Xxzy_bZwNcI&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  [&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;2&lt;/span&gt;]=&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;array&lt;/span&gt;(&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;2&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    [&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;title&amp;#34;&lt;/span&gt;]=&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    string(&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;51&lt;/span&gt;) &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Tinie Tempah - Mamacita ft. Wizkid (Official Video)&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    [&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;url&amp;#34;&lt;/span&gt;]=&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    string(&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;39&lt;/span&gt;) &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;https://youtube.com/watch?v=J4GQxzUdZNo&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  [&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;3&lt;/span&gt;]=&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;array&lt;/span&gt;(&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;2&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    [&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;title&amp;#34;&lt;/span&gt;]=&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    string(&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;54&lt;/span&gt;) &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Michael Gove&amp;#39;s Shows you What&amp;#39;s Under his Kilt&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    [&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;url&amp;#34;&lt;/span&gt;]=&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    string(&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;39&lt;/span&gt;) &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;https://youtube.com/watch?v=GIpVBLDky30&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  [&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;4&lt;/span&gt;]=&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;array&lt;/span&gt;(&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;2&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    [&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;title&amp;#34;&lt;/span&gt;]=&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    string(&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;25&lt;/span&gt;) &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Deception, Lies, and CSGO&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    [&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;url&amp;#34;&lt;/span&gt;]=&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    string(&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;39&lt;/span&gt;) &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;https://youtube.com/watch?v=_8fU2QG-lV0&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  [&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;5&lt;/span&gt;]=&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;array&lt;/span&gt;(&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;2&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    [&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;title&amp;#34;&lt;/span&gt;]=&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    string(&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;68&lt;/span&gt;) &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Last Week Tonight with John Oliver: Independence Day (Web Exclusive)&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    [&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;url&amp;#34;&lt;/span&gt;]=&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    string(&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;39&lt;/span&gt;) &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;https://youtube.com/watch?v=IQwMCQFgQgo&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  [&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;6&lt;/span&gt;]=&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;array&lt;/span&gt;(&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;2&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    [&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;title&amp;#34;&lt;/span&gt;]=&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    string(&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;21&lt;/span&gt;) &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Last Week I Ate A Pug&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    [&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;url&amp;#34;&lt;/span&gt;]=&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    string(&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;39&lt;/span&gt;) &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;https://youtube.com/watch?v=TTk5uQL2oO8&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  [&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;7&lt;/span&gt;]=&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;array&lt;/span&gt;(&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;2&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    [&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;title&amp;#34;&lt;/span&gt;]=&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    string(&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;59&lt;/span&gt;) &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;PEP GUARDIOLA VS NOEL GALLAGHER | Exclusive First Interview&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    [&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;url&amp;#34;&lt;/span&gt;]=&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    string(&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;39&lt;/span&gt;) &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;https://youtube.com/watch?v=ZWE8qkmhGmc&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  [&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;8&lt;/span&gt;]=&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;array&lt;/span&gt;(&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;2&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    [&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;title&amp;#34;&lt;/span&gt;]=&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    string(&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;78&lt;/span&gt;) &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Skins, lies and videotape - Enough of these dishonest hacks. [strong language]&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    [&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;url&amp;#34;&lt;/span&gt;]=&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    string(&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;39&lt;/span&gt;) &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;https://youtube.com/watch?v=8z_VY8KZpMU&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  [&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;9&lt;/span&gt;]=&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;array&lt;/span&gt;(&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;2&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    [&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;title&amp;#34;&lt;/span&gt;]=&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    string(&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;62&lt;/span&gt;) &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;We Are America ft. John Cena | Love Has No Labels | Ad Council&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    [&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;url&amp;#34;&lt;/span&gt;]=&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    string(&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;39&lt;/span&gt;) &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;https://youtube.com/watch?v=0MdK8hBkR3s&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Isn’t it easy?&lt;/p&gt;
&lt;h3 id=&#34;the-end&#34;&gt;The end&lt;/h3&gt;
&lt;p&gt;I have some advice if you want to make this kind of script be processing the same page all the time:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;set the user agent header to simulate a real web browser request&lt;/li&gt;
&lt;li&gt;make calls with a random delay to avoid blacklisting from a web server&lt;/li&gt;
&lt;li&gt;use PHP 7&lt;/li&gt;
&lt;li&gt;try to optimize the script as much as possible&lt;/li&gt;
&lt;/ul&gt;
&lt;br/&gt;
&lt;p&gt;You can use this script for production code but, to be honest, it’s not the most optimal approach. If you are not satisfied, code it by yourself :-).&lt;/p&gt;
&lt;p&gt;Nice documentation is located here: &lt;a href=&#34;http://simplehtmldom.sourceforge.net/&#34;&gt;http://simplehtmldom.sourceforge.net/&lt;/a&gt;&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Medium-inspired Parallax Blur Effect For WordPress</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2016/01/medium-inspired-parallax-blur-effect/"/>
      <id>https://www.endpointdev.com/blog/2016/01/medium-inspired-parallax-blur-effect/</id>
      <published>2016-01-22T00:00:00+00:00</published>
      <author>
        <name>Marina Lohova</name>
      </author>
      <content type="html">
        &lt;p&gt;Are you are running a WordPress blog, but secretly dying to have that &lt;a href=&#34;https://medium.com/&#34;&gt;Medium&lt;/a&gt; parallax blur effect? I recently implemented this, and would like to share it with you. By the way, while I was working on the article, the effect was removed from Medium, which only makes having one on the website more precious.&lt;/p&gt;
&lt;p&gt;Let’s assume that we have our custom theme class &lt;code&gt;MyTheme&lt;/code&gt;. In functions.php:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;MyThemeBaseFunctions&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;__construct&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    add_image_size(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;blurred&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1600&lt;/span&gt;, &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1280&lt;/span&gt;, &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;true&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    add_filter(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;wp_generate_attachment_metadata&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;array&lt;/span&gt;(&lt;span style=&#34;color:#369&#34;&gt;$this&lt;/span&gt;,&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;wp_blur_attachment_filter&amp;#39;&lt;/span&gt;));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We added a custom image size blurred, and a callback &lt;code&gt;wp_blur_attachment_filter&lt;/code&gt; to &lt;code&gt;wp_generate_attachment_metadata&lt;/code&gt;. Here’s where the magic happens.&lt;/p&gt;
&lt;p&gt;Before that let’s talk a little about ImageMagick, a powerful library for image processing that we will use to create the blurred effect. After some experimenting I figured that the image needed to be darkened, and then a regular blur should be applied with sigma=20. You can read more about these settings at &lt;a href=&#34;http://www.imagemagick.org/Usage/blur/#blur&#34;&gt;ImageMagick Blur Usage&lt;/a&gt;. I used Gaussian Blur at first, but found the processing was extremely slow, and there wasn’t much difference in the end result compared to other blur methods.&lt;/p&gt;
&lt;p&gt;Now we are ready to write a blurring function:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;wp_blur_attachment_filter&lt;/span&gt;(&lt;span style=&#34;color:#369&#34;&gt;$image_data&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;if&lt;/span&gt; ( !isset(&lt;span style=&#34;color:#369&#34;&gt;$image_data&lt;/span&gt;[&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;sizes&amp;#39;&lt;/span&gt;][&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;blurred&amp;#39;&lt;/span&gt;]) )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$image_data&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       &lt;span style=&#34;color:#369&#34;&gt;$upload_dir&lt;/span&gt; = wp_upload_dir();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       &lt;span style=&#34;color:#369&#34;&gt;$src&lt;/span&gt; = &lt;span style=&#34;color:#369&#34;&gt;$upload_dir&lt;/span&gt;[&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;path&amp;#39;&lt;/span&gt;] . &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;/&amp;#39;&lt;/span&gt; . &lt;span style=&#34;color:#369&#34;&gt;$image_data&lt;/span&gt;[&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;sizes&amp;#39;&lt;/span&gt;][&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;large&amp;#39;&lt;/span&gt;][&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;file&amp;#39;&lt;/span&gt;];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       &lt;span style=&#34;color:#369&#34;&gt;$destination&lt;/span&gt; = &lt;span style=&#34;color:#369&#34;&gt;$upload_dir&lt;/span&gt;[&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;path&amp;#39;&lt;/span&gt;] . &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;/&amp;#39;&lt;/span&gt; . &lt;span style=&#34;color:#369&#34;&gt;$image_data&lt;/span&gt;[&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;sizes&amp;#39;&lt;/span&gt;][&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;blurred&amp;#39;&lt;/span&gt;][&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;file&amp;#39;&lt;/span&gt;];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       &lt;span style=&#34;color:#369&#34;&gt;$imagick&lt;/span&gt; = &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;new&lt;/span&gt; \Imagick(&lt;span style=&#34;color:#369&#34;&gt;$src&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       &lt;span style=&#34;color:#369&#34;&gt;$imagick&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;blurImage&lt;/span&gt;(&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;20&lt;/span&gt;, Imagick::&lt;span style=&#34;color:#369&#34;&gt;CHANNEL_ALL&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       &lt;span style=&#34;color:#369&#34;&gt;$imagick&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;modulateImage&lt;/span&gt;(&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;75&lt;/span&gt;, &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;105&lt;/span&gt;, &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;100&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       &lt;span style=&#34;color:#369&#34;&gt;$imagick&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;writeImage&lt;/span&gt;(&lt;span style=&#34;color:#369&#34;&gt;$destination&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$image_data&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; }&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;I darken the image:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#369&#34;&gt;$imagick&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;modulateImage&lt;/span&gt;(&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;75&lt;/span&gt;, &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;105&lt;/span&gt;, &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;100&lt;/span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And I blur the image:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#369&#34;&gt;$imagick&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;blurImage&lt;/span&gt;(&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;20&lt;/span&gt;, Imagick::&lt;span style=&#34;color:#369&#34;&gt;CHANNEL_ALL&lt;/span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now we are able to use the custom image size in the template. Place the helper function in functions.php:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;static&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;non_blurred&lt;/span&gt;(&lt;span style=&#34;color:#369&#34;&gt;$src&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#369&#34;&gt;$url&lt;/span&gt; = get_site_url() . substr(&lt;span style=&#34;color:#369&#34;&gt;$src&lt;/span&gt;, strrpos(&lt;span style=&#34;color:#369&#34;&gt;$src&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;/wp-content&amp;#39;&lt;/span&gt;));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#369&#34;&gt;$post_ID&lt;/span&gt; = attachment_url_to_postid(&lt;span style=&#34;color:#369&#34;&gt;$url&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;list&lt;/span&gt;(&lt;span style=&#34;color:#369&#34;&gt;$url&lt;/span&gt;, &lt;span style=&#34;color:#369&#34;&gt;$width&lt;/span&gt;, &lt;span style=&#34;color:#369&#34;&gt;$height&lt;/span&gt;) = wp_get_attachment_image_src(&lt;span style=&#34;color:#369&#34;&gt;$post_ID&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;large&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$url&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;static&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;blurred&lt;/span&gt;(&lt;span style=&#34;color:#369&#34;&gt;$src&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#369&#34;&gt;$url&lt;/span&gt; = get_site_url() . substr(&lt;span style=&#34;color:#369&#34;&gt;$src&lt;/span&gt;, strrpos(&lt;span style=&#34;color:#369&#34;&gt;$src&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;/wp-content&amp;#39;&lt;/span&gt;));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#369&#34;&gt;$post_ID&lt;/span&gt; = attachment_url_to_postid(&lt;span style=&#34;color:#369&#34;&gt;$src&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;list&lt;/span&gt;(&lt;span style=&#34;color:#369&#34;&gt;$url&lt;/span&gt;, &lt;span style=&#34;color:#369&#34;&gt;$width&lt;/span&gt;, &lt;span style=&#34;color:#369&#34;&gt;$height&lt;/span&gt;) = wp_get_attachment_image_src(&lt;span style=&#34;color:#369&#34;&gt;$post_ID&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;blurred&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$url&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And now use it in the template like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-html&#34; data-lang=&#34;html&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;div&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;class&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;blurImg&amp;#34;&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &amp;lt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;div&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;style&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;background-image: url(&amp;#39;&amp;lt;?php echo MyTheme::non_blurred(get_theme_mod( &amp;#39;header&amp;#39; )); ?&amp;gt;&amp;#39;)&amp;#34;&lt;/span&gt;&amp;gt;&amp;lt;/&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;div&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &amp;lt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;div&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;style&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;background-image: url(&amp;#39;&amp;lt;?php echo MyTheme::blurred(get_theme_mod(&amp;#39;header&amp;#39;)); ?&amp;gt;&amp;#39;); opacity: 0;&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;class&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;blur&amp;#34;&lt;/span&gt;&amp;gt;&amp;lt;/&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;div&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;/&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;div&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;header&lt;/span&gt;&amp;gt;&amp;lt;/&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;header&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Add CSS:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-css&#34; data-lang=&#34;css&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;.&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;blurImg&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;height&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;440&lt;/span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;px&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;left&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;position&lt;/span&gt;: &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;relative&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;top&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;width&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;100&lt;/span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;%&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;z-index&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;-1&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;.&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;blurImg&lt;/span&gt; &amp;gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;div&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;background-position&lt;/span&gt;: &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;center&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;center&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;background-repeat&lt;/span&gt;: &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;no-repeat&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;background-size&lt;/span&gt;: &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;cover&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;height&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;440&lt;/span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;px&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;position&lt;/span&gt;: &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;fixed&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;width&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;100&lt;/span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;%&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;header&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;padding&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0&lt;/span&gt; &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;20&lt;/span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;px&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;position&lt;/span&gt;: &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;absolute&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;top&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;width&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;100&lt;/span&gt;&lt;span style=&#34;color:#888;font-weight:bold&#34;&gt;%&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;z-index&lt;/span&gt;: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Add JavaScript magic sauce to gradually replace the non-blurred image with the blurred version as the user scrolls:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;(&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;function&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    jQuery(&lt;span style=&#34;color:#038&#34;&gt;window&lt;/span&gt;).scroll(&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;function&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;var&lt;/span&gt; H = &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;240&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;var&lt;/span&gt; oVal = jQuery(&lt;span style=&#34;color:#038&#34;&gt;window&lt;/span&gt;).scrollTop() / H;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      jQuery(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;.blurImg .blur&amp;#34;&lt;/span&gt;).css(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;opacity&amp;#34;&lt;/span&gt;, oVal);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    });
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }).call(&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;this&lt;/span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Generating the blurred version can be very strenuous on the server. I would oftentimes receive the error PHP Fatal error:  Maximum execution time of 30 seconds exceeded. There are ways to work around that. One way is to use a quicker method of blurring the image by shrinking it, blurring, and resizing it back. Another way is to use a background job, something like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;add_action( &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;add_attachment&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;array&lt;/span&gt;(&lt;span style=&#34;color:#369&#34;&gt;$this&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;wp_blur_attachment_filter&amp;#39;&lt;/span&gt;) );
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;add_action( &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;wp_blur_attachment_filter_hook&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;wp_blur_attachment_filter_callback&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;wp_blur_attachment_filter_callback&lt;/span&gt;(&lt;span style=&#34;color:#369&#34;&gt;$path&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#369&#34;&gt;$path_parts&lt;/span&gt; = pathinfo(&lt;span style=&#34;color:#369&#34;&gt;$path&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#369&#34;&gt;$imagick&lt;/span&gt; = &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;new&lt;/span&gt; \Imagick(&lt;span style=&#34;color:#369&#34;&gt;$path&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#369&#34;&gt;$imagick&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;blurImage&lt;/span&gt;(&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;20&lt;/span&gt;, Imagick::&lt;span style=&#34;color:#369&#34;&gt;CHANNEL_ALL&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#369&#34;&gt;$imagick&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;modulateImage&lt;/span&gt;(&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;75&lt;/span&gt;, &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;105&lt;/span&gt;, &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;100&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#369&#34;&gt;$destination&lt;/span&gt; = dirname(&lt;span style=&#34;color:#369&#34;&gt;$path&lt;/span&gt;) . &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;/&amp;#34;&lt;/span&gt; . &lt;span style=&#34;color:#369&#34;&gt;$path_parts&lt;/span&gt;[&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;filename&amp;#39;&lt;/span&gt;] . &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;_darken_blur.&amp;#34;&lt;/span&gt; . &lt;span style=&#34;color:#369&#34;&gt;$path_parts&lt;/span&gt;[&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;extension&amp;#39;&lt;/span&gt;];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#369&#34;&gt;$imagick&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;writeImage&lt;/span&gt;(&lt;span style=&#34;color:#369&#34;&gt;$destination&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;wp_blur_attachment_filter&lt;/span&gt;(&lt;span style=&#34;color:#369&#34;&gt;$post_ID&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#369&#34;&gt;$path&lt;/span&gt; = get_attached_file( &lt;span style=&#34;color:#369&#34;&gt;$post_ID&lt;/span&gt; );
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  wp_schedule_single_event(time(), &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;wp_blur_attachment_filter_hook&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;array&lt;/span&gt;(&lt;span style=&#34;color:#369&#34;&gt;$path&lt;/span&gt;));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Or better yet, use cloud image processing—​I wrote about that &lt;a href=&#34;/blog/2015/12/image-processing-in-cloud-with-blitline/&#34;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I hope you found this writeup useful!&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Install WordPress on Heroku in OS X Yosemite</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2016/01/install-wordpress-on-heroku-in-os-x/"/>
      <id>https://www.endpointdev.com/blog/2016/01/install-wordpress-on-heroku-in-os-x/</id>
      <published>2016-01-12T00:00:00+00:00</published>
      <author>
        <name>Marina Lohova</name>
      </author>
      <content type="html">
        &lt;p&gt;I wanted to install WordPress locally for my blog (about programming!), but using MAMP, XAMP or even Vagrant for this seemed overkill. I wanted a light setup. PHP and Apache are already integrated into Mac OS X, so why not use them? I wanted to deploy the app to Heroku, so that was another thing, since Heroku only provides PostgreSQL, not MySQL, out of the box. I’d like to share my research on how I did it.&lt;/p&gt;
&lt;h3 id=&#34;wordpress-with-heroku-support&#34;&gt;WordPress with Heroku support&lt;/h3&gt;
&lt;p&gt;I found &lt;a href=&#34;https://github.com/mhoofman/wordpress-heroku&#34;&gt;this handy WordPress template with built-in Heroku support&lt;/a&gt;. It has everything one needs to run WordPress on Heroku: PostgreSQL for WordPress (because MySQL on Heroku is a paid service), Amazon S3 and Cloudfront for your uploads since Heroku has an ephemeral file system, WP Sendgrid to send emails and WordPress HTTPS. Check out a copy with this command:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;git clone https://github.com/mhoofman/wordpress-heroku.git&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Let’s run the project locally first because a file cannot be written to Heroku’s file system, and updating and installing plugins or themes should be done locally anyways and then pushed to Heroku. I’m using &lt;a href=&#34;https://www.jetbrains.com/phpstorm/&#34;&gt;PhpStorm&lt;/a&gt; for my PHP development.&lt;/p&gt;
&lt;h3 id=&#34;configuring-apache&#34;&gt;Configuring Apache&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;mkdir -p ~/Sites
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#038&#34;&gt;echo&lt;/span&gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&amp;lt;html&amp;gt;&amp;lt;body&amp;gt;&amp;lt;h1&amp;gt;my site works&amp;lt;/h1&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;#34;&lt;/span&gt; &amp;gt; ~/sites/index.html.en&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Enable PHP support:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sudo vi /etc/apache2/httpd.conf&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Uncomment the following lines to look like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;LoadModule php5_module libexec/apache2/libphp5.so
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;LoadModule userdir_module libexec/apache2/mod_userdir.so
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Include /private/etc/apache2/extra/httpd-userdir.conf&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Save and exit. Open the following file:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sudo vi /etc/apache2/extra/httpd-userdir.conf&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Uncomment the following line to look like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Include /private/etc/apache2/users/*.conf&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Save and exit. Open or create:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sudo vi /etc/apache2/users/~YOURUSERNAME.conf&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Type the following in there:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;Directory &amp;#34;/Users/~YOURUSERNAME/Sites/&amp;#34;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    AddLanguage en .en
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    LanguagePriority en fr de
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ForceLanguagePriority Fallback
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    Options Indexes MultiViews
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    AllowOverride None
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    Order allow,deny
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    Allow from localhost
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    Require all granted
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;/Directory&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Restart Apache with:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sudo apachectl restart&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Go to http://localhost/~YOURUSER/wordpress-heroku/ and enjoy the results of your work! OK, not so fast! There are more steps to make it happen ;)&lt;/p&gt;
&lt;h3 id=&#34;enabling-postgresql-for-php&#34;&gt;Enabling PostgreSQL for PHP&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Your PHP installation appears to be missing the PostgreSQL extension which is required by WordPress with PG4WP.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Here is a handy script to fix this problem &lt;a href=&#34;https://gist.github.com/marinalohova/ec5d77ffd9d8e8acce2c&#34;&gt;Install PHP PGSQL extensions on Mac OS X Yosemite (change PHP_VER with your PHP version)&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&#34;creating-the-database&#34;&gt;Creating the database&lt;/h3&gt;
&lt;p&gt;Hit http://localhost/~YOURUSER/blog-heroku/wp-admin/install.php&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-html&#34; data-lang=&#34;html&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Error establishing a database connection&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The template we are using is tailored for the deployment to Heroku, which means wp-config.php takes its values from the DATABASE_URL environment variable that Heroku config creates in local environment pointing to the database source on Heroku servers.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;pg_ctl -D /usr/local/var/postgres -l /usr/local/var/postgres/server.log start
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;gt; createdb wordpress
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;gt; psql wordpress
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;CREATE USER wordpress WITH PASSWORD &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;wordpress&amp;#39;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;GRANT ALL PRIVILEGES ON DATABASE wordpress to wordpress; &lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In wp-config.php, edit as follows. Make sure it matches the database and user that you just created.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#369&#34;&gt;$db&lt;/span&gt; = parse_url(&lt;span style=&#34;color:#369&#34;&gt;$_ENV&lt;/span&gt;[&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;DATABASE_URL&amp;#34;&lt;/span&gt;] ? &lt;span style=&#34;color:#369&#34;&gt;$_ENV&lt;/span&gt;[&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;DATABASE_URL&amp;#34;&lt;/span&gt;] : &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;postgres://wordpress:wordpress@localhost:5432/wordpress&amp;#34;&lt;/span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now 5 hours later, you are completely ready for the famous 5-min install ;D. Go to http://localhost/~YOURUSER/blog-heroku/wp-admin/install.php&lt;/p&gt;
&lt;h3 id=&#34;uploading-the-custom-themeplugin&#34;&gt;Uploading the custom theme/plugin&lt;/h3&gt;
&lt;p&gt;What to do next? Of course, upload a custom theme or plugin.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Unable to create directory wp-content/uploads/2015/08. Is its parent directory writable by the server?
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ cd ~/Sites/THESITE
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ sudo chown -R _www wordpress
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ sudo chmod -R g+w wordpress&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;If you encounter an error asking you for FTP credentials in order to do this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-plain&#34; data-lang=&#34;plain&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;To perform the requested action, WordPress needs to access your web server.
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Please enter your FTP credentials to proceed.
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;If you do not remember your credentials, you should contact your web host.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The problem is that Apache HTTP Server in Mac OS X runs under the user account _www which belongs to the group _www. To allow WordPress to perform operations with Apache, one way to do this is to change the owner of the wordpress directory and its contents to _www. Keep the group as staff, a group to which your user account belongs and give write permissions to the group.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ &lt;span style=&#34;color:#038&#34;&gt;cd&lt;/span&gt; ~/Sites/THESITE
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ sudo chown -R _www wordpress
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ sudo chmod -R g+w wordpress&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This way, no file nor directory is world-writable.&lt;/p&gt;
&lt;p&gt;Remember to commit your plugins/themes because due to the nature of Heroku all of the files will be overwritten there if uncommitted or not in the database, effectively wiping out all of your changes at each server restart if you do them on the server.&lt;/p&gt;
&lt;p&gt;I installed this pretty theme for myself called Literatum—​just bragging.&lt;/p&gt;
&lt;h3 id=&#34;deployment-to-heroku&#34;&gt;Deployment to Heroku&lt;/h3&gt;
&lt;p&gt;One of the most exciting last steps. This will make your blog visible to the world! Commit the changes:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;rm -rf .git
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;git init
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;git add .
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;git commit -m &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Initial commit&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Create Heroku app:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ &lt;span style=&#34;color:#038&#34;&gt;cd&lt;/span&gt; wordpress-heroku
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ heroku create
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ heroku addons:create heroku-postgresql
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ heroku pg:promote HEROKU_POSTGRESQL_INSTANCE
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ heroku addons:create sendgrid:starter&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Your first deployment!&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;git push heroku master&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Go to &lt;a href=&#34;http://YOURAPP.herokuapp.com/wp-admin/install.php&#34;&gt;http://YOURAPP.herokuapp.com/wp-admin/install.php&lt;/a&gt; and run the famous 5-minute setup again, activate all the plugins and the chosen custom theme aaand&amp;hellip; You are done!&lt;/p&gt;
&lt;p&gt;Hope you will find this write-up useful and it will help you create your blog on the web!&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Image Processing In The Cloud With Blitline and Wordpress</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2015/12/image-processing-in-cloud-with-blitline/"/>
      <id>https://www.endpointdev.com/blog/2015/12/image-processing-in-cloud-with-blitline/</id>
      <published>2015-12-01T00:00:00+00:00</published>
      <author>
        <name>Marina Lohova</name>
      </author>
      <content type="html">
        &lt;p&gt;Working with ImageMagick can be difficult. First, you have to get it installed on your OS (do you have Dev libs in place?), then you have to enable it in the language of your choice, then get it working in your application. After all that, do it all over again on the staging server where debugging may be complicated, and you may not have Admin rights. Meet Image Processing in the Cloud. Meet Blitline.&lt;/p&gt;
&lt;p&gt;I’m doing a lot of things with Wordpress now, so we’ll set it up with Wordpress and PHP.&lt;/p&gt;
&lt;h3 id=&#34;step-1&#34;&gt;Step 1&lt;/h3&gt;
&lt;p&gt;Get a free developer account with &lt;a href=&#34;https://blitline.com/signup&#34;&gt;Blitline&lt;/a&gt;, and note your application id.&lt;/p&gt;
&lt;h3 id=&#34;step-2&#34;&gt;Step 2&lt;/h3&gt;
&lt;p&gt;Get the Blitline PHP wrapper library &lt;a href=&#34;https://github.com/karikas/blitline_php&#34;&gt;Blitline_php&lt;/a&gt;. It’s clean and awesome, but unfortunately at the time of writing it was missing a few things, like being able to run your own Image Magick script and set a postback URL for when the job is finished. Yes, those are all useful features of Blitline cloud image processing! I’m still waiting on my pull request to be incorporated into the official version, so you can use mine that has these two useful features for now in &lt;a href=&#34;https://github.com/marinalohova/blitline_php&#34;&gt;my Blitline_php&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&#34;step-3&#34;&gt;Step 3&lt;/h3&gt;
&lt;p&gt;Now it’s time to integrate it in our application. Since it’s Wordpress, I’m doing it in the &amp;lsquo;wp_generate_attachment_metadata&amp;rsquo; callback in functions.php&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;require_once&lt;/span&gt; dirname(&lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;__FILE__&lt;/span&gt;) . &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;/blitline_php/lib/blitline_php.php&amp;#39;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;...
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;add_filter( &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;wp_generate_attachment_metadata&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;array&lt;/span&gt;(&lt;span style=&#34;color:#369&#34;&gt;$this&lt;/span&gt;,&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;wp_blur_attachment_filter&amp;#39;&lt;/span&gt;), &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;10&lt;/span&gt;, &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;2&lt;/span&gt; );
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;...
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;wp_blur_attachment_filter&lt;/span&gt;(&lt;span style=&#34;color:#369&#34;&gt;$image_data&lt;/span&gt;, &lt;span style=&#34;color:#369&#34;&gt;$attachment_id&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#369&#34;&gt;$url&lt;/span&gt; = wp_get_attachment_url(&lt;span style=&#34;color:#369&#34;&gt;$attachment_id&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;list&lt;/span&gt;(&lt;span style=&#34;color:#369&#34;&gt;$src&lt;/span&gt;, &lt;span style=&#34;color:#369&#34;&gt;$width&lt;/span&gt;, &lt;span style=&#34;color:#369&#34;&gt;$length&lt;/span&gt;) = wp_get_attachment_image_src(&lt;span style=&#34;color:#369&#34;&gt;$attachment_id&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#369&#34;&gt;$data&lt;/span&gt; = pathinfo(&lt;span style=&#34;color:#369&#34;&gt;$src&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#369&#34;&gt;$dest&lt;/span&gt; = &lt;span style=&#34;color:#369&#34;&gt;$data&lt;/span&gt;[&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;filename&amp;#39;&lt;/span&gt;] . &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;_darken_75_105_100_blur_0_20.&amp;#39;&lt;/span&gt; . &lt;span style=&#34;color:#369&#34;&gt;$data&lt;/span&gt;[&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;extension&amp;#39;&lt;/span&gt;];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#369&#34;&gt;$Blit&lt;/span&gt; = &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;new&lt;/span&gt; Blitline_php();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#369&#34;&gt;$Blit&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;load&lt;/span&gt;(&lt;span style=&#34;color:#369&#34;&gt;$url&lt;/span&gt;, &lt;span style=&#34;color:#369&#34;&gt;$dest&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#369&#34;&gt;$Blit&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;do_script&lt;/span&gt;(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;convert input.png -blur 0x20 -modulate 75,105,100 output.png&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#369&#34;&gt;$Blit&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;set_postback_url&lt;/span&gt;( get_site_url() . &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;/wp-json/myapp/v1/blitline_callback&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#369&#34;&gt;$results&lt;/span&gt; = &lt;span style=&#34;color:#369&#34;&gt;$Blit&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;process&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#369&#34;&gt;$results&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;success&lt;/span&gt;()) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;foreach&lt;/span&gt;(&lt;span style=&#34;color:#369&#34;&gt;$results&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;get_images&lt;/span&gt;() &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;as&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;$name&lt;/span&gt; =&amp;gt; &lt;span style=&#34;color:#369&#34;&gt;$url&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      error_log(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Processed: &lt;/span&gt;&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;{&lt;/span&gt;&lt;span style=&#34;color:#369&#34;&gt;$name&lt;/span&gt;&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt; at &lt;/span&gt;&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;{&lt;/span&gt;&lt;span style=&#34;color:#369&#34;&gt;$url&lt;/span&gt;&lt;span style=&#34;color:#33b;background-color:#fff0f0&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#04d;background-color:#fff0f0&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  } &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;else&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    error_log(&lt;span style=&#34;color:#369&#34;&gt;$results&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;get_errors&lt;/span&gt;());
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We are sending a JSON POST request to Blitline to make the blurred and saturated version of the uploaded image. You can track the progress of your jobs &lt;a href=&#34;https://blitline.com/dashboard&#34;&gt;here&lt;/a&gt;. The request will return a URL to the image on the Blitline server, but the image may not be there right away, because the processing is asynchronous.&lt;/p&gt;
&lt;p&gt;I tried to set up S3 bucket integration (yes, Blitline can upload to S3 for you!), but the setup procedure is quite tedious. You have to manually enter your AWS Canonical ID (and obtain it first from Amazon) on the Blitline page. Then you have to create a special &lt;a href=&#34;https://www.blitline.com/docs/s3_permissions&#34;&gt;policy&lt;/a&gt; in your bucket for Blitline. This is a lot of hassle, and giving permissions to someone else might not be the way to go for you. For me personally it didn’t work, because my policy was being automatically overwritten all the time. I don’t even know why. So here’s where the &lt;a href=&#34;https://blitline.com/docs/postback#returnedData&#34;&gt;postback URL&lt;/a&gt; comes in play.&lt;/p&gt;
&lt;h3 id=&#34;step-4&#34;&gt;Step 4&lt;/h3&gt;
&lt;p&gt;I’m using &lt;a href=&#34;http://v2.wp-api.org&#34;&gt;this plugin WP-API V2&lt;/a&gt; that soon will become part of Wordpress to make REST endpoints. In wp-content/mu-plugins/my-app-custom-endpoints/lib/endpoints.php&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;add_action(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;rest_api_init&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;function&lt;/span&gt; () {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  register_rest_route(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;portfolio/v1&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;/blitline_callback&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;array&lt;/span&gt;(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;methods&amp;#39;&lt;/span&gt; =&amp;gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;POST&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;callback&amp;#39;&lt;/span&gt; =&amp;gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;process_blitline_callback&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  ));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;});&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In wp-content/mu-plugins/loader.php&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;require_once&lt;/span&gt; dirname(&lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;__FILE__&lt;/span&gt;) . &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;/blitline_php/lib/blitline_php.php&amp;#39;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;require_once&lt;/span&gt; dirname(&lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;__FILE__&lt;/span&gt;) . &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;/my-app-custom-endpoints/api.php&amp;#39;&lt;/span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In wp-content/mu-plugins/my-app-custom-endpoints/api.php&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;if&lt;/span&gt;( ! defined( &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;ABSPATH&amp;#39;&lt;/span&gt; ) ) &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;exit&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;require_once&lt;/span&gt; dirname(&lt;span style=&#34;color:#036;font-weight:bold&#34;&gt;__FILE__&lt;/span&gt;) . &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;/lib/endpoints.php&amp;#39;&lt;/span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Here’s the fun part. Add to wp-content/mu-plugins/my-app-custom-endpoints/lib/endpoints.php&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;use&lt;/span&gt; Aws\S3\S3Client;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#06b;font-weight:bold&#34;&gt;process_blitline_callback&lt;/span&gt;(&lt;span style=&#34;color:#369&#34;&gt;$request&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;if&lt;/span&gt;( !class_exists( &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;WP_Http&amp;#39;&lt;/span&gt; ) )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;include_once&lt;/span&gt;( ABSPATH . WPINC. &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;/class-http.php&amp;#39;&lt;/span&gt; );
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#369&#34;&gt;$s3Client&lt;/span&gt; = S3Client::&lt;span style=&#34;color:#369&#34;&gt;factory&lt;/span&gt;(&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;array&lt;/span&gt;(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;credentials&amp;#39;&lt;/span&gt; =&amp;gt; &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;array&lt;/span&gt;(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;key&amp;#39;&lt;/span&gt;    =&amp;gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;YOUR S3 KEY&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;secret&amp;#39;&lt;/span&gt; =&amp;gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;YOUR S3 SECRET&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  ));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#369&#34;&gt;$photo&lt;/span&gt; = &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;new&lt;/span&gt; WP_Http();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#369&#34;&gt;$body&lt;/span&gt; = &lt;span style=&#34;color:#369&#34;&gt;$request&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;get_body_params&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#369&#34;&gt;$var&lt;/span&gt; = (&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;array&lt;/span&gt;) json_decode(stripslashes(&lt;span style=&#34;color:#369&#34;&gt;$body&lt;/span&gt;[&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;results&amp;#39;&lt;/span&gt;]), &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;true&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;if&lt;/span&gt; (isset(&lt;span style=&#34;color:#369&#34;&gt;$var&lt;/span&gt;[&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;images&amp;#39;&lt;/span&gt;][&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0&lt;/span&gt;][&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;error&amp;#39;&lt;/span&gt;])) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    error_log(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;Error &amp;#39;&lt;/span&gt; . &lt;span style=&#34;color:#369&#34;&gt;$var&lt;/span&gt;[&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;images&amp;#39;&lt;/span&gt;][&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0&lt;/span&gt;][&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;error&amp;#39;&lt;/span&gt;]);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#369&#34;&gt;$photo&lt;/span&gt; = &lt;span style=&#34;color:#369&#34;&gt;$photo&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;request&lt;/span&gt;( &lt;span style=&#34;color:#369&#34;&gt;$var&lt;/span&gt;[&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;images&amp;#39;&lt;/span&gt;][&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0&lt;/span&gt;][&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;s3_url&amp;#39;&lt;/span&gt;] );
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#369&#34;&gt;$photo_name&lt;/span&gt; = &lt;span style=&#34;color:#369&#34;&gt;$var&lt;/span&gt;[&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;images&amp;#39;&lt;/span&gt;][&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0&lt;/span&gt;][&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;image_identifier&amp;#39;&lt;/span&gt;];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#369&#34;&gt;$attachment&lt;/span&gt; = wp_upload_bits(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#369&#34;&gt;$photo_name&lt;/span&gt;, &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;null&lt;/span&gt;, &lt;span style=&#34;color:#369&#34;&gt;$photo&lt;/span&gt;[&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;body&amp;#39;&lt;/span&gt;],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    date(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;Y-m&amp;#34;&lt;/span&gt;, strtotime(&lt;span style=&#34;color:#369&#34;&gt;$photo&lt;/span&gt;[&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;headers&amp;#39;&lt;/span&gt;][&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;last-modified&amp;#39;&lt;/span&gt;]))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  );
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#369&#34;&gt;$upload_dir&lt;/span&gt; = wp_upload_dir();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#369&#34;&gt;$s3Client&lt;/span&gt;-&amp;gt;&lt;span style=&#34;color:#369&#34;&gt;putObject&lt;/span&gt;(&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;array&lt;/span&gt;(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;Bucket&amp;#39;&lt;/span&gt; =&amp;gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;yourbucket&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;Key&amp;#39;&lt;/span&gt;    =&amp;gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;wp-content/uploads&amp;#39;&lt;/span&gt; . &lt;span style=&#34;color:#369&#34;&gt;$upload_dir&lt;/span&gt;[&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;subdir&amp;#39;&lt;/span&gt;] . &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;/&amp;#39;&lt;/span&gt; . &lt;span style=&#34;color:#369&#34;&gt;$photo_name&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;SourceFile&amp;#39;&lt;/span&gt;   =&amp;gt; &lt;span style=&#34;color:#369&#34;&gt;$attachment&lt;/span&gt;[&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;file&amp;#39;&lt;/span&gt;],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;ACL&amp;#39;&lt;/span&gt;    =&amp;gt; &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;public-read&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  ));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In the callback we download the processed image from the temporary Blitline URL. One little bonus in here is the upload to Amazon S3 bucket. I use &lt;a href=&#34;http://docs.aws.amazon.com/aws-sdk-php/v2/api/index.html&#34;&gt;Amazon PHP SDK&lt;/a&gt; to achieve that. Note the permissions. This was one last thing when I actually almost gave up trying to make Blitline postback URL work. When the image finally appeared in my bucket, it wasn’t accessible from the outside, because I didn’t set permissions.&lt;/p&gt;
&lt;h3 id=&#34;step-5-if-it-doesnt-work-debugging&#34;&gt;Step 5. If it doesn’t work. Debugging.&lt;/h3&gt;
&lt;p&gt;I used &lt;a href=&#34;https://addons.mozilla.org/en-us/firefox/addon/httprequester/&#34;&gt;Firefox add-on HttpRequester&lt;/a&gt; to post the mock response from Blitline to my application. If you don’t want to deploy each time you change the code, here’s another useful thing &lt;a href=&#34;https://localtunnel.me/&#34;&gt;LocalTunnel&lt;/a&gt;, so you can expose your localhost to the internet and set the postback to your local app.&lt;/p&gt;
&lt;p&gt;And that’s how you do image processing in the cloud!&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Broken wikis due to PHP and MediaWiki “namespace” conflicts</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2015/11/broken-wikis-due-to-php-and-mediawiki/"/>
      <id>https://www.endpointdev.com/blog/2015/11/broken-wikis-due-to-php-and-mediawiki/</id>
      <published>2015-11-09T00:00:00+00:00</published>
      <author>
        <name>Greg Sabino Mullane</name>
      </author>
      <content type="html">
        &lt;div class=&#34;separator&#34; style=&#34;clear: both; float:right; text-align: center;&#34;&gt;&lt;a href=&#34;/blog/2015/11/broken-wikis-due-to-php-and-mediawiki/image-0-big.jpeg&#34; imageanchor=&#34;1&#34; style=&#34;clear: right; margin-bottom: 1em; margin-left: 1em;&#34;&gt;&lt;img border=&#34;0&#34; src=&#34;/blog/2015/11/broken-wikis-due-to-php-and-mediawiki/image-0.jpeg&#34;/&gt;&lt;/a&gt;&lt;br/&gt;&lt;small&gt;&lt;a href=&#34;https://flic.kr/p/79LtQP&#34;&gt;Photo&lt;/a&gt; by &lt;a href=&#34;https://www.flickr.com/people/mothernaturephotos/&#34;&gt;Elliot Gilfix&lt;/a&gt;&lt;/small&gt;&lt;/div&gt;
&lt;p&gt;I was recently tasked with resurrecting an ancient wiki. In this case, a wiki last
updated in 2005, running &lt;a href=&#34;https://mediawiki.org/&#34;&gt;MediaWiki&lt;/a&gt; version 1.5.2, and that needed to get transformed
to something more modern (in this case, version 1.25.3). The old settings and extensions were not important,
but we did want to preserve any content that was made.&lt;/p&gt;
&lt;p&gt;The items available to me were a tarball of the mediawiki directory (including the
LocalSettings.php file), and a MySQL dump of the wiki database. To import the items
to the new wiki (which already had been created and was gathering content), an
XML dump needed to be generated. MediaWiki has two simple command-line scripts
to export and import your wiki, named dumpBackup.php and
importDump.php. So it
was simply a matter of getting the wiki up and running enough to run dumpBackup.php.&lt;/p&gt;
&lt;p&gt;My first thought was to simply bring the wiki up as it was—​all the files were in
place, after all, and specifically designed to read the old version of the schema.
(Because the database scheme changes over time, newer MediaWikis cannot run against
older database dumps.) So I unpacked the MediaWiki directory, and prepared to
resurrect the database.&lt;/p&gt;
&lt;p&gt;Rather than MySQL, the distro I was using defaulted to using the freer and
arguably better &lt;a href=&#34;https://mariadb.org/&#34;&gt;MariaDB&lt;/a&gt;, which installed painlessly.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;## Create a quick dummy database:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ echo &amp;#39;create database footest&amp;#39; | sudo mysql
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;## Install the 1.5.2 MediaWiki database into it:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ cat mysql-acme-wiki.sql | sudo mysql footest
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;## Sanity test as the output of the above commands is very minimal:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;echo &amp;#39;select count(*) from revision&amp;#39; | sudo mysql footest
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;count(*)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;727977&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Success! The MariaDB instance was easily able to parse and load the
old MySQL file. The next step was to unpack the old 1.5.2 mediawiki directory
into Apache’s docroot, adjust the LocalSettings.php file to point to the
newly created database, and try and access the wiki. Once all that was done, however, both the
browser and the command-line scripts spat out the same error:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Parse error: syntax error, unexpected &amp;#39;Namespace&amp;#39; (T_NAMESPACE), 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  expecting identifier (T_STRING) in 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  /var/www/html/wiki/includes/Namespace.php on line 52&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;What is this about? Turns out that some years ago, someone added a class to
MediaWiki with the terrible name of “Namespace”. Years later, PHP finally
caved to user demands and added some non-optimal support for namespaces, which
means that (surprise), “namespace” is now a reserved word. In short, older
versions of MediaWiki cannot run with modern (5.3.0 or greater) versions
of PHP. Amusingly, a web search for this error on DuckDuckGo revealed not
only many people asking about this error and/or offering solutions, but
many results were actual wikis that are currently not working!
Thus, their wiki was working fine one moment, and then PHP was (probably automatically)
upgraded, and now the wiki is dead. But DuckDuckGo is happy to show you
the wiki and its now-single page of output, the error above. :)&lt;/p&gt;
&lt;p&gt;There are three groups to blame for this sad situation, as well as
three obvious solutions to the problem. The first group to share the
blame, and the most culpable, is the MediaWiki developers who chose
the word “Namespace” as a class name. As PHP has always had very
non-existent/poor support for packages, namespaces, and scoping, it is
vital that all your PHP variables, class names, etc. are as unique as possible.
To that end, the name of the class was changed at some point&lt;br&gt;
to “MWNamespace”—​but the damage has been done. The second group to share the
blame is the PHP developers, both for not having namespace support for
so long, and for making it into a reserved word full knowing that one of
the poster children for “mature” PHP apps, MediaWiki, was using “namespace”.
Still, we cannot blame them too much for picking what is a pretty obvious
word choice. The third group to blame is the owners of all those wikis
out there that are suffering that syntax error. They ought to be repairing their
wikis. The fixes are pretty simple, which leads us to the three solutions to the problem.&lt;/p&gt;
&lt;div class=&#34;separator&#34; style=&#34;clear: both; padding: 0em 0em 2em 2em; float:right; text-align: center;&#34;&gt;&lt;a href=&#34;/blog/2015/11/broken-wikis-due-to-php-and-mediawiki/image-1-big.png&#34; id=&#34;gtsm.com/mediawiki_flower.png&#34; imageanchor=&#34;1&#34; style=&#34;clear: right; margin-bottom: 1em; margin-left: 1em;&#34;&gt;&lt;img border=&#34;0&#34; src=&#34;/blog/2015/11/broken-wikis-due-to-php-and-mediawiki/image-1.png&#34;/&gt;&lt;/a&gt;&lt;br/&gt;&lt;small&gt;MediaWiki’s cool install image&lt;/small&gt;&lt;/div&gt;
&lt;p&gt;The quickest (and arguably worst) solution is to downgrade PHP to
something older than 5.3. At that point, the wiki will probably work
again. Unless it’s a museum (static) wiki, and you do not intend to
upgrade anything on the server ever again, this solution will not
work long term. The second solution is to upgrade your MediaWiki! The upgrade process is actually very
robust and works well even for very old versions of MediaWiki (as
we shall see below). The third solution is to make some quick edits
to the code to replace all uses of “Namespace” with “MWNamespace”.
Not a good solution, but ideal when you just need to get the wiki up
and running. Thus, it’s the solution I tried for the original problem.&lt;/p&gt;
&lt;p&gt;However, once I solved the Namespace problem by renaming to MWNamespace,
some other problems popped up. I will not run through them here—​although they were
small and quickly solved, it began to feel like a neverending whack-a-mole
game, and I decided to cut the Gordian knot with a completely different
approach.&lt;/p&gt;
&lt;p&gt;As mentioned, MediaWiki has an upgrade process, which means that
you can install the software and it will, in theory, transform your
database schema and data to the new version. However, version
1.5 of MediaWiki was released in October 2005, almost exactly
10 years ago from the current release (1.25.3 as of this writing).
Ten years is a really, really long time on the Internet.
Could MediaWiki really convert something that old? (spoilers: yes!).
Only one way to find out. First, I prepared the old database for the upgrade.
Note that all of this was done on a private local machine where security was not
an issue.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;## As before, install mariadb and import into the &amp;#39;footest&amp;#39; database
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ echo &amp;#39;create database footest&amp;#39; | sudo mysql test
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ cat mysql-acme-wiki.sql | sudo mysql footest
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ echo &amp;#34;set password for &amp;#39;root&amp;#39;@&amp;#39;localhost&amp;#39; = password(&amp;#39;foobar&amp;#39;)&amp;#34; | sudo mysql test&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Next, I grabbed the latest version of MediaWiki, verified it, put it in place, and
started up the webserver:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ wget http://releases.wikimedia.org/mediawiki/1.25/mediawiki-1.25.3.tar.gz
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ wget http://releases.wikimedia.org/mediawiki/1.25/mediawiki-1.25.3.tar.gz.sig
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ gpg --verify mediawiki-1.25.3.tar.gz.sig 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;gpg: assuming signed data in `mediawiki-1.25.3.tar.gz&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;gpg: Signature made Fri 16 Oct 2015 01:09:35 PM EDT using RSA key ID 23107F8A
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;gpg: Good signature from &amp;#34;Chad Horohoe &amp;lt;chad@wikimedia.org&amp;gt;&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;gpg:                 aka &amp;#34;keybase.io/demon &amp;lt;demon@keybase.io&amp;gt;&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;gpg:                 aka &amp;#34;Chad Horohoe (Personal e-mail) &amp;lt;innocentkiller@gmail.com&amp;gt;&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;gpg:                 aka &amp;#34;Chad Horohoe (Alias for existing email) &amp;lt;chadh@wikimedia.org&amp;gt;&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;## Chad&amp;#39;s cool. Ignore the below.
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;gpg: WARNING: This key is not certified with a trusted signature!
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;gpg:          There is no indication that the signature belongs to the owner.
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Primary key fingerprint: 41B2 ABE8 17AD D3E5 2BDA  946F 72BC 1C5D 2310 7F8A&amp;lt;/chadh@wikimedia.org&amp;gt;&amp;lt;/innocentkiller@gmail.com&amp;gt;&amp;lt;/demon@keybase.io&amp;gt;&amp;lt;/chad@wikimedia.org&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ tar xvfz mediawiki-1.25.3.tar.gz
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ mv mediawiki-1.25.3 /var/www/html/
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ cd /var/www/html/mediawiki-1.25.3
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;## Because &amp;#34;composer&amp;#34; is a really terrible idea:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ git clone https://gerrit.wikimedia.org/r/p/mediawiki/vendor.git 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ sudo service httpd start&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now, we can call up the web page to install MediaWiki.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Visit http://localhost/mediawiki-1.25.3, see the familiar yellow flower&lt;/li&gt;
&lt;li&gt;Click “set up the wiki”&lt;/li&gt;
&lt;li&gt;Click next until you find “Database name”, and set to “footest”&lt;/li&gt;
&lt;li&gt;Set the “Database password:” to “foobar”&lt;/li&gt;
&lt;li&gt;Aha! Looks what shows up: “Upgrade existing installation” and “There are MediaWiki tables in this database. To upgrade them to MediaWiki 1.25.3, click Continue”&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It worked! Next messages are: “Upgrade complete. You can now start using your wiki. If you want to regenerate your LocalSettings.php file, click the button below. This is not recommended unless you are having problems with your wiki.” That message is a little misleading. You almost certainly &lt;em&gt;do&lt;/em&gt; want to generate a new LocalSettings.php file when doing an upgrade like this. So say yes, leave the database choices as they are, and name your wiki something easily greppable like “ABCD”. Create an admin account, save the generated LocalSettings.php file, and move it to your mediawiki directory.&lt;/p&gt;
&lt;p&gt;At this point, we can do what we came here for: generate a XML dump of the wiki content in the database, so we can import it somewhere else.
We only wanted the actual content, and did not want to worry about the history of the pages, so the command was:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ php maintenance/dumpBackup.php --current &amp;gt; acme.wiki.2005.xml&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;It ran without a hitch. However, close examination showed that it had an amazing amount of unwanted stuff from the
“MediaWiki:” namespace. While there are probably some clever solutions that could be devised to cut them out of the
XML file (either on export, import, or in between), sometimes quick beats clever, and I simply opened the file in an
editor and removed all the “page” sections with a title beginning with “MediaWiki:”. Finally, the file was shipped
to the production wiki running 1.25.3, and the old content was added in a snap:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ php maintenance/importDump.php acme.wiki.2005.xml&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The script will recommend rebuilding the “Recent changes” page by running rebuildrecentchanges.php (can we
get consistentCaps please MW devs?). However, this data is at least 10 years old, and Recent changes only goes back
90 days by default in version 1.25.3 (and even shorter in previous versions). So, one final step:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;## 20 years should be sufficient
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ echo &amp;#39;$wgRCMAxAge = 20 * 365 * 24 * 3600;&amp;#39; &amp;gt;&amp;gt; LocalSettings.php
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ php maintenance/rebuildrecentchanges.php&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Voila! All of the data from this ancient wiki is now in place on a modern wiki!&lt;/p&gt;

      </content>
    </entry>
  
</feed>
