<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Michał Romańczuk</title>
    <description>The latest articles on DEV Community by Michał Romańczuk (@mromanczuk).</description>
    <link>https://dev.to/mromanczuk</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F819139%2F07f785fb-78a5-41aa-bbc9-2bba41c2da1d.jpeg</url>
      <title>DEV Community: Michał Romańczuk</title>
      <link>https://dev.to/mromanczuk</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mromanczuk"/>
    <language>en</language>
    <item>
      <title>Equal or identical. How to compare variables?</title>
      <dc:creator>Michał Romańczuk</dc:creator>
      <pubDate>Wed, 27 Jul 2022 05:50:13 +0000</pubDate>
      <link>https://dev.to/accesto/equal-or-identical-how-to-compare-variables-5d7f</link>
      <guid>https://dev.to/accesto/equal-or-identical-how-to-compare-variables-5d7f</guid>
      <description>&lt;p&gt;&lt;span&gt;==&lt;/span&gt; or &lt;span&gt;===&lt;/span&gt;? How many equal signs to put up so that it is correct and that nobody in the code review has a problem with it? Why is it so tricky in PHP?&lt;/p&gt;

&lt;h2&gt;
  
  
  Type Juggling
&lt;/h2&gt;

&lt;p&gt;In PHP, the type of a variable is defined by how it was used*. Depending on what we have assigned to variable, &lt;strong&gt;it becomes that type&lt;/strong&gt;&lt;sup id="fnref1"&gt;1&lt;/sup&gt;. What does it mean?&lt;/p&gt;

&lt;p&gt;&lt;small&gt;(* From PHP 7 we can define types of method arguments, from 7.4 it is possible to define types of class properties!&lt;sup id="fnref2"&gt;2&lt;/sup&gt;)&lt;/small&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$foo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"1"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;// $foo is string&lt;/span&gt;
&lt;span class="nv"&gt;$foo&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;   &lt;span class="c1"&gt;// $foo is integer&lt;/span&gt;
&lt;span class="nv"&gt;$foo&lt;/span&gt; &lt;span class="o"&gt;*=&lt;/span&gt; &lt;span class="mf"&gt;1.5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// $foo is float&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In example above, string &lt;span&gt;"1"&lt;/span&gt; has been assigned to the variable, so variable is &lt;span&gt;string&lt;/span&gt; type, then &lt;span&gt;integer&lt;/span&gt; &lt;span&gt;0&lt;/span&gt; was added to it so it changes its type to &lt;span&gt;integer&lt;/span&gt;. After that, let's multiply the &lt;span&gt;integer&lt;/span&gt; variable by &lt;span&gt;float&lt;/span&gt; &lt;span&gt;1.5&lt;/span&gt;. The variable is now &lt;span&gt;float&lt;/span&gt; type.&lt;/p&gt;

&lt;p&gt;This example shows how &lt;strong&gt;automatic conversion is performed by operators&lt;/strong&gt;, in this case adding and multiplying. It is enough that one of the sides of the action is of the &lt;span&gt;float&lt;/span&gt; type, then both will be treated as &lt;span&gt;float&lt;/span&gt; and the result is also going to be of this type. The case is similar with &lt;span&gt;integer&lt;/span&gt; although &lt;span&gt;float&lt;/span&gt; is "stronger" and in the case of &lt;span&gt;integer&lt;/span&gt; + &lt;span&gt;float&lt;/span&gt; both sides will be treated as &lt;span&gt;float&lt;/span&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Unequal equal
&lt;/h2&gt;

&lt;p&gt;Let's get to equality, first look at the php documentation&lt;sup id="fnref3"&gt;3&lt;/sup&gt; :&lt;/p&gt;

&lt;blockquote&gt;Equal: $a == $b, TRUE if $a is equal to $b after type juggling.&lt;/blockquote&gt;

&lt;p&gt;Sounds trivial and looks natural. Type juggling results in that, there is no need to think too much about what we compare, &lt;strong&gt;all is done automaticall&lt;/strong&gt;y... ;) but is it?&lt;/p&gt;

&lt;p&gt;Time for a quiz, will the following conditions be met?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"eleven"&lt;/span&gt;
&lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"wtf-1.3e3"&lt;/span&gt;
&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n\r&lt;/span&gt;&lt;span class="s2"&gt;1&lt;/span&gt;&lt;span class="se"&gt;\t&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="mi"&gt;10&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"10 - 10"&lt;/span&gt;
&lt;span class="s2"&gt;"-1300"&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"-1.3e3"&lt;/span&gt;
&lt;span class="mi"&gt;9223372036854775807&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"9223372036854775811"&lt;/span&gt;
&lt;span class="s2"&gt;"1.00000000000000001"&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"1.00000000000000002"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now attention, drums... in &lt;strong&gt;each of above cases it will be &lt;span&gt;true&lt;/span&gt;&lt;/strong&gt;. What just happened here? The, so far, highly intuitive comparison operator has now behaved in a manner rather unnatural to the eye.&lt;/p&gt;

&lt;p&gt;How does the comparison operator actually work? To find out, it's best to look at how it was implemented&lt;sup id="fnref4"&gt;4&lt;/sup&gt;. However, this is not a simple reading, because the code is very complicated there. In short, the operation of &lt;span&gt;==&lt;/span&gt; &lt;strong&gt;depends on the type of its arguments&lt;/strong&gt;. Pairs of argument types are defined. If a given pair is supported (it is known how to compare it), the result of the comparison is returned, but if the given pair of types is not supported, the arguments are cast to another type (&lt;em&gt;Type Juggling&lt;/em&gt;) and are only then compared.&lt;br&gt;
In most cases this works in a predictable manner, but there are pairs of types that can surprise.&lt;/p&gt;
&lt;h3&gt;
  
  
  numeric value - string with a number and something extra
&lt;/h3&gt;

&lt;p&gt;When &lt;span&gt;string&lt;/span&gt; is compared with &lt;span&gt;integer&lt;/span&gt; or &lt;span&gt;float&lt;/span&gt;, then the &lt;span&gt;string&lt;/span&gt; is cast to a numeric value, but this casting can sometimes be surprising.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="mi"&gt;10&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"10 - 10"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this case, the value of &lt;span&gt;string&lt;/span&gt; is cast on to the &lt;span&gt;integer&lt;/span&gt;, if &lt;span&gt;string&lt;/span&gt; starts with a number, then &lt;strong&gt;whatever goes after that number is ignored&lt;/strong&gt;. In this case &lt;span&gt;"10 - 10"&lt;/span&gt; will not be the result of subtraction, so not &lt;span&gt;0&lt;/span&gt; but &lt;span&gt;10&lt;/span&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  numeric value - string with a non-number at the beginning
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"Lorem ipsum dolor sit 0"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If &lt;span&gt;string&lt;/span&gt; does not start with a number, then along with a numeric value &lt;strong&gt;it will always be cast on to &lt;span&gt;0&lt;/span&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  numeric value - string with a big number
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="mi"&gt;9223372036854775807&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"9223372036854775811"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;span&gt;integer&lt;/span&gt; from this comparison is the maximum &lt;span&gt;integer&lt;/span&gt; value - PHP_INT_MAX (on 64-bit platforms), the &lt;span&gt;string&lt;/span&gt; on the other hand includes a number slightly higher, just at 4 ;). In this case the &lt;span&gt;integer&lt;/span&gt; will be cast to &lt;span&gt;float&lt;/span&gt;, so the &lt;span&gt;string&lt;/span&gt; eventually also to &lt;span&gt;float&lt;/span&gt;. Such is the nature of &lt;span&gt;float&lt;/span&gt;.&lt;/p&gt;

&lt;blockquote&gt;
So never trust floating number results to the last digit, 
and do not compare floating point numbers directly for equality.
&lt;/blockquote&gt;

&lt;p&gt;It's a warning from PHP documentation&lt;sup id="fnref5"&gt;5&lt;/sup&gt; so &lt;strong&gt;it's better not to compare &lt;span&gt;float&lt;/span&gt; values directly&lt;/strong&gt;, but use dedicated functions:&lt;/p&gt;

&lt;h3&gt;
  
  
  numeric value - string with a number and white characters
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="mf"&gt;1.0&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n\r&lt;/span&gt;&lt;span class="s2"&gt;1&lt;/span&gt;&lt;span class="se"&gt;\t&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;White characters are ignored when casting to a numeric value. If you look at the example above you'll find &lt;strong&gt;one &lt;span&gt;"\n\r&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;\t"&lt;/span&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  string - string
&lt;/h3&gt;

&lt;p&gt;When comparing two &lt;span&gt;string&lt;/span&gt; values it would be natural if it were &lt;strong&gt;&lt;span&gt;true&lt;/span&gt; when they are the same&lt;/strong&gt; ;) but what does it mean "the same".&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;1"&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\r&lt;/span&gt;&lt;span class="s2"&gt;1"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// true&lt;/span&gt;
&lt;span class="s2"&gt;"1.0"&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\t&lt;/span&gt;&lt;span class="s2"&gt;1"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// true&lt;/span&gt;
&lt;span class="s2"&gt;"1e0"&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"1"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;   &lt;span class="c1"&gt;// true&lt;/span&gt;
&lt;span class="s2"&gt;"-0.5e-2"&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"-0.005"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;// true&lt;/span&gt;
&lt;span class="s2"&gt;"1.00000000000000001"&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"1.00000000000000009"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// true&lt;/span&gt;

&lt;span class="s2"&gt;"10"&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"0xA"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;// true  PHP &amp;lt; 7&lt;/span&gt;
                 &lt;span class="c1"&gt;// false PHP &amp;gt;= 7&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When comparing &lt;span&gt;string&lt;/span&gt; type arguments, at the beginning both arguments are cast to a numeric value. If it succeeds, they are compared as numerical values. Interestingly, &lt;strong&gt;PHP can do a lot of tricks when searching for a number in string&lt;/strong&gt;&lt;sup id="fnref6"&gt;6&lt;/sup&gt;. Scientific notation or leading whitespace are not a problem, as well as hexadecimal notation up to version 7.0.&lt;/p&gt;

&lt;h3&gt;
  
  
  object - object
&lt;/h3&gt;

&lt;p&gt;If it was too clear and predictable up to now, it works a little different when comparing objects.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Example&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nv"&gt;$property&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nv"&gt;$foo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Example&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nv"&gt;$bar&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Example&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nv"&gt;$bar&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;property&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nv"&gt;$foo&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nv"&gt;$bar&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;    &lt;span class="c1"&gt;// true&lt;/span&gt;
&lt;span class="nv"&gt;$foo&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nv"&gt;$bar&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;   &lt;span class="c1"&gt;// false&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Considering what I wrote earlier, everything in the example above is correct. The &lt;span&gt;$foo&lt;/span&gt; and &lt;span&gt;$bar&lt;/span&gt; variables are of the same type (Example class), their property (&lt;span&gt;$property&lt;/span&gt;) in &lt;span&gt;$foo&lt;/span&gt; is equal to 1 (&lt;span&gt;integer&lt;/span&gt;) and in &lt;span&gt;\$bar&lt;/span&gt; is equal to 1.0 (&lt;span&gt;float&lt;/span&gt;). Thus, the result of the &lt;span&gt;==&lt;/span&gt; comparison is &lt;span&gt;true&lt;/span&gt;. Because the values match, types don't have to (&lt;span&gt;integer&lt;/span&gt; vs. &lt;span&gt;float&lt;/span&gt;). &lt;strong&gt;For &lt;span&gt;===&lt;/span&gt; the result is &lt;span&gt;false&lt;/span&gt;&lt;/strong&gt; because the class property types do not match?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$baz&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Example&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nv"&gt;$qux&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Example&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nv"&gt;$baz&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nv"&gt;$qux&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;    &lt;span class="c1"&gt;// true&lt;/span&gt;
&lt;span class="nv"&gt;$baz&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nv"&gt;$qux&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;   &lt;span class="c1"&gt;// false&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What happened here? Both variables are of the same type and their property is of the same value and of the same type, and &lt;strong&gt;still the result of &lt;span&gt;===&lt;/span&gt; is &lt;span&gt;false&lt;/span&gt;&lt;/strong&gt;.&lt;br&gt;
When comparing objects, identity (&lt;span&gt;===&lt;/span&gt;) will return as &lt;span&gt;true&lt;/span&gt; only if the same instance of the same class is compared&lt;sup id="fnref7"&gt;7&lt;/sup&gt; . Yes. It is not enough for a variable's type (class) and all its properties to match, it must be same object. Comparison &lt;span&gt;==&lt;/span&gt; will compare the object type and compare all its properties (also with &lt;span&gt;==&lt;/span&gt;).&lt;/p&gt;

&lt;h2&gt;
  
  
  So how should I compare?
&lt;/h2&gt;

&lt;p&gt;In the examples I showed, it is clear that using the &lt;span&gt;==&lt;/span&gt; comparison, sometimes may give a result different from what we expect. How to deal with this? Every time you write &lt;span&gt;==&lt;/span&gt; (two equal signs), a red light should start flashing. We should always use the identity operator &lt;span&gt;===&lt;/span&gt; (three equal signs), which also compares the type of the variable and does not cast anything before that. Well, almost always.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The only time we can use the equality &lt;span&gt;==&lt;/span&gt; instead of identity operator &lt;span&gt;===&lt;/span&gt; is when we do it intentionally and we have reasons for that&lt;/strong&gt;!&lt;/p&gt;




&lt;ol&gt;

&lt;li id="fn1"&gt;
&lt;p&gt;&lt;a href="https://www.php.net/manual/en/language.types.type-juggling.php"&gt;&lt;/a&gt;&lt;a href="https://www.php.net/manual/en/language.types.type-juggling.php"&gt;https://www.php.net/manual/en/language.types.type-juggling.php&lt;/a&gt; ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn2"&gt;
&lt;p&gt;&lt;a href="https://www.php.net/manual/en/migration74.new-features.php"&gt;&lt;/a&gt;&lt;a href="https://www.php.net/manual/en/migration74.new-features.php"&gt;https://www.php.net/manual/en/migration74.new-features.php&lt;/a&gt; ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn3"&gt;
&lt;p&gt;&lt;a href="https://www.php.net/manual/en/language.operators.comparison.php"&gt;&lt;/a&gt;&lt;a href="https://www.php.net/manual/en/language.operators.comparison.php"&gt;https://www.php.net/manual/en/language.operators.comparison.php&lt;/a&gt; ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn4"&gt;
&lt;p&gt;&lt;a href="https://github.com/php/php-src/blob/PHP-7.4.2/Zend/zend_operators.c#L2022"&gt;&lt;/a&gt;&lt;a href="https://github.com/php/php-src/blob/PHP-7.4.2/Zend/zend_operators.c#L2022"&gt;https://github.com/php/php-src/blob/PHP-7.4.2/Zend/zend_operators.c#L2022&lt;/a&gt; ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn5"&gt;
&lt;p&gt;&lt;a href="https://www.php.net/manual/en/language.types.float.php"&gt;&lt;/a&gt;&lt;a href="https://www.php.net/manual/en/language.types.float.php"&gt;https://www.php.net/manual/en/language.types.float.php&lt;/a&gt; ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn6"&gt;
&lt;p&gt;&lt;a href="https://github.com/php/php-src/blob/PHP-7.4.2/Zend/zend_operators.c#L2908"&gt;&lt;/a&gt;&lt;a href="https://github.com/php/php-src/blob/PHP-7.4.2/Zend/zend_operators.c#L2908"&gt;https://github.com/php/php-src/blob/PHP-7.4.2/Zend/zend_operators.c#L2908&lt;/a&gt; ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn7"&gt;
&lt;p&gt;&lt;a href="https://www.php.net/manual/en/language.oop5.object-comparison.php"&gt;&lt;/a&gt;&lt;a href="https://www.php.net/manual/en/language.oop5.object-comparison.php"&gt;https://www.php.net/manual/en/language.oop5.object-comparison.php&lt;/a&gt; ↩&lt;/p&gt;
&lt;/li&gt;

&lt;/ol&gt;

</description>
      <category>php</category>
      <category>programming</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Boy scout rule in 6 examples - the basic principle of web development</title>
      <dc:creator>Michał Romańczuk</dc:creator>
      <pubDate>Mon, 20 Jun 2022 09:23:56 +0000</pubDate>
      <link>https://dev.to/accesto/boy-scout-rule-in-6-examples-the-basic-principle-of-web-development-nnl</link>
      <guid>https://dev.to/accesto/boy-scout-rule-in-6-examples-the-basic-principle-of-web-development-nnl</guid>
      <description>&lt;p&gt;Not everyone was a scout in his youth. Most of programmers, however, have probably heard of the &lt;strong&gt;Boy Scout Rule in software development&lt;/strong&gt;. But can you name a few of the activities that are part of that rule? When I started to explore this topic, looking for what exactly needs to be done to say &lt;strong&gt;"I just applied the boy scout rule"&lt;/strong&gt;, I only found one thing: "Leave your code better than you found it" - everyone quotes Uncle Bob. But what does it mean? What am I supposed to do exactly?&lt;/p&gt;

&lt;p&gt;In scouting it is easier, everyone can see if the surroundings are clean, what can be cleaned to make it cleaner, but in the code? I will try to show you a few examples from my daily work where I try to stick to this principle.&lt;/p&gt;

&lt;p&gt;Most of us use some IDE, they are getting smarter and in many cases, they keep the code clean, suggest better solutions, "clean up" automatically with one click. However, &lt;strong&gt;it's worth keeping an eye on what's happening in the code&lt;/strong&gt; and not rely entirely on the IDE.&lt;/p&gt;

&lt;h2&gt;
  
  
  The campground, not an entire forest!
&lt;/h2&gt;

&lt;p&gt;I am allergic to multiple blank lines. When doing a code review, I also pay attention to what the code looks like. &lt;strong&gt;The code must be readable&lt;/strong&gt;, multiple blank lines, in my opinion, are disturbing. So I sometimes leave a comment in CR like "one blank line is enough (everywhere)". One day my colleague got it a little bit differently than I meant. That merge request before my first review contained 7 changed files. What was my surprise when I saw it after applying the changes - 350 files changed… he removed multiple lines from the whole project. You can not do this, such changes introduce a huge disruption to the code, the MR becomes completely unreadable. &lt;/p&gt;

&lt;p&gt;You can't go overboard with cleaning. “Leave the campground cleaner than you found it”, &lt;strong&gt;the campground, not an entire forest!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Code cleanup should be limited to the files we work with. If we add a new method to the class and we notice something that can be improved/cleaned up "next to" - this is the place where we should apply the Boy Scout Rule. &lt;/p&gt;

&lt;h2&gt;
  
  
  Examples in practice
&lt;/h2&gt;

&lt;h3&gt;
  
  
  a) deprecations
&lt;/h3&gt;

&lt;p&gt;If you use a smart IDE, it probably detects deprecated methods. The IDE I'm using strikes out deprecated names. Below, you can see crossed-out annotations &lt;em&gt;@ Route&lt;/em&gt; and &lt;em&gt;@Method&lt;/em&gt;. Something like that is very noticeable, it's hard to miss it.&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--52eH83IM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vqgnbrqui0yobzsiow5p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--52eH83IM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vqgnbrqui0yobzsiow5p.png" alt="Deprecations example 1 - before" width="502" height="220"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Inside the classes of these deprecated annotations, I found lines like this:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--tEwhzg0K--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/c7de32dfju9xu0bal1n6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--tEwhzg0K--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/c7de32dfju9xu0bal1n6.png" alt="Deprecations example 1 - hint 1" width="880" height="185"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--83v6kqWG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/atbeek3lqman90kf84gl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--83v6kqWG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/atbeek3lqman90kf84gl.png" alt="Deprecations example 1 - hint 2" width="880" height="241"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is a very easy change to apply. They say what exactly we have to do. Just like in the screen below. A few seconds change and one thing less hurts the eyes.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Dvv4x6RE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/a5cgcni4vl3gpnbaxq08.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Dvv4x6RE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/a5cgcni4vl3gpnbaxq08.png" alt="Deprecations example 1 - after" width="708" height="192"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It was an outdated annotation, now an example of outdated method. More crossed letters below. Method &lt;em&gt;setData()&lt;/em&gt;. Just like a moment ago. It cannot be overlooked.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--xxfxcxOL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/j4m6a3fzyvm9mrvzciku.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--xxfxcxOL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/j4m6a3fzyvm9mrvzciku.png" alt="Deprecations example 2 -  before" width="855" height="182"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A good scout will not ignore it. &lt;strong&gt;It is like a plastic bottle on the ground&lt;/strong&gt; in the campground, that needs to be cleaned up. So just check this method, maybe the author left a hint. And I did it:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--FBnceCwQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/om537gu4c6ygyan158fy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--FBnceCwQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/om537gu4c6ygyan158fy.png" alt="Deprecations example 2 - hint" width="880" height="217"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Again, we have exactly what to replace. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Pmt3c1WL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/it7msy6yyhxn21i8pxqr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Pmt3c1WL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/it7msy6yyhxn21i8pxqr.png" alt="Deprecations example 2 - after" width="731" height="370"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And the last one. We're using some vendor class here that is deprecated.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--6iEAxvEc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/uyj73z024w4ha3lw7b3y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--6iEAxvEc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/uyj73z024w4ha3lw7b3y.png" alt="Deprecations example 3 - before" width="880" height="157"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Just open it:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--HWE8bdWw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/e28l6vcx2s8pzbcpem3y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--HWE8bdWw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/e28l6vcx2s8pzbcpem3y.png" alt="Deprecations example 3 - hint" width="880" height="116"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And another quick change&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--7XgzS1kF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kempzztpvl1g0xjx9aav.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--7XgzS1kF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kempzztpvl1g0xjx9aav.png" alt="Deprecations example 3 - after" width="880" height="131"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Of course, if you are using a less wise IDE, looking for deprecations is not that easy. Nobody in their right mind opens every class and method they use to look for information about deprecations. We just use what we know, usually without much thought. Therefore, &lt;strong&gt;it is worth using tools that support our work&lt;/strong&gt;. &lt;/p&gt;

&lt;h3&gt;
  
  
  b) unused code
&lt;/h3&gt;

&lt;p&gt;The code changes very often, especially in the early stages of development. With changes, sometimes you miss to delete an unused import, variable, or method.&lt;/p&gt;

&lt;p&gt;A smart IDE detects unused code, for example, turns unused private methods to gray. This also works for unused imports, unused private properties and parameters.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--4wHE3eag--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/h95gsuh0grkq9gl7rn16.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--4wHE3eag--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/h95gsuh0grkq9gl7rn16.png" alt="Unused code - example 1" width="763" height="87"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--gbl2FfMS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mbqnaxnqtospazhufrvu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--gbl2FfMS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mbqnaxnqtospazhufrvu.png" alt="Unused code - example 2" width="312" height="118"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--om8Xlfih--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cl5fun7x8a5foitv8x24.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--om8Xlfih--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cl5fun7x8a5foitv8x24.png" alt="Unused code - example 3" width="479" height="90"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But you have to be careful, &lt;strong&gt;I never trust the IDE completely&lt;/strong&gt;, it is better to double-check that deleting a piece of code won't break something. Of course, your code is fully test covered, so you can be sure nothing's broken ;).&lt;/p&gt;

&lt;p&gt;It's not that easy with public methods, they don't turn gray automatically ;). You need to be more careful with them. When you stop using some public method, let's just check to see if it’s used somewhere else. If not and no one needs it anymore, let's get rid of it. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;We should remove unused code.&lt;/strong&gt; That's what we use version-control systems for, so we don't have to see "maybe it will be useful someday" unused code. (YAGNI)&lt;/p&gt;

&lt;h3&gt;
  
  
  c) naming
&lt;/h3&gt;

&lt;p&gt;We have to name many things in our work. Variables, methods, classes, everything must have a name. What should a good name look like? Not too long, not too short, and clear to anyone who can see it. It is not easy at all. I believe that &lt;strong&gt;this is a key part of our work&lt;/strong&gt;. A badly named variable can mess up a lot and add a lot of work to others who later have to get into our code. It should be understandable without thinking. After reading the name of the method, the names of its arguments, and what it returns, we should know exactly what it does, without looking inside. &lt;/p&gt;

&lt;p&gt;When I see the name of the variable $a, $var, $arr or $array in the code review, I immediately get a fever. &lt;/p&gt;

&lt;p&gt;And below is an example of a variable with the two-letter name "ac". To know what it will contain, you need to look where something is assigned, it is easy to find if it is a constructor, and not always it will be there. It is obvious to the author of this code at the time of writing that "ac" stands for "authorizationChecker". But not everyone will figure it out, even the author after a while may not be sure what "ac" meant.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--D_0qDS-P--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ni1165u1qnkoufsd340w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--D_0qDS-P--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ni1165u1qnkoufsd340w.png" alt="Naming - before" width="551" height="126"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A good scout, seeing such a variable, will quickly change its name to clean up a bit. For example, such a change is enough:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--2OXtZkBf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/b3ryif4q0n0qcvuri79o.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--2OXtZkBf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/b3ryif4q0n0qcvuri79o.png" alt="Naming - after" width="687" height="127"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Don't save on letters or you'll pay for it later&lt;/strong&gt; trying to understand what the abbreviations mean.&lt;/p&gt;

&lt;h3&gt;
  
  
  d) code formatting
&lt;/h3&gt;

&lt;p&gt;The worst thing I have seen about improving code formatting is to use autoformat in the IDE all over the project directory ("not an entire forest"!). Let's clean around the place where we work, but not too far, &lt;strong&gt;one file or one method is enough&lt;/strong&gt;. Someone who reviews the code should be able to focus on important things and not scroll through hundreds of non-essential changes.&lt;/p&gt;

&lt;p&gt;"My formatting is the clearest and most readable" - every programmer. Everyone has their own taste and habits, so you need to agree on the details of code formatting in your team. You probably have your standards in the project you work in. However, there is one thing everyone should follow - the PSR-12 (If you missed, the PSR-2 is deprecated).&lt;/p&gt;

&lt;p&gt;Let’s see some examples.&lt;/p&gt;

&lt;h4&gt;
  
  
  one-line IF.
&lt;/h4&gt;

&lt;p&gt;Very unreadable:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--CrgmGzoN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0h1g88agdpxli8cmel3y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--CrgmGzoN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0h1g88agdpxli8cmel3y.png" alt="Code formatting - example 1 - before" width="671" height="117"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Just add a few spaces and new lines&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s---b0hpWSy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dtve6nokdi3rraq2w6jv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s---b0hpWSy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dtve6nokdi3rraq2w6jv.png" alt="Code formatting - example 1 - after 1" width="345" height="140"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;or a short IF syntax&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--pIpck9UC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/u2fzq6rg3ggh9m27biy9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--pIpck9UC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/u2fzq6rg3ggh9m27biy9.png" alt="Code formatting - example 1 - after 2" width="345" height="106"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  multiple blank lines
&lt;/h4&gt;

&lt;p&gt;I don't know why, but very common&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--11IVHupM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wq7xt1lu6rwq1jztlrta.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--11IVHupM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wq7xt1lu6rwq1jztlrta.png" alt="Code formatting - example 2 - before" width="378" height="103"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Just be a good scout and remove unnecessary blank lines&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--RhaGEXgz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/08ntnehti8gkg88aunkx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--RhaGEXgz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/08ntnehti8gkg88aunkx.png" alt="Code formatting - example 2 - after" width="349" height="57"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  strange indentation in the code.
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--D0V_QUMf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/y07qxg6fzidtb0xvi0he.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--D0V_QUMf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/y07qxg6fzidtb0xvi0he.png" alt="Code formatting - example 3 - before" width="362" height="146"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Are you uncomfortable reading it too? When you see something like this in the code, &lt;strong&gt;you can easily do the right thing and correct the indentation&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--kW5j1gr8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nqmje16461361oc2huy3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--kW5j1gr8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nqmje16461361oc2huy3.png" alt="Code formatting - example 3 - after" width="345" height="140"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  all other things incompatible with PSR-12
&lt;/h4&gt;

&lt;p&gt;Boy Scout is not looking for whom to blame, but clean code himself, leaving the code better than he found (DO NOT apply this rule in code review, such things must be eliminated immediately! ;))&lt;/p&gt;

&lt;h3&gt;
  
  
  e) complex conditions
&lt;/h3&gt;

&lt;p&gt;How many times have you wasted time trying to understand a complicated if condition? For me - too much. Worse, with many conditions, it is often easy to get confused (if you have tests, you are a lucky scout)&lt;/p&gt;

&lt;p&gt;I made up an example below. let's say it's a method that handle some response. It first checks if the response is valid, if so, does something with it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--r2tmct7C--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mmrvg81ni5xdcu9e7jcf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--r2tmct7C--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mmrvg81ni5xdcu9e7jcf.png" alt="Complex conditions - before" width="531" height="211"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It is not overly complicated to analyze, but it does take a moment to analyze what is being checked here. &lt;/p&gt;

&lt;p&gt;A good scout will find a few things here that he can clean up so that others will enjoy reading the code. But the most important thing is to &lt;strong&gt;separate the entire condition&lt;/strong&gt; into a separate method whose name will clearly say what the condition checks.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--mMv7kTtx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0xlse45bp545i4m59949.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--mMv7kTtx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0xlse45bp545i4m59949.png" alt="Complex conditions - after" width="523" height="312"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Of course, in this example condition, many other things can be improved ;). But the point here was that &lt;strong&gt;we took the time to understand what it checks, so let's make it easy for other people&lt;/strong&gt; not to waste that time. &lt;/p&gt;

&lt;h3&gt;
  
  
  f) code duplication
&lt;/h3&gt;

&lt;p&gt;Why do we find duplicate code in projects? If something works in one place, it will work in another ;). But there is one problem. If you want to change or correct something later, you must remember to do it in all places where you copied the code fragment. &lt;strong&gt;Mindless copy-pasting is unacceptable&lt;/strong&gt; and no one should do it. But we often see that it happens, all the time. When a good scout sees duplicate code, he just clean it. The smart IDE notices that the code is duplicated, it should alert you.&lt;/p&gt;

&lt;p&gt;Here is an example to show what I'm talking about&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--V6bBn4Xy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/g4yrpnn6p6520488x99s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--V6bBn4Xy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/g4yrpnn6p6520488x99s.png" alt="Code duplications - before" width="880" height="618"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We have two methods here, even if you search, you won't notice the difference other than the function name. &lt;/p&gt;

&lt;p&gt;The first method sends an email with some positive decision, and the second method sends an email with some negative decision. For some reason, instead of extracting common code, someone just copied it. &lt;/p&gt;

&lt;p&gt;Then, you have the task to change the title of sent emails, and you noticed what it looks like. Now you have two options what to do. First one is to only change the title in both places. If it works, why not? Well, NO! &lt;strong&gt;A good scout would do more than that. He'd clean up that piece of code.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--r1O7M2bv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7abxof1mc3o3jz1j9qsf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--r1O7M2bv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7abxof1mc3o3jz1j9qsf.png" alt="Code duplications - after" width="880" height="511"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It doesn't take much time, and the code is a bit cleaner and easier to maintain.&lt;/p&gt;

&lt;h3&gt;
  
  
  g) what else?
&lt;/h3&gt;

&lt;p&gt;Anything that will improve the readability of the code or its maintenance. You are not doing it for others, you are doing it for yourself in the future. &lt;br&gt;
Doing more serious refacotring (eg. to ensure &lt;a href="https://accesto.com/blog/solid-php-solid-principles-in-php/"&gt;SOLID princinples&lt;/a&gt;) may not be a quick job. But all the examples shown above can be done in less than a minute. This is well invested time. It will pay off in the future. &lt;/p&gt;

&lt;h2&gt;
  
  
  Need further help?
&lt;/h2&gt;

&lt;p&gt;Boy Scout rule is a good practice that helps to keep the &lt;a href="https://accesto.com/blog/technical-debt-the-number-one-reason-why-software-development-projects-get-derailed/"&gt;technical debt&lt;/a&gt; low. But sometimes it is just not enough. If you are no longer able to cope with the code of your web application just contact us. We help our clients fight the &lt;a href="https://accesto.com/blog/handle-technical-debt-in-legacy-application-4-possible-scenarios/"&gt;technical debt in legacy PHP &amp;amp; Javascript applications&lt;/a&gt;. &lt;br&gt;
__&lt;/p&gt;

</description>
      <category>php</category>
      <category>codequality</category>
    </item>
    <item>
      <title>Simple Docker setup for Symfony project</title>
      <dc:creator>Michał Romańczuk</dc:creator>
      <pubDate>Fri, 15 Apr 2022 13:43:47 +0000</pubDate>
      <link>https://dev.to/accesto/simple-docker-setup-for-symfony-project-1p9e</link>
      <guid>https://dev.to/accesto/simple-docker-setup-for-symfony-project-1p9e</guid>
      <description>&lt;p&gt;It is no longer the case at Accesto that we work on projects without Docker. Of course, projects are not born dockerized, someone has to do it. Sometimes I do. I am a PHP developer and I work the most with &lt;a href="https://accesto.com/blog/what-is-php-framework-symfony-explained-for-executives/"&gt;Symfony framework&lt;/a&gt; on a daily basis. In this article, I will present an example development &lt;strong&gt;Docker configuration for the Symfony&lt;/strong&gt; project.&lt;/p&gt;

&lt;h2&gt;
  
  
  So let's use a Docker in a Symfony project.
&lt;/h2&gt;

&lt;p&gt;When it comes to Docker setup, &lt;strong&gt;there is no one-size-fits-all solution&lt;/strong&gt;. It always depends on particular project needs. But some good practices should be followed, for example, those described in the official &lt;a rel="nofollow" href="https://docs.docker.com/develop/develop-images/dockerfile_best-practices/"&gt;Docker documentation&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's imagine we take over an existing project, a simple one, &lt;strong&gt;PHP, Symfony, MySQL database&lt;/strong&gt;, that's all. But there is no Docker. So instead of installing a local web server, the appropriate PHP version, the appropriate database engine, etc., the first thing to do is ... dockerize!&lt;/p&gt;

&lt;p&gt;Why should I spend time on dockerizing first? Because I will do it once, and everyone working on this project will benefit from it. You can read more about why it is worth using the Docker in &lt;a href="https://accesto.com/blog/what-is-docker-and-why-to-use-it/"&gt;the article&lt;/a&gt; of our CEO Piotr. &lt;/p&gt;

&lt;p&gt;I assume you already have &lt;a rel="nofollow" href="https://docs.docker.com/engine/install/"&gt;Docker Engine&lt;/a&gt; and &lt;a rel="nofollow" href="https://docs.docker.com/compose/install/"&gt;Docker Compose&lt;/a&gt; installed, if not, you should do so.&lt;/p&gt;

&lt;p&gt;Now let's focus on what we need. This project is currently running on Apache, PHP 7.4 and MySQL 5.7.&lt;/p&gt;

&lt;h2&gt;
  
  
  Configure Docker using docker-compose.yml
&lt;/h2&gt;

&lt;p&gt;I have added the &lt;em&gt;docker-compose.yml&lt;/em&gt; file in the main project directory, and I defined our first container in it - the one with the database. File &lt;em&gt;docker-compose.yml&lt;/em&gt; is used by (Docker) Compose to manage and run Docker applications.&lt;/p&gt;

&lt;p&gt;At the beginning of the file, you can (from version v1.27.0 it is optional) define the version of the configuration. Different versions of Docker Engine + Docker Compose work with different versions of the configuration, which you can &lt;a rel="nofollow" href="https://docs.docker.com/compose/compose-file/"&gt;check here&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;3.8'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  MySql database in Docker container
&lt;/h2&gt;

&lt;p&gt;Let's start by defining the first service, the database. First, the name, I chose “db”:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;3.8'&lt;/span&gt;

&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;db&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After that I had to define what &lt;em&gt;db&lt;/em&gt; service is. For example, we can use the &lt;em&gt;image&lt;/em&gt; option. As an &lt;em&gt;image&lt;/em&gt;, I had to select the appropriate Docker image. &lt;strong&gt;We can use ready-made images&lt;/strong&gt; available on the web for this. Most of the tools come with Docker images. And to search for these images, we can use the &lt;a rel="nofollow" href="https://hub.docker.com/"&gt;search engine&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In my case, the project uses MySql version 5.7 and such an image is also available so I could just use its name:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;3.8'&lt;/span&gt;

&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;db&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mysql:5.7&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As per the &lt;a rel="nofollow" href="https://hub.docker.com/_/mysql"&gt;documentation&lt;/a&gt; for this image, some things in this container can be &lt;strong&gt;configured using environment variables&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;One variable is mandatory, this is &lt;code&gt;MYSQL_ROOT_PASSWORD&lt;/code&gt;. As you might guess, this is the MySql root superuser password. But there are a few other variables that are helpful, I used the following: &lt;code&gt;MYSQL_DATABASE&lt;/code&gt;, a database with this name will be created during image startup. If we define &lt;code&gt;MYSQL_USER&lt;/code&gt;, &lt;code&gt;MYSQL_PASSWORD&lt;/code&gt;, a user with this name and password will be created, and he will have granted superuser access to the database defined in &lt;code&gt;MYSQL_DATABASE&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;3.8'&lt;/span&gt;

&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;db&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mysql:5.7&lt;/span&gt;
        &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;MYSQL_ROOT_PASSWORD=somerootpass&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;MYSQL_PASSWORD=somepass&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;MYSQL_DATABASE=dockerizeme_db&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;MYSQL_USER=someuser&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Symfony Docker image
&lt;/h2&gt;

&lt;p&gt;But &lt;strong&gt;there are no ready-made images for everything&lt;/strong&gt;. For example, there is no ready image for a web server configured according to the needs of our application.&lt;/p&gt;

&lt;p&gt;So while defining the container for our web server with PHP, I did not use the &lt;em&gt;image&lt;/em&gt; option with the name of the ready image, but I used the &lt;em&gt;build&lt;/em&gt; option with directory (.) that contains the file (&lt;em&gt;Dockerfile&lt;/em&gt;) from which the image will be built. But about &lt;em&gt;Dockerfile&lt;/em&gt; soon.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;3.8'&lt;/span&gt;

&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;db&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# cut for readability, see above&lt;/span&gt;
    &lt;span class="na"&gt;web&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Going further. It would be nice to add a basic Apache configuration. I added the &lt;em&gt;docker&lt;/em&gt; directory in the main project directory, where I will keep files related to the Docker environment configuration. Then, in that directory, I added the &lt;em&gt;apache.conf&lt;/em&gt; file. This file is a very basic configuration of a web server.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;VirtualHost *:80&amp;gt;
    DocumentRoot /var/www/public

    &amp;lt;Directory /var/www/public&amp;gt;
        AllowOverride None
        Order Allow,Deny
        Allow from All

        &amp;lt;IfModule mod_rewrite.c&amp;gt;
            RewriteEngine On
            RewriteCond %{REQUEST_FILENAME} !-f
            RewriteRule ^(.*)$ index.php [QSA,L]
        &amp;lt;/IfModule&amp;gt;
    &amp;lt;/Directory&amp;gt;
&amp;lt;/VirtualHost&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's make our container accessible "from the outside". I used the &lt;em&gt;ports&lt;/em&gt; option for this. Port 80 from the inside of the container will be exposed to the outside at port 8080.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;3.8'&lt;/span&gt;

&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;db&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# cut for readability, see above&lt;/span&gt;
    &lt;span class="na"&gt;web&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# cut for readability, see above&lt;/span&gt;
        &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;8080:80&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Why didn't I put it on port 80 but 8080? Because we often have Apache running locally on port 80, so we would have a conflict and the Docker container wouldn't start.&lt;/p&gt;

&lt;p&gt;Finally docker-compose.yml looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;3.8'&lt;/span&gt;

&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;db&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mysql:5.7&lt;/span&gt;
        &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;MYSQL_ROOT_PASSWORD=somerootpass&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;MYSQL_PASSWORD=somepass&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;MYSQL_DATABASE=dockerizeme_db&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;MYSQL_USER=someuser&lt;/span&gt;
    &lt;span class="na"&gt;web&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.&lt;/span&gt;
        &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;8080:80&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  &lt;em&gt;Dockerfile&lt;/em&gt; for Apache and PHP part
&lt;/h2&gt;

&lt;p&gt;Now we need to take a step back. Let's go into the details of our web image. To define it, I added a &lt;em&gt;Dockerfile&lt;/em&gt; file to the main project directory. Of course, we will use the ready-made public image as a starting point, and slightly adjust it to our needs. I used the &lt;em&gt;php:7.4-apache&lt;/em&gt; image as a base.&lt;/p&gt;

&lt;p&gt;At the beginning of &lt;em&gt;Dockerfile&lt;/em&gt;, I defined the Docker image which is our base:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; php:7.4-apache&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then I enabled Apache &lt;code&gt;mod_rewrite&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;RUN &lt;/span&gt;a2enmod rewrite
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After that I did the classic update, several packages installed such as &lt;code&gt;libzip-dev&lt;/code&gt; (needed for zip which is needed for composer) &lt;code&gt;git wget&lt;/code&gt; via apt-get install. And some extensions such as &lt;code&gt;pdo mysqli pdo_mysql zip&lt;/code&gt; via docker-php-ext-install mechanism, more about that you can find in base image &lt;a rel="nofollow" href="https://hub.docker.com/_/php"&gt;documentation&lt;/a&gt;. In the meantime, I also deleted files that are unnecessary in the container, they would only take up space.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;RUN &lt;/span&gt;apt-get update &lt;span class="se"&gt;\
&lt;/span&gt;  &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; libzip-dev git wget &lt;span class="nt"&gt;--no-install-recommends&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;  &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; apt-get clean &lt;span class="se"&gt;\
&lt;/span&gt;  &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; /var/lib/apt/lists/&lt;span class="k"&gt;*&lt;/span&gt; /tmp/&lt;span class="k"&gt;*&lt;/span&gt; /var/tmp/&lt;span class="k"&gt;*&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;docker-php-ext-install pdo mysqli pdo_mysql zip&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The application uses &lt;a rel="nofollow" href="https://getcomposer.org/"&gt;Composer&lt;/a&gt; to manage dependencies in our application, so I installed in the container:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;RUN &lt;/span&gt;wget https://getcomposer.org/download/2.0.9/composer.phar &lt;span class="se"&gt;\ &lt;/span&gt;
    &amp;amp;&amp;amp; mv composer.phar /usr/bin/composer &amp;amp;&amp;amp; chmod +x /usr/bin/composer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I copied the Apache configuration file to the appropriate place in the container, and our project files to &lt;em&gt;/var/www&lt;/em&gt; in the container:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; docker/apache.conf /etc/apache2/sites-enabled/000-default.conf&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . /var/www&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Instruction WORKDIR sets the working directory for other instructions, for example for the RUN command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /var/www&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And last command to run Apache in the container foreground:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["apache2-foreground"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To sum up, we have such &lt;em&gt;Dockerfile&lt;/em&gt; for web container:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; php:7.4-apache&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;a2enmod rewrite

&lt;span class="k"&gt;RUN &lt;/span&gt;apt-get update &lt;span class="se"&gt;\
&lt;/span&gt;  &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; libzip-dev git wget &lt;span class="nt"&gt;--no-install-recommends&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;  &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; apt-get clean &lt;span class="se"&gt;\
&lt;/span&gt;  &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; /var/lib/apt/lists/&lt;span class="k"&gt;*&lt;/span&gt; /tmp/&lt;span class="k"&gt;*&lt;/span&gt; /var/tmp/&lt;span class="k"&gt;*&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;docker-php-ext-install pdo mysqli pdo_mysql zip&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;wget https://getcomposer.org/download/2.0.9/composer.phar &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;mv &lt;/span&gt;composer.phar /usr/bin/composer &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;chmod&lt;/span&gt; +x /usr/bin/composer

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; docker/apache.conf /etc/apache2/sites-enabled/000-default.conf&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . /var/www&lt;/span&gt;

&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /var/www&lt;/span&gt;

&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["apache2-foreground"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Is that all? &lt;strong&gt;As for the minimum, yes!&lt;/strong&gt; Now if I run the &lt;code&gt;docker-compose up -d&lt;/code&gt; in project directory in the console, the containers will be created and the whole project will start running.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--YNFb6l2C--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4fp88dp721pbzuk5fbzp.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--YNFb6l2C--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4fp88dp721pbzuk5fbzp.gif" alt="Run Symfony on docker" width="571" height="273"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But wait a minute. Let's see if it really works:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--bN4UJCtf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zayda6x9xjju6kv0l612.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--bN4UJCtf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zayda6x9xjju6kv0l612.png" alt="Run Symfony on docker 2" width="880" height="183"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Of course not ;). Our project needs a few more steps. For example, what about &lt;code&gt;composer install&lt;/code&gt;? Database migrations? Now we can describe these steps in the Readme of the project. It would be something like “enter the container (&lt;em&gt;docker-compose exec web bash&lt;/em&gt;) and run composer install in the container, then run migrations (&lt;em&gt;bin/console doc:mig:mig&lt;/em&gt;)”.&lt;/p&gt;

&lt;h2&gt;
  
  
  Improvements in startup a dockerized project
&lt;/h2&gt;

&lt;p&gt;We can try to "automate" these steps, for example by using Entrypoint. Using Entrypoint is not the best solution, but it will &lt;strong&gt;facilitate development&lt;/strong&gt; at this stage. Let's assume that I want to run every time the container is started:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;composer install&lt;/li&gt;
&lt;li&gt;new database migrations to be loaded
&lt;/li&gt;
&lt;li&gt;fresh fixtures (dummy/test data) in the database.
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So I added &lt;em&gt;entrypoint.sh&lt;/em&gt; in &lt;em&gt;docker&lt;/em&gt; directory with a simple script to do it&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;

composer &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt;
bin/console doc:mig:mig &lt;span class="nt"&gt;--no-interaction&lt;/span&gt;
bin/console doc:fix:load &lt;span class="nt"&gt;--no-interaction&lt;/span&gt;

&lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$@&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then I had to slightly modify our &lt;em&gt;Dockerfile&lt;/em&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; php:7.4-apache&lt;/span&gt;

&lt;span class="c"&gt;# … cut for readability&lt;/span&gt;

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; docker/apache.conf /etc/apache2/sites-enabled/000-default.conf&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; docker/entrypoint.sh /entrypoint.sh&lt;/span&gt;

&lt;span class="c"&gt;# … cut for readability&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;chmod&lt;/span&gt; +x /entrypoint.sh

&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["apache2-foreground"]&lt;/span&gt;

&lt;span class="k"&gt;ENTRYPOINT&lt;/span&gt;&lt;span class="s"&gt; ["/entrypoint.sh"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I COPY the new script file to the container and add executable permissions to that file. At the end of the &lt;em&gt;Dockerfile&lt;/em&gt;, I defined that the file is Entrypoint&lt;/p&gt;

&lt;p&gt;Now I had to rebuild the Docker image to apply these changes &lt;code&gt;docker-compose build web&lt;/code&gt;, and restart the containers. Let's check our application:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--JmdDEclu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ylquzpo6tidq4y72v7f3.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--JmdDEclu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ylquzpo6tidq4y72v7f3.gif" alt="app" width="803" height="398"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It works! Now, anyone who downloads this project and has Docker &lt;strong&gt;can simply run it&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Can you also work with Docker on a Mac? You can read about it in the latest post by Krzysiek &lt;a href="https://accesto.com/blog/docker-on-mac-how-to-speed-it-up/"&gt;here&lt;/a&gt;  :)&lt;/p&gt;

&lt;p&gt;Docker is a powerful tool, I only showed you a minimal snippet that will allow you to start your project in a box. You can find more practical tips and &lt;strong&gt;advanced Docker optimization techniques&lt;/strong&gt; in our &lt;strong&gt;free e-book&lt;/strong&gt; &lt;a href="https://accesto.com/books/docker-deep-dive/"&gt;Docker Deep Dive&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://accesto.com/books/docker-deep-dive/"&gt;&lt;br&gt;
&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--3q2MLbUH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4rlvwyhxbekczgalcmvi.png" alt="Docker Deep Dive Book" width="880" height="446"&gt;&lt;br&gt;
&lt;/a&gt;&lt;/p&gt;

</description>
      <category>docker</category>
      <category>symfony</category>
      <category>php</category>
    </item>
    <item>
      <title>Strangler Pattern in practice</title>
      <dc:creator>Michał Romańczuk</dc:creator>
      <pubDate>Fri, 01 Apr 2022 08:58:54 +0000</pubDate>
      <link>https://dev.to/accesto/strangler-pattern-in-practice-41kp</link>
      <guid>https://dev.to/accesto/strangler-pattern-in-practice-41kp</guid>
      <description>&lt;p&gt;This week I finally &lt;strong&gt;hit the delete button&lt;/strong&gt;. I've been waiting for this moment for the last 3 years. Finally, I erased the legacy part of the codebase I was working on! Using a strangler pattern, we got to the point where the &lt;strong&gt;legacy SaaS application was completely replaced&lt;/strong&gt; with a new shiny web application that everyone wants to work on and develop. But let’s start from the beginning.&lt;/p&gt;

&lt;p&gt;Who likes working with legacy code? You may not believe it, but I do. It is always a challenge. Working on web application refactoring is usually archeology combined with graphology and patience training. Moreover, it is a job for the brave, let's be honest, how many legacy projects have tests? But if they do, these are usually red, and had been red  a long time before we took over the project. If you change something in one place of a legacy code, you never know whether it won't break anything in another place that you don't know about, just because you haven't dug into it yet. Fixing a bug that needs to be fixed in the legacy code is similar to playing with dominoes. &lt;strong&gt;You fix one thing, and it causes you to discover another bug&lt;/strong&gt;, and another, and another. And this way, from fixing one simple bug, it turns out you've changed 60 files. And when you finally deploy changes, you sweat while waiting for something to explode.&lt;/p&gt;

&lt;p&gt;But don't worry, it is not the way we work with legacy code. At Accesto, we specialize in difficult cases. We usually take over the projects that others were afraid of. We like legacy, we usually live in peace with legacy code. But how is it even possible? &lt;strong&gt;How to deal with the legacy without going nuts??&lt;/strong&gt; Fortunately, there are a few ways to do it and maintain sanity. For example a Legacy can be tamed, Krzysiek showed it in his &lt;a href="https://accesto.com/blog/legacy-code-strangle-or-tame/" rel="noopener noreferrer"&gt;recent article&lt;/a&gt;. I'll show you another way.&lt;/p&gt;

&lt;h2&gt;
  
  
  So let's start from the beginning.
&lt;/h2&gt;

&lt;p&gt;We took over a several-years old SaaS product that had already achieved business success in its market. But this success came with hidden costs, the cost of a technological debt (the costs of technical debt are &lt;a href="https://accesto.com/blog/technical-debt-the-silent-villain-of-web-development/" rel="noopener noreferrer"&gt;described in detail&lt;/a&gt; by our CEO Piotr).  It was a classic case, it was taking more and more time and effort to  introduce new features, users were waiting a long time for bug fixes, and the entire website was working slower and slower day by day. Technical debt was so high that the previous &lt;strong&gt;developers quit their jobs&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;The client, after a long search for someone who would like to help him, came to us.&lt;/p&gt;

&lt;p&gt;Let's do a quick audit of what we got: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;files with 2k+ lines of code?  &lt;span&gt;✓&lt;/span&gt;
&lt;/li&gt;
&lt;li&gt;copy-paste driven development? &lt;span&gt;✓&lt;/span&gt;
&lt;/li&gt;
&lt;li&gt;hardcoded secret keys in code?   &lt;span&gt;✓&lt;/span&gt;
&lt;/li&gt;
&lt;li&gt;PHP/HTML/CSS/SQL/JS in single file? &lt;span&gt;✓&lt;/span&gt;
&lt;/li&gt;
&lt;li&gt;code versioning with file names &lt;span&gt;✓&lt;/span&gt; &lt;/li&gt;
&lt;li&gt;vulnerability to SQL injection, XSS, IDOR, etc.? &lt;span&gt;✓&lt;/span&gt;
&lt;/li&gt;
&lt;li&gt;FTP deployment?  &lt;span&gt;✓&lt;/span&gt;
&lt;/li&gt;
&lt;li&gt;spaghetti code? (everywhere)  &lt;span&gt;✓&lt;/span&gt;
&lt;/li&gt;
&lt;li&gt;and many more, it was not good, to sum up - a big ball of mud. &lt;span&gt;✓&lt;/span&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://accesto.com/blog/solid-php-solid-principles-in-php/" rel="noopener noreferrer"&gt;SOLID princinples?&lt;/a&gt; forget it, that would require using at least some OOP ;-) &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;How did we approach it? To be honest, we didn't have much experience with the strangler pattern that time. We did have many &lt;strong&gt;successful SaaS refactoring projects&lt;/strong&gt; behind us, but not with that approach. So where did we start?&lt;/p&gt;

&lt;h3&gt;
  
  
  Versioning
&lt;/h3&gt;

&lt;p&gt;This legacy SaaS had a very peculiar way of versioning the code.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftjdev3yr8ljr4vj58wso.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftjdev3yr8ljr4vj58wso.png" alt="Versioning"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To make it funnier, the "latest version" was not always used everywhere. For example, the login page used "footer_v4.php" and the password reset page used "footer_v2.php", etc. So we couldn't just delete "older versions" that we thought were unused because there were "newer versions". &lt;/p&gt;

&lt;h3&gt;
  
  
  Copy-paste driven development + useful comments:
&lt;/h3&gt;

&lt;p&gt;Anyone can make a bit of copy-paste. But "a bit" is crucial here. What we saw there deserved a master's degree in copy-pasting.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvioh26j7r9zi7sz3nzea.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvioh26j7r9zi7sz3nzea.png" alt="Copy-paste driven development"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You won't find a significant difference between the two methods above. The entire system was based on the known methodology of &lt;em&gt;"copy-pasting"&lt;/em&gt;. How can such things be cleaned? I described an example in the article about "&lt;a href="https://accesto.com/blog/Boy-scout-rule-in-6-examples-the-basic-principle-of-web-development/" rel="noopener noreferrer"&gt;boy scout rule&lt;/a&gt;". But unfortunately, we couldn't put on rubber gloves and start cleaning, the strangler pattern is not about legacy refactoring. We tried to &lt;strong&gt;close legacy in a box&lt;/strong&gt; and slowly replaced the old functions one by one until we could finally delete the &lt;code&gt;"/legacy"&lt;/code&gt; directory. We tried not to touch the legacy code as long as we could. Unfortunately, it was not always possible, sometimes (actually very often at the beginning) the &lt;em&gt;"urgent error"&lt;/em&gt; comes up. Then, as in the old times, we had to find and fix the bug (it is not easy in two-thousand-liners).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsh679wsjdx86ncl8n4s3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsh679wsjdx86ncl8n4s3.png" alt="French comments"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you think leaving French comments in the code is okay, go to hell.&lt;/p&gt;

&lt;h3&gt;
  
  
  What options do we have?
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;keep going with legacy saas - add new features, fix reported bugs just like the developers who have just quit...&lt;/li&gt;
&lt;li&gt;work on SaaS refactoring - slowly improve code quality, improving legacy php...&lt;/li&gt;
&lt;li&gt;rewrite from scratch - favorite of all programmers but not business!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Some other ideas?&lt;/p&gt;

&lt;p&gt;All of the above ways are hopeless. We wanted to try something different, something that will &lt;strong&gt;satisfy everyone, programmers, users and the business&lt;/strong&gt;. And that is why we decided to use the strangler pattern. &lt;/p&gt;

&lt;p&gt;What is the strangler pattern? In a nutshell, it’s incrementally replacing individual functionalities of the legacy system with new ones. Over time, nothing will be left of the legacy and the new system will replace all its functions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sounds trifle&lt;/strong&gt;, doesn't it? So where to start?&lt;/p&gt;

&lt;p&gt;Disclaimer: I would just like to point out that this is not a tutorial, this is a story of what we did some time ago. Could it be done better? For sure, and now we would have done some things differently. But we didn't go that wrong either.&lt;/p&gt;

&lt;h3&gt;
  
  
  Repository
&lt;/h3&gt;

&lt;p&gt;We only got FTP access, so we need to civilize it. We've added code versioning (GIT). I was the unfortunate one who became the "author" of the entire legacy. I do not need to explain this, each of you knows that it is impossible to work without versioning. Simple, a new repository, &lt;code&gt;git add .&lt;/code&gt; without hesitation &lt;code&gt;git commit -m "clean legacy project"&lt;/code&gt; and &lt;code&gt;git push&lt;/code&gt;. That's it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Environment
&lt;/h3&gt;

&lt;p&gt;To be able to work, we had to prepare a development environment. We put all the legacy code in a separate directory. Guess how it was named… yes you’re right &lt;code&gt;"/legacy"&lt;/code&gt;. After that, we deleted files that should not be in the repository like &lt;code&gt;"/upload/*"&lt;/code&gt;, something like a &lt;em&gt;"cache"&lt;/em&gt;, etc. Later we found a lot of useless files, but at this stage, we were not sure what was needed and what was not. Finally, we could create a simple Docker setup to run the project. Two containers on start, one for database and one for the legacy SaaS.&lt;/p&gt;

&lt;h3&gt;
  
  
  Facade
&lt;/h3&gt;

&lt;p&gt;According to the strangler pattern - Gateway/Facade. We added the &lt;strong&gt;nginx reverse proxy&lt;/strong&gt;, in the beginning, all traffic just went to the legacy container. The architecture looked like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6xhx0xgonre6gxodhl69.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6xhx0xgonre6gxodhl69.png" alt="Architecture v1"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Thus, we have taken the first step of the strangler pattern in practice.&lt;/p&gt;

&lt;h3&gt;
  
  
  New
&lt;/h3&gt;

&lt;p&gt;Time for something enjoyable. We have to prepare a place for new features. We create a new project next to the legacy. The new, clean project, everyone likes them - the greenfield. We could choose what technologies we wanted, awesome. &lt;strong&gt;We chose what we are good at&lt;/strong&gt; - a Symfony framework. &lt;/p&gt;

&lt;p&gt;We started the easiest way - monorepo - next to the legacy we created the &lt;code&gt;"/new"&lt;/code&gt; directory with Symfony and dockerize it. Now the structure of our project looked like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffukeemirlezc2n5sgeqh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffukeemirlezc2n5sgeqh.png" alt="Architecture v2"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the schema above, the size of the "new" and "legacy" boxes matters. All traffic was still routed across the gateway to the legacy. But we had a new container with clean Symfony that was connected to the same database as the legacy.&lt;/p&gt;

&lt;p&gt;Let's stop here for a moment.&lt;/p&gt;

&lt;h3&gt;
  
  
  Database
&lt;/h3&gt;

&lt;p&gt;We decided at that point to use the same database. Using the legacy database in the new part &lt;strong&gt;is actually an anti-pattern.&lt;/strong&gt; We were aware of it, and despite that, we chose this solution. Why is it wrong? The boundary where the "New" and "Legacy" meet must be clear and transparent. When we use one database, we lose these boundaries. You don't see what is used by which part. It is not clear what can be changed and removed, what dependencies you have. The existing structure imposes a data model on you, so you are not able to &lt;strong&gt;create optimal solutions&lt;/strong&gt;, you only use what you have. You improve the code by rewriting functionalities, but you are sometimes blocked by the existing data structure. Finally, you end up with a database where some of the tables are new, they are yours and you are proud of them, but they have relations to the old ugly ones, besides, there are many tables in the database that you don't even know what they're for. And worse, you don't know if you can delete them, because maybe something in legacy is using them, or maybe something in "new" is using it somewhere?&lt;/p&gt;

&lt;p&gt;So, how should it be done in a perfect world?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Boundaries should be clearly delineated&lt;/strong&gt; so it is best if the database is separate.&lt;/p&gt;

&lt;p&gt;How can this be achieved? &lt;strong&gt;Synchronization.&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Cyclic synchronization - it can be a script that synchronizes the databases cyclically, for example once every day.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Events - Legacy can dispatch events based on which New will update its copy of the data. But that requires legacy changes, and that can be tough.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;DB Triggers - we can synchronize at the database level and use, for example use database triggers.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Transaction log - each transaction in the database leaves a log. We can use these logs and recreate them in a second database. For example, we can use Binlog + Kafka for this.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We summed up the pros and cons and decided to go single database. We had no experience with this pattern then, so we didn't know why it was wrong yet. As proof of the concept, we wanted to have a working project quickly so that we could test it in practice, and connecting directly to the Legacy base is quick and convenient. This has caused problems several times. Relationships and suboptimal model structure were the most problematic. &lt;strong&gt;We survived&lt;/strong&gt;, the project survived it too, we finally managed to improve it all, but today we'd think twice or even three times before using one database.&lt;/p&gt;

&lt;h3&gt;
  
  
  Initial dump
&lt;/h3&gt;

&lt;p&gt;Speaking of the database. Legacy as legacy did not use the full ORM (just something that pretended to be ORM), did not use migration and had no documentation. &lt;/p&gt;

&lt;p&gt;All we had was production access to the database.&lt;/p&gt;

&lt;p&gt;In order to be able to work in a development environment, it was necessary to prepare a safe database dump. It was a huge database. Over a hundred tables. A dump was needed to have some starting point for local development. First and foremost, &lt;strong&gt;anonymization&lt;/strong&gt;, we had to go through all the tables and locate critical data that would be troublesome to come out. Another thing is data thinning. Out of &lt;strong&gt;several gigabytes of data&lt;/strong&gt;, it was necessary to leave a maximum of tens of megabytes (because it will end up in the repository). Database relations were the problem, of course. This was a legacy, obviously not always the necessary foreign keys were added, so we had to be careful. Once the DB dump was prepared, we added it to the repository and prepared a script (in this case, it was phing) to load it into the database on the project setup.&lt;/p&gt;

&lt;h3&gt;
  
  
  Migrations
&lt;/h3&gt;

&lt;p&gt;To manage database changes in a more predictable way, we decided to use &lt;a href="https://symfony.com/doc/3.1/bundles/DoctrineMigrationsBundle/index.html" rel="nofollow noopener noreferrer"&gt;Doctrine Migrations&lt;/a&gt;. But to be able to use migrations, we needed to have tables mapped to objects (ORM). I don't know if anyone would be able to map manually hundreds of tables to mapped objects without bugs. Luckily we didn't have to, Doctrine has a script ready for this (&lt;a href="https://symfony.com/doc/2.8/doctrine/reverse_engineering.html" rel="nofollow noopener noreferrer"&gt;reverse engineering&lt;/a&gt; - yes, we started with SF 2.8 - it was already some time ago). &lt;/p&gt;

&lt;p&gt;If you think that after that I ran the command that generates the migration and it was empty (it should be because we didn't want to change anything at this point, only map to objects), &lt;strong&gt;you are wrong&lt;/strong&gt;. This script can't handle everything. It cannot cope with some data types (eg. tinyint is mapped to type boolean - regardless of length), some default values (eg. &lt;code&gt;"0000-00-00 00:00:00"&lt;/code&gt; in datetime), and sometimes with relations (eg. stubbornly tries to rename foreign keys). So initially, such a migration, although it should be empty, contained several dozen &lt;strong&gt;changes that we did not make&lt;/strong&gt;, it was necessary to review and decide what to do with them. We had to get used to some of them in the initial phase, if someone did not write the migration himself but generated them, he had to start by removing those changes that he did not make. And if someone forgot, it was remembered in the code review. &lt;/p&gt;

&lt;p&gt;From now on, each change in the database will be implemented using &lt;a href="https://symfony.com/doc/3.1/bundles/DoctrineMigrationsBundle/index.html" rel="nofollow noopener noreferrer"&gt;Doctrine Migrations&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Tests
&lt;/h3&gt;

&lt;p&gt;We decided to protect ourselves and write tests for basic business paths. It was not that simple, the project had no documentation, there were so many unused files that no one was able to tell what functionality it had at all. I dare to assume that most of the files in the &lt;code&gt;"/legacy"&lt;/code&gt; directory &lt;strong&gt;weren't used at all&lt;/strong&gt;. Even our client did not know about many of the existing functionalities in his system. &lt;/p&gt;

&lt;p&gt;At the beginning, there was not much time for testing, it was practically no time for that. Tests are not what the client came for, he expected bug fixes and new functionalities, not days spent writing tests. It was just for us, for a quiet job. &lt;/p&gt;

&lt;p&gt;So what to test? We decided to test what the client recognized as key functionalities. For the client, &lt;strong&gt;most of the functionality is crucial&lt;/strong&gt;, we had to use our intuition. Of course, no one is able to write tests for an unknown existing system that will cover everything, having no budget for it. We used &lt;a href="https://phpunit.de/" rel="nofollow noopener noreferrer"&gt;PHPUnit&lt;/a&gt;. We have also added smoke tests (does it return "200"?) to most of the subpages that we managed to find by clicking.&lt;/p&gt;

&lt;h3&gt;
  
  
  Secrets
&lt;/h3&gt;

&lt;p&gt;What's next? What most people don't like to do, some urgent clean up the legacy. I will show an example, a database connection configuration:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftx3a6tymifz6ywjfoz9i.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftx3a6tymifz6ywjfoz9i.png" alt="Database configuration 1"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdj1m9wrhfq1lk05hiw8y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdj1m9wrhfq1lk05hiw8y.png" alt="Database configuration 2"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can see, many &lt;em&gt;"secrets"&lt;/em&gt; have been hardcoded. And the way it was done, it cried out to heaven for vengeance.&lt;/p&gt;

&lt;p&gt;We decided to change this way of configuration to &lt;strong&gt;environment variables&lt;/strong&gt;. There were many secrets in the system, often hidden among files with several thousand lines, defined wherever they were needed, they had to be found and replaced with environment variables. It's not a pleasant job, but it has to be done.&lt;/p&gt;

&lt;h3&gt;
  
  
  Security issues
&lt;/h3&gt;

&lt;p&gt;The next step was to secure critical security issues. Legacy code was in poor shape from that point of view. Inserting URL parameters straight into SQL queries? Of course. I think I could write an article &lt;em&gt;"Top 20 OWASP Vulnerabilities"&lt;/em&gt; and illustrate each subsection with examples we found in the code. Yes, it was that bad. We spent a lot of time &lt;strong&gt;finding and patching security issues&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;When we told the client about the scale of the problem, we managed to persuade him to perform penetration tests by an external company that specializes in it. The several dozen pages report we received was very helpful.&lt;/p&gt;

&lt;p&gt;My knowledge of security has grown significantly during this time. It hit me so much that later, for fun, I did "pentests" (it's easy when you have access to the code :)) of our other projects, upsetting my colleagues from other teams.&lt;/p&gt;

&lt;h3&gt;
  
  
  Continuous integration / Continuous delivery
&lt;/h3&gt;

&lt;p&gt;As we are working on this project on Gitlab, you probably won't be surprised when I say that we used Gitlab CI. Here it was classic, we've added a few steps for build after commit, checking tests etc. Of course, most of CI's scripts were defined for the new part (which was still not doing anything at this point). After all, we will &lt;strong&gt;not run static legacy code analysis&lt;/strong&gt; like phpstan, php-cs-fixer, phpmd etc, because the build would never pass, legacy php was a mess. &lt;/p&gt;

&lt;p&gt;The docker images ready for deployment were built on the pushed git tag. We used the &lt;a href="https://rancher.com/products/rancher/" rel="nofollow noopener noreferrer"&gt;Rancher&lt;/a&gt; tool for test/stage environments.&lt;/p&gt;

&lt;p&gt;We also used Capistrano for RC and production deployment. &lt;strong&gt;No more FTP deployment&lt;/strong&gt;. What has it given to us? The possibility of quick rollback, speed, and certainty of deployed changes.&lt;/p&gt;

&lt;h3&gt;
  
  
  What about session and users?
&lt;/h3&gt;

&lt;p&gt;As &lt;em&gt;"new"&lt;/em&gt; and &lt;em&gt;"legacy"&lt;/em&gt; are two separate docker containers, we had to choose some way to have information about the logged in user in the &lt;em&gt;"new"&lt;/em&gt; part.&lt;/p&gt;

&lt;p&gt;The first solution was a &lt;strong&gt;separate memcached container with users session&lt;/strong&gt; that was shared between &lt;em&gt;"new"&lt;/em&gt; and &lt;em&gt;"legacy"&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;The legacy php part did not need to be changed:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F65l7hw76bpminao7map2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F65l7hw76bpminao7map2.png" alt="Login/Session 1"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The logged in user's username was already stored in the session (I am also puzzled by this comment &lt;em&gt;"if true"&lt;/em&gt;). All we had to do was get to it in the new part.&lt;/p&gt;

&lt;p&gt;So we used &lt;a href="https://symfony.com/doc/2.8/security/guard_authentication.html" rel="nofollow noopener noreferrer"&gt;Symfony Guard&lt;/a&gt;. It is a cool and easy-to-use authentication mechanism. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7ldmwwf16y4hjq39rhx4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7ldmwwf16y4hjq39rhx4.png" alt="Login/Session 2"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The new part, having access to the username of the logged in user, can thus simply authorize him. &lt;/p&gt;

&lt;p&gt;No, it wasn't that simple ;). In the old part, there was a &lt;strong&gt;complicated access control system&lt;/strong&gt; based on numbers (sic!). Users had numbers in the database, each number meant a different level of authorization. Of course, this wasn't documented anywhere, and we had to take the time to understand what number means what permissions and recreate that in the new part on Symfony user roles.&lt;/p&gt;

&lt;p&gt;Seriously, I'm not kidding, in legacy application, it looked like this ("ctrl+f" results below):&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvf48lrs6q6pxtt0twzsy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvf48lrs6q6pxtt0twzsy.png" alt="Access level"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  First tickets
&lt;/h3&gt;

&lt;p&gt;Of course, the &lt;strong&gt;first tasks were all about  hotfixes&lt;/strong&gt;. Before the client came to us, he did a little research, bounced off a few companies that did not want to work with legacy SaaS. So, the project was running without technical support for some time. When the client came to us, the queue of errors to be fixed was already long. This is unfortunately a job that &lt;strong&gt;is not a strangler pattern&lt;/strong&gt;, It is just a simple SaaS refactoring. &lt;/p&gt;

&lt;p&gt;But at this point, we had the whole development setup ready to work with.&lt;/p&gt;

&lt;p&gt;The problems were like with any legacy application. It was necessary to &lt;strong&gt;patch urgent bugs&lt;/strong&gt; and optimize the code in some parts. We mainly used &lt;a href="https://blackfire.io/" rel="nofollow noopener noreferrer"&gt;Blackfire&lt;/a&gt; and &lt;a href="https://dev.mysql.com/doc/refman/5.7/en/slow-query-log.html" rel="nofollow noopener noreferrer"&gt;MySQL Slow Query Log&lt;/a&gt; to monitor and look for the causes of problems.&lt;/p&gt;

&lt;h3&gt;
  
  
  First new feature
&lt;/h3&gt;

&lt;p&gt;But aside from improving performance and reliability, &lt;strong&gt;competition never sleeps&lt;/strong&gt;. When we fixed the most urgent bugs, we started working on the new functionality in parallel with fixing the other bugs in legacy code. It's finally time for what everyone likes. It's &lt;strong&gt;time to go wild on the greenfield&lt;/strong&gt;, our new part. &lt;/p&gt;

&lt;p&gt;There were a few things that limited us. The legacy had some layout. To make the whole system consistent, we had to recreate these styles and page structure in the new part. This had some consequences. For example, adding a new item to a menu had to be done in both parts. It became necessary to &lt;strong&gt;maintain two systems&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Okay, we had CSS and layout in the new part. We could start working. Could we? Yes! We decided to prefix the paths for the new part. So our Reverse Proxy started redirecting paths starting with &lt;code&gt;"/app/"&lt;/code&gt; to the Symfony container. &lt;/p&gt;

&lt;p&gt;The user logged in, entered the system dashboard, a new item "Some new feature" appeared in the menu. So far, everything has happened in the legacy. But when he clicked on that new item in the menu he was redirected to &lt;code&gt;"/app/some-new-feature"&lt;/code&gt;. Reverse Proxy recognized the prefix &lt;code&gt;"/app/"&lt;/code&gt; and routes the request to the new part of the system. We have it! &lt;strong&gt;Strangler pattern in practice&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Getting rid of legacy
&lt;/h3&gt;

&lt;p&gt;In each iteration, we tried to replace some legacy. feature with a new one. We wanted to change the size proportions of the boxes new and legacy below with every release and make the box legacy finally disappear and we will be left with new.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fws88cq2h3gqon61hvvbm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fws88cq2h3gqon61hvvbm.png" alt="Architecture v3"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For example, logging into the system was one of the first. Remember how it worked so far? The user logged in in legacy, his username was saved in the session (memcached) and the username in the new part was taken from the session, after which the user could be authorized in both. Now we had to reverse this mechanism. By the way, we decided to stop using the memcached session and replace it with the &lt;a href="https://jwt.io/" rel="nofollow noopener noreferrer"&gt;JWT token&lt;/a&gt; passed in the cookie. This was also necessary later to be able &lt;strong&gt;to scale the app in the Cloud&lt;/strong&gt; (sessionless authentication).&lt;/p&gt;

&lt;p&gt;In functionalities, not as crucial as logging in we used &lt;strong&gt;feature flag&lt;/strong&gt; and &lt;strong&gt;canary releasing&lt;/strong&gt;. We didn't use any fancy tools for the feature flags, simple bool in the database, feature enabled or not. Switching functionality from legacy to new sometimes involved migrating data from old legacy tables to the new database structure, so we did this for specific groups of users. After that, we switched the feature flag for those users. For users, this usually revealed a slightly refreshed layout of the old functionality. If &lt;strong&gt;everything was fine&lt;/strong&gt;, we were gradually migrating data to subsequent users until everyone was using the new feature, and we could remove the legacy code and that feature flag at all.&lt;/p&gt;

&lt;h3&gt;
  
  
  Do you need to rewrite EVERY legacy functionality?
&lt;/h3&gt;

&lt;p&gt;Of course not. But the client never wants to get rid of any functionality. So we used the &lt;strong&gt;user behavior tracking tools&lt;/strong&gt;. And it was an enlightening experience, especially for the client. It turned out that some functionalities that the client considered to be one of the most important ones, were completely unused by users! Why waste energy and money on parts of the system that are not used by anyone?&lt;/p&gt;

&lt;p&gt;By using the feature flags, we were gradually hiding some unused legacy functionalities from users. If nobody complained, we &lt;strong&gt;just removed them&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Scalability
&lt;/h3&gt;

&lt;p&gt;Sometimes a product becomes popular. Traffic is increasing, we are opening up to new markets. We have come to the point where we had to migrate to the cloud. We choose &lt;a href="https://cloud.google.com/" rel="nofollow noopener noreferrer"&gt;Google Cloud&lt;/a&gt; and &lt;a href="https://kubernetes.io/" rel="nofollow noopener noreferrer"&gt;Kubernetes&lt;/a&gt;. The fact that we used the docker from the beginning was helpful. We had sessionless authentication, filesystem abstraction, all infrastructure in docker, &lt;strong&gt;we were cloud-ready&lt;/strong&gt;, so that's happened.&lt;/p&gt;

&lt;h3&gt;
  
  
  So here we are today.
&lt;/h3&gt;

&lt;p&gt;This week I finally deleted the legacy directory and its docker container. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fevfelhudeyp7ijymc5k1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fevfelhudeyp7ijymc5k1.png" alt="Architecture v4"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Seriously, it inspired me to write this article as a remembrance of this event. We are with a product that works fully on our principles. The day has come when our &lt;strong&gt;"new" has become "new legacy"&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  How's your Legacy?
&lt;/h2&gt;

&lt;p&gt;Legacy is not that bad if you get it right. If you are still not comfortable with your Legacy, feel &lt;strong&gt;free to write to us&lt;/strong&gt;, maybe we can help you.&lt;/p&gt;

</description>
      <category>php</category>
      <category>codequality</category>
      <category>symfony</category>
      <category>programming</category>
    </item>
    <item>
      <title>Using PHPStan with Symfony - static analysis for better PHP code quality</title>
      <dc:creator>Michał Romańczuk</dc:creator>
      <pubDate>Mon, 14 Mar 2022 07:49:03 +0000</pubDate>
      <link>https://dev.to/accesto/using-phpstan-with-symfony-static-analysis-for-better-php-code-quality-26eh</link>
      <guid>https://dev.to/accesto/using-phpstan-with-symfony-static-analysis-for-better-php-code-quality-26eh</guid>
      <description>&lt;p&gt;Software developers are not robots, even after seven years of PHP programming experience, I do sometimes make bugs in the code. In the age of smart IDEs, it's getting harder and harder to make silly mistakes, most IDEs catch them very well, but it still happens.&lt;/p&gt;

&lt;p&gt;To avoid errors in the code, we can use tools that detect them during &lt;strong&gt;static code analysis&lt;/strong&gt;. What kinds of errors are detected by static code analyzers? Lots, such as calls to unknown classes/methods, number of method arguments, using undefined variables, calling methods on nullable types, types of arguments passed to methods, and much much more.&lt;/p&gt;

&lt;p&gt;There are many tools for static &lt;strong&gt;PHP code analysis&lt;/strong&gt;, but one of the most popular is PHPStan. It is possible that it is due to its ease of use, versatility and the possibility of using many extensions for example to Symfony, doctrine, elasticsearch, monolog, guzzle etc. I'll show you how to easily add PHPStan to the Symfony project.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to install PHPStan in a Symfony framework?
&lt;/h2&gt;

&lt;p&gt;It is easier when we create a new environment and have a new, greenfield, clean and fresh project. Then we do not init PHPStan into the "dirty" code, but we have the &lt;strong&gt;code analysed from the beginning&lt;/strong&gt;. But of course that rarely happens. If you haven't used static code analysis tools before, you are probably adding it because you already encounter some problems with your code.&lt;/p&gt;

&lt;p&gt;First step, install &lt;a rel="nofollow noopener noreferrer" href="https://packagist.org/packages/phpstan/phpstan"&gt;PHPStan&lt;/a&gt; via Composer&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;composer require &lt;span class="nt"&gt;--dev&lt;/span&gt; phpstan/phpstan
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It will install PHPStan’s executable in the project &lt;code&gt;bin&lt;/code&gt; directory. Depending on your &lt;a href="https://accesto.com/blog/what-is-php-framework-symfony-explained-for-executives/" rel="noopener noreferrer"&gt;Symfony framework&lt;/a&gt; version/configuration it will be &lt;code&gt;vendor/bin&lt;/code&gt; or just &lt;code&gt;bin&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Second step.. just run the &lt;strong&gt;PHPStan analyse command&lt;/strong&gt; (note that it is not a Symfony console command):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bin/phpstan analyse src
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;where &lt;code&gt;src&lt;/code&gt; is a directory name you want to check.&lt;/p&gt;

&lt;p&gt;That's it, you can start correcting bugs :). If this is a new project, you'll probably see there are no errors… but, let's use this in a project I was working on a while ago, and PHPstan wasn't used in it.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to use PHPStan in a Symfony project?
&lt;/h2&gt;

&lt;p&gt;PHPStan can be run with few parameters. One of them is required, this is the directory to be checked.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;level&lt;/code&gt; parameter is very important - "Specifies the rule level to run.". Analyse command can be run with one from nine levels where &lt;strong&gt;level 0 is the loosest and level 8 is the strictest&lt;/strong&gt;. Default level is 0. How do these levels differ? You can check it in &lt;a rel="nofollow noopener noreferrer" href="https://phpstan.org/user-guide/rule-levels"&gt;documentation&lt;/a&gt;. If this is a new, empty project, and you are just starting to write code, &lt;strong&gt;it is worth using a higher level&lt;/strong&gt;. Then you will maintain high standards from the very beginning. But if you are adding PHPStan to an existing project, it will probably be not easy to pass even the lowest level.&lt;/p&gt;

&lt;p&gt;How the response look like? Let’s check the project I mentioned. I will start from default level 0.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb0ic2h2ua1pxjgctu7ju.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb0ic2h2ua1pxjgctu7ju.png" alt="PHPStan analysis results"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F50dxulsk8q2pixg77bz3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F50dxulsk8q2pixg77bz3.png" alt="Found 53 errors"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So in the src directory PHPStan found 53 bugs. What types of errors are these?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Access to an undefined property $foo&lt;/li&gt;
&lt;li&gt;Method foo() invoked with X parameter, Y required.&lt;/li&gt;
&lt;li&gt;Class \Bar constructor invoked with X parameters, Y required.&lt;/li&gt;
&lt;li&gt;Call to an undefined method bar()&lt;/li&gt;
&lt;li&gt;Method \Bar::foo() should return string but return statement is missing.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Even though 53 seems like a lot, &lt;strong&gt;it is very easy to correct it&lt;/strong&gt; because these are simple ones.&lt;/p&gt;

&lt;p&gt;So let's try to raise the level, how many bugs will it find? I do not have good news:&lt;/p&gt;

&lt;p&gt;level 2 - 629 errors&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fib5wl1kuiqva3trcrg4j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fib5wl1kuiqva3trcrg4j.png" alt="Found 629 errors"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;level 8 - 2695 errors&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpys45xgytjooe1lgphw5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpys45xgytjooe1lgphw5.png" alt="Found 2695 errors"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;No customer will agree if you tell him, now for a few weeks I will correct all errors found by PHPStan. You need to approach it wisely.&lt;/p&gt;
&lt;h2&gt;
  
  
  How to configure PHPStan?
&lt;/h2&gt;

&lt;p&gt;Another parameter in the analyse command is &lt;code&gt;--configuration&lt;/code&gt; (&lt;code&gt;-c&lt;/code&gt;). It’s a path to file with configuration in NEON format (very similar to YAML).&lt;/p&gt;

&lt;p&gt;What can we configure there? A lot, you can find all the options in the &lt;a rel="nofollow noopener noreferrer" href="https://github.com/phpstan/phpstan-src/blob/0.12.92/conf/config.neon"&gt;documentation&lt;/a&gt; but, for me, the most important options are paths to be checked and excluded from check, level and ignoring errors.&lt;/p&gt;

&lt;p&gt;When I look at the result for level 2, I can see that most of the errors are &lt;code&gt;PHPDoc tag @param has invalid value ($foo): Unexpected token "$foo", expected type&lt;/code&gt;. Let's be honest, &lt;strong&gt;this is not a critical bug&lt;/strong&gt;, I won't be fixing all PHPDoc tags right now. Let's say I'll fix it another time, and now I'd like to ignore it (but make sure to put it on my refactoring list to keep a track on &lt;a href="https://accesto.com/blog/technical-debt-the-silent-villain-of-web-development/" rel="noopener noreferrer"&gt;the technical debt&lt;/a&gt;). For this, I'll use the ignore error option in my configuration file. We can use regular expressions there, so I will ignore all errors starting with "PHPDoc tag ..."&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;parameters&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;ignoreErrors&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;#PHPDoc&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;tag&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;.#'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When you add your configuration file, do not forget to add it as a parameter for the analyse command. Otherwise, PHPstan will look for a file with the default name &lt;code&gt;phpstan.neon&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bin/phpstan analyse &lt;span class="nt"&gt;-l&lt;/span&gt; 2 &lt;span class="nt"&gt;-c&lt;/span&gt; myPHPStanConfig.neon src
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Faoply3wf0flm7s1u3e1x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Faoply3wf0flm7s1u3e1x.png" alt="Found 268 errors"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This way the number of bugs has dropped from 629 to 268. &lt;strong&gt;I didn't fix these bugs&lt;/strong&gt;, I just silenced them, I will correct them later (yeah, someday ;)) because they are not critical. I allowed myself to silence a few other minor bugs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;parameters&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;ignoreErrors&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;#PHPDoc&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;tag&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;.#'&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;#.&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;typehint&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;specified#'&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;#.&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;return&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;statement&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;is&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;missing#'&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;#Return&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;typehint&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;of&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;method&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;.#'&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;#Call&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;to&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;an&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;undefined&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;method&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Psr\\Container\\ContainerInterface::getParameter\(\)#'&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;#Call&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;to&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;an&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;undefined&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;method&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Doctrine\\ORM\\EntityRepository&amp;lt;.+&amp;gt;::find.+\(\).#'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feyv07fwtk39y9k5glg1o.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feyv07fwtk39y9k5glg1o.png" alt="Found 108 errors"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So right now I have "only" 108 erros to check and fix. But I am still on level 2, how does it look like on level 8?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyf1o5i2ife4t61fu0zse.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyf1o5i2ife4t61fu0zse.png" alt="Found 833 errors"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;833 errors, it’s not good, but that's better than the initial 2695 errors. Fortunately, &lt;strong&gt;this post is not about correcting bugs&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;If this was a legacy project and it is not our priority to correct the legacy files, we can exclude whole legacy directores.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;parameters&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
     &lt;span class="na"&gt;excludePaths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;src/Utils/*&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With this configuration, PHPStan will skip all files in the &lt;code&gt;src/Utils/*&lt;/code&gt; directory&lt;/p&gt;

&lt;h2&gt;
  
  
  PHPStan extensions
&lt;/h2&gt;

&lt;p&gt;By default, PHPStan does not understand the magic that Symfony and Doctrine do sometimes. We can use framework specific extensions that expand it’s knowledge.&lt;/p&gt;

&lt;h3&gt;
  
  
  How to install PHPStan Symfony extension?
&lt;/h3&gt;

&lt;p&gt;First we need to start with generic &lt;a rel="nofollow noopener noreferrer" href="https://github.com/phpstan/extension-installer"&gt;PHPStan Extension Installer&lt;/a&gt;, this is a composer plugin that handles the &lt;strong&gt;automatic installation of new extensions&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Just run&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;composer require &lt;span class="nt"&gt;--dev&lt;/span&gt; phpstan/extension-installer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Than install &lt;a rel="nofollow noopener noreferrer" href="https://packagist.org/packages/phpstan/phpstan-symfony"&gt;PHPStan Symfony framework extension&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;composer require &lt;span class="nt"&gt;--dev&lt;/span&gt; phpstan/phpstan-symfony
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After that, we need to define in the configuration file the path to the file that describes our container. It’s a XML file generated in the &lt;code&gt;cache/dev&lt;/code&gt; directory. Extension documentation has some example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;parameters&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;symfony&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# container_xml_path: var/cache/dev/srcDevDebugProjectContainer.xml&lt;/span&gt;
        &lt;span class="c1"&gt;# or with Symfony 4.2+&lt;/span&gt;
        &lt;span class="na"&gt;container_xml_path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;var/cache/dev/srcApp_KernelDevDebugContainer.xml&lt;/span&gt;
        &lt;span class="c1"&gt;# or with Symfony 5+&lt;/span&gt;
        &lt;span class="c1"&gt;# container_xml_path: var/cache/dev/App_KernelDevDebugContainer.xml&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  How to install PHPStan Doctrine extension?
&lt;/h3&gt;

&lt;p&gt;PHPStan Doctrine extension provides support for all Doctrine magic. It checks our DQLs, QueryBuilders, validates entities and relations, recognizes magic methods like &lt;code&gt;findBy*&lt;/code&gt; etc.&lt;br&gt;
We already have "PHPStan Extension Installer", so we just need to install extension:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;composer require &lt;span class="nt"&gt;--dev&lt;/span&gt; phpstan/phpstan-doctrine
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To use more advanced analysis like DQL validation we &lt;strong&gt;have to provide an object manager&lt;/strong&gt;. It’s described in extension &lt;a rel="nofollow noopener noreferrer" href="https://packagist.org/packages/phpstan/phpstan-doctrine"&gt;documentation&lt;/a&gt;. We need to create a file eg. &lt;code&gt;tests/object-manager.php&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;App\Kernel&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;require&lt;/span&gt; &lt;span class="k"&gt;__DIR__&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s1"&gt;'/../config/bootstrap.php'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nv"&gt;$kernel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Kernel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$_SERVER&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'APP_ENV'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;$_SERVER&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'APP_DEBUG'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="nv"&gt;$kernel&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;boot&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$kernel&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getContainer&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'doctrine'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getManager&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and use it in PHPStan configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;parameters&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;doctrine&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;objectManagerLoader&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;tests/object-manager.php&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So let's check my project after adding these extensions.&lt;/p&gt;

&lt;p&gt;For level 2:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flyoict12iczhcux1mi0x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flyoict12iczhcux1mi0x.png" alt="Found 429 errors"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For level 8:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqrvv8uu1y8cszc142m5a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqrvv8uu1y8cszc142m5a.png" alt="Found 1166 errors"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The number of bugs found increased for level 2 from 108 to 429, and for level 8 from 833 to 1166 errors. As you can see, &lt;strong&gt;adding extensions improves code validation efficiency&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Other PHPStan extensions
&lt;/h3&gt;

&lt;p&gt;There are many other extensions, for example you can check &lt;strong&gt;&lt;a rel="nofollow noopener noreferrer" href="https://packagist.org/packages/phpstan/phpstan-strict-rules"&gt;PHPStan strict rules&lt;/a&gt;&lt;/strong&gt; extension. From their documentation:&lt;/p&gt;

&lt;blockquote&gt;
This repository contains additional rules that revolve around strictly and strongly typed code with no loose casting for those who want additional safety in extremely defensive programming
&lt;/blockquote&gt;

&lt;p&gt;And that's exactly how it works. I added it to my project. The number of errors detected increased on level 2 from 429 to 684 and on level 8 from 1166 to 1459.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to automate PHPStan in a Symfony project?
&lt;/h2&gt;

&lt;p&gt;Of course, even if you start with a clean project and you have zero errors, if you had to make sure that you &lt;strong&gt;run PHPStan before each Commit&lt;/strong&gt;, you would forget one time. Someone else might not use it at all and with time the number of errors would grow.&lt;/p&gt;

&lt;p&gt;From time to time you would have to sit and fix all bugs. This is not good, the customer will not want to pay for it.&lt;/p&gt;

&lt;p&gt;Therefore, &lt;strong&gt;PHPStan should be added to your CI&lt;/strong&gt; (you probably have some?). You can add the PHPStan check run job to composer scripts or directly to CI configuration. If it detects an error, then the build will fail, and you will know with each push that you need to correct something.&lt;/p&gt;

&lt;p&gt;It is worth thinking about it from the beginning of the project, already at the stage of the initial setup and dockerization of the Symfony project, which I described in the previous post: &lt;a href="https://accesto.com/blog/simple-docker-setup-for-symfony-project/" rel="noopener noreferrer"&gt;Simple Docker setup for Symfony project&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusions
&lt;/h2&gt;

&lt;p&gt;Is it worth installing PHPStan in an existing project? Of course, but.. probably not at the highest level from the beginning. You will have to start at the lowest level and work your way up gradually.I also recommend using extensions that only &lt;strong&gt;increase PHPStan capabilities&lt;/strong&gt;, and thus find more potential bugs.&lt;/p&gt;

&lt;p&gt;PHPStan is a great tool that will help you to maintain better quality of your PHP code. But remember, neither PHPStan nor any otehr tool will substitute the proper design (eg. following &lt;a href="https://accesto.com/blog/solid-php-solid-principles-in-php/" rel="noopener noreferrer"&gt;SOLID in PHP&lt;/a&gt;), practices (like a &lt;a href="https://accesto.com/blog/Boy-scout-rule-in-6-examples-the-basic-principle-of-web-development/" rel="noopener noreferrer"&gt;Boy Scout rule&lt;/a&gt;) and simply a thoughtfull development. &lt;/p&gt;

&lt;p&gt;If you are not coping with your code, because you have not started using PHPStan on time, you can contact us, &lt;strong&gt;we deal with difficult cases&lt;/strong&gt;.&lt;/p&gt;

</description>
      <category>php</category>
      <category>programming</category>
      <category>codequality</category>
      <category>symfony</category>
    </item>
    <item>
      <title>Solid PHP - SOLID principles in PHP</title>
      <dc:creator>Michał Romańczuk</dc:creator>
      <pubDate>Fri, 25 Feb 2022 07:39:39 +0000</pubDate>
      <link>https://dev.to/accesto/solid-php-solid-principles-in-php-1n7b</link>
      <guid>https://dev.to/accesto/solid-php-solid-principles-in-php-1n7b</guid>
      <description>&lt;p&gt;&lt;strong&gt;SOLID&lt;/strong&gt;, this acronym was coined by Michael Feathers, it represents the five basic principles of &lt;strong&gt;object-oriented programming&lt;/strong&gt; developed by &lt;a href="https://blog.cleancoder.com/" rel="nofollow noopener noreferrer"&gt;Uncle Bob&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Most programmers probably know this acronym. But it seems to me that a minority can decode it.&lt;/p&gt;

&lt;p&gt;Is it wrong? In my opinion, no, I think that writing &lt;strong&gt;clean and simple code&lt;/strong&gt; is more important than knowing the theory. But don't completely ignore the theory. How else will you pass on knowledge to someone? How do you justify your code in a discussion in code review? You have to be based on theory and generally accepted standards.&lt;/p&gt;

&lt;p&gt;But it's also good to know the basics of what &lt;strong&gt;clean and simple code&lt;/strong&gt; looks like.&lt;br&gt;
&lt;strong&gt;SOLID principles&lt;/strong&gt; can be used in any object-oriented programming language. I work in Symfony on a daily basis, so I will show some principles in PHP.&lt;/p&gt;

&lt;p&gt;So let's go through the five SOLID principles together.&lt;/p&gt;
&lt;h2&gt;
  
  
  Single responsibility principle (SPR)
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe2x0u2dotoc317rvm733.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe2x0u2dotoc317rvm733.png" alt="Single responsibility principle (SPR)"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I think this is the most famous rule (probably because it is the first and some people did not read on). But seriously, I think it is very important.&lt;/p&gt;

&lt;p&gt;Uncle Bob describes it as “&lt;strong&gt;A class should have one, and only one, a reason to change&lt;/strong&gt;”. What does it mean? For me, this sentence is not helpful :).&lt;/p&gt;

&lt;p&gt;Other explanations say that a class, a function &lt;strong&gt;should do one thing&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;But what is one thing? Is user registration one thing? Or maybe it's more things, because registrations include some other smaller things, password encryption, saving to the database, sending an e-mail.&lt;/p&gt;

&lt;p&gt;Is sending an email one thing? After all, it consists of many steps such as preparing the e-mail content and subject, extracting the user's e-mail address and handling the response. Should we &lt;strong&gt;set up a separate class&lt;/strong&gt; for each of these activities? How far do we go with this “single responsibility”?!&lt;/p&gt;

&lt;p&gt;I think we need to use a different object-oriented programming principle to answer this question. In 1974, the "&lt;strong&gt;high cohesion and low coupling&lt;/strong&gt;" principle was described for the first time in the article Structured Design in the IBM journal.&lt;/p&gt;

&lt;p&gt;I will try to present it in simple to understand examples.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cohesion&lt;/strong&gt; determines &lt;u&gt;how much a function or class is responsible for&lt;/u&gt;. An simple example here would be Bob and Alice, the cook's helpers. Alice is making desserts. She needs to make a sponge cake, cream, glaze, cut the fruit and put it all together. Each of these steps consists of several others. It's an example of low cohesion. Bob's job is to peel potatoes, nothing else, it’s an example of high cohesion. &lt;strong&gt;Your method/class should be like Bob, do one thing&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Coupling&lt;/strong&gt; is about &lt;u&gt;how easy it is to reuse a given module or class&lt;/u&gt;. Puzzles and Lego blocks are a good example for that. The puzzles are characterized by high coupling. One puzzle fits only in one place, it cannot be combined with other puzzles. The opposite of Lego bricks, they have a low coupling, they can be combined freely and each one can be used anywhere. &lt;strong&gt;Your code should be like Lego blocks, easy to use in different places&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The single responsibility principle should be used together with the "high cohesion and low coupling" principle. &lt;strong&gt;Both of these principles, in my opinion, try to say the same&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Now an example in PHP. Imagine a BlogPost class:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BlogPost&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;Author&lt;/span&gt; &lt;span class="nv"&gt;$author&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$title&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$content&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;\DateTime&lt;/span&gt; &lt;span class="nv"&gt;$date&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// ..&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;getData&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;array&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="s1"&gt;'author'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;author&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;fullName&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
            &lt;span class="s1"&gt;'title'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'content'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'timestamp'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;date&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getTimestamp&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;printJson&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;json_encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getData&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;printHtml&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sb"&gt;`&amp;lt;article&amp;gt;
                    &amp;lt;h1&amp;gt;{$this-&amp;gt;title}&amp;lt;/h1&amp;gt;
                    &amp;lt;article&amp;gt;
                        &amp;lt;p&amp;gt;{$this-&amp;gt;date-&amp;gt;format('Y-m-d H:i:s')}&amp;lt;/p&amp;gt;
                        &amp;lt;p&amp;gt;{$this-&amp;gt;author-&amp;gt;fullName()}&amp;lt;/p&amp;gt;
                        &amp;lt;p&amp;gt;{$this-&amp;gt;content}&amp;lt;/p&amp;gt;
                    &amp;lt;/article&amp;gt;
                &amp;lt;/article&amp;gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What's wrong here? The BlogPost class &lt;strong&gt;does too many things&lt;/strong&gt;, and as we know &lt;strong&gt;it should do one thing&lt;/strong&gt;. The main problem here is that it is responsible for printing to various formats, json, html and more if needed. So let's see how this could be improved.&lt;/p&gt;

&lt;p&gt;We remove printing methods from the BlogPost class, the rest remains unchanged. And we're adding a new PrintableBlogPost interface. With a method that can print a blogpost.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;PrintableBlogPost&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;BlogPost&lt;/span&gt; &lt;span class="nv"&gt;$blogPost&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we can implement this interface in as many ways as we need:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;JsonBlogPostPrinter&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;PrintableBlogPost&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;BlogPost&lt;/span&gt; &lt;span class="nv"&gt;$blogPost&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;json_encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$blogPost&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getData&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;HtmlBlogPostPrinter&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;PrintableBlogPost&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;BlogPost&lt;/span&gt; &lt;span class="nv"&gt;$blogPost&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sb"&gt;`&amp;lt;article&amp;gt;
                    &amp;lt;h1&amp;gt;{$blogPost-&amp;gt;getTitle()}&amp;lt;/h1&amp;gt;
                    &amp;lt;article&amp;gt;
                        &amp;lt;p&amp;gt;{$blogPost-&amp;gt;getDate()-&amp;gt;format('Y-m-d H:i:s')}&amp;lt;/p&amp;gt;
                        &amp;lt;p&amp;gt;{$blogPost-&amp;gt;getAuthor()-&amp;gt;fullName()}&amp;lt;/p&amp;gt;
                        &amp;lt;p&amp;gt;{$blogPost-&amp;gt;getContent()}&amp;lt;/p&amp;gt;
                    &amp;lt;/article&amp;gt;
                &amp;lt;/article&amp;gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can see a whole example of bad and good implementation &lt;a href="https://github.com/accesto/solid-php" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I've seen projects where classes only have one public method with a few lines of code (usually call to a different method from a different class). Completely illegible and terrible to maintain. In my opinion, this &lt;strong&gt;is an example of going too far&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;To sum up. Your classes and methods shouldn't be responsible for a few things. But the point here is not to go to extremes and exude absolutely everything. &lt;strong&gt;Just to make them easy to understand&lt;/strong&gt;, but they also have to be consistent. So that you don't have to read them cover to cover to understand what they are doing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Open/closed principle (OCP)
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Faogbkt8l7dplwhwqy5w9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Faogbkt8l7dplwhwqy5w9.png" alt="Open/closed principle (OCP)"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Second, from SOLID principles. The general explanation is that “code should be &lt;strong&gt;open for extension, but closed for modification&lt;/strong&gt;”. It is not obvious to me what this means in practice. Perhaps it is better explained by the consequence of not following this rule. Changing the declaration of a method may cause it to malfunction somewhere it is used. The main point is that the changes have to be backward compatible. Of course, it's best to &lt;strong&gt;write code that works perfectly from the beginning&lt;/strong&gt; and you don't have to change it, but we don't live in a perfect world.&lt;/p&gt;

&lt;p&gt;I will try to present it with some examples:&lt;/p&gt;

&lt;h3&gt;
  
  
  a) open/closed API
&lt;/h3&gt;

&lt;p&gt;This will be an example of the open/closed principle not on a single class but on the entire API. It's a large SaaS, it is an accounting system written in &lt;strong&gt;PHP, Symfony Framework&lt;/strong&gt;. Your API is used by several hundred customers who use it to issue invoices. Your API has a method to retrieve invoices as PDF. Let's say it is an endpoint like “&lt;em&gt;GET /invoice/{id}/print&lt;/em&gt;”. Everything is fine, but one day customers demand the option to download CSV (everyone from business loves tables).&lt;/p&gt;

&lt;p&gt;So you implement this capability quickly and change the endpoint from:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;"GET /invoice/{id}/print"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;to&lt;/p&gt;

&lt;p&gt;&lt;em&gt;"GET /invoice/{id}/{format}"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;where the format can be PDF or CSV.&lt;/p&gt;

&lt;p&gt;Now only hundreds of programmers using your API have to change how they download the report in PDF. Well, no, &lt;strong&gt;it shouldn't be done that way&lt;/strong&gt;. How to do it correctly? Unfortunately, it is sometimes necessary to see potential problems and anticipate possible future changes. From the beginning, your endpoint did not follow the open/closed principle because &lt;strong&gt;it was not closed for modification&lt;/strong&gt;. Your endpoint should assume that the needs of other formats may arise someday.&lt;/p&gt;

&lt;h3&gt;
  
  
  b) open/closed animals
&lt;/h3&gt;

&lt;p&gt;Another example, a more classic one. Let's say we have several different animal classes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Dog&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;bark&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s1"&gt;'woof woof'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Duck&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;quack&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s1"&gt;'quack quack'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Fox&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;whatDoesTheFoxSay&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s1"&gt;'ring-ding-ding-ding-dingeringeding!, wa-pa-pa-pa-pa-pa-pow!'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And a class that allows animals to communicate:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Communication&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;communicate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$animal&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;switch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="nv"&gt;$animal&lt;/span&gt; &lt;span class="k"&gt;instanceof&lt;/span&gt; &lt;span class="nc"&gt;Dog&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$animal&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;bark&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="nv"&gt;$animal&lt;/span&gt; &lt;span class="k"&gt;instanceof&lt;/span&gt; &lt;span class="nc"&gt;Duck&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$animal&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;quack&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="nv"&gt;$animal&lt;/span&gt; &lt;span class="k"&gt;instanceof&lt;/span&gt; &lt;span class="nc"&gt;Fox&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$animal&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;whatDoesTheFoxSay&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
                &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;\InvalidArgumentException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Unknown animal'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Is the &lt;em&gt;Communication&lt;/em&gt; class open for extension and closed for modification? To answer this question, we can ask it differently. Are we able to add a new animal class &lt;strong&gt;without changing the existing code&lt;/strong&gt;? No. Adding a new animal class would necessitate the modification of the switch in the &lt;em&gt;communicate()&lt;/em&gt; function. So what should our code look like to comply with our principle? Let's try to improve our classes a bit.&lt;/p&gt;

&lt;p&gt;We can start by adding an interface Communicative, and using it in our classes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;Communicative&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;speak&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Dog&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;Communicative&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;speak&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s1"&gt;'woof woof'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Duck&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;Communicative&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;speak&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s1"&gt;'quack quack'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Fox&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;Communicative&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;speak&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s1"&gt;'ring-ding-ding-ding-dingeringeding!, Wa-pa-pa-pa-pa-pa-pow!'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After that, we can change the Communication class so that it complies with the open/close principle.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Communication&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;communicate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Communicative&lt;/span&gt; &lt;span class="nv"&gt;$animal&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$animal&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;speak&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;How to code according to the opened/closed principle?&lt;/p&gt;

&lt;p&gt;In code, &lt;strong&gt;it is worth using interfaces&lt;/strong&gt; and sticking to them. However, if you need to change something, consider the decorator pattern.&lt;/p&gt;

&lt;p&gt;A class or method should be small enough and have one specific task so that no future event can necessitate modification (single responsibility principle). But &lt;strong&gt;you also need to consider&lt;/strong&gt; whether there may be a need for changes in the future, such as a new response format or an additional parameter, your code should be closed for modification.&lt;/p&gt;

&lt;h2&gt;
  
  
  Liskov substitution principle (LSP)
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxfxxvyeids9p3rxe2e5j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxfxxvyeids9p3rxe2e5j.png" alt="Liskov substitution principle (LSP)"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Substitution principle applies to well-designed class inheritance. The author of this principle is Barbara Liskov. The principle says that &lt;strong&gt;we can use any inheriting class in place of the base class&lt;/strong&gt;. If we implement a subclass, we must also be able to use it instead of the main class. Otherwise, it means that inheritance has been implemented incorrectly.&lt;/p&gt;

&lt;p&gt;There are some popular examples of the Liskov substitution principle in PHP:&lt;/p&gt;

&lt;h3&gt;
  
  
  a) rectangle-square
&lt;/h3&gt;

&lt;p&gt;The first example. We already have a Rectangle PHP class. Now we're adding a Square PHP class that inherits the Rectangle class. Because every square is also a rectangle :). They have the same properties, height and width.&lt;/p&gt;

&lt;p&gt;The height of the square is the same as the width. So, &lt;em&gt;setHeight()&lt;/em&gt; and &lt;em&gt;setWidth()&lt;/em&gt; will set both (what about single responsibility?) of these values:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Square&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Rectangle&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;setWidth&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nv"&gt;$width&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;width&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$width&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;height&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$width&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;setHeight&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nv"&gt;$height&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;width&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$height&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;height&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$height&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Is that a good solution? Unfortunately, &lt;strong&gt;it does not follow the Liskov substitution principle&lt;/strong&gt;. Let's say there is a test that computes the area of a rectangle, and it looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;testCalculateArea&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$shape&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Rectangle&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nv"&gt;$shape&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;setWidth&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nv"&gt;$shape&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;setHeight&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;assertEquals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$shape&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;calculateArea&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nv"&gt;$shape&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;setWidth&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;assertEquals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$shape&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;calculateArea&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;According to the Liskov substitution principle, we should be able to replace the Rectangle class with the Square class. But if we replace it, it turns out that the test does not pass (100 != 20). Overriding the &lt;em&gt;setWidth()&lt;/em&gt; and &lt;em&gt;setHight()&lt;/em&gt; methods broke the Liskov substitution rule. &lt;strong&gt;We should not change how the parent class's methods work&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;So what is the correct solution? &lt;strong&gt;Not every idea from "reality" should be implemented 1:1 in code&lt;/strong&gt;. The Square class should not inherit from the Rectangle class. If both of these classes can have a computed area, let them implement a common interface, and not inherit one from the other since they are quite different.&lt;/p&gt;

&lt;p&gt;You can see an example solution &lt;a href="https://github.com/accesto/solid-php/blob/master/liskov-substitution-principle/liskov-substitution-principle-good.php" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  b) live duck vs toy duck
&lt;/h3&gt;

&lt;p&gt;Imagine a living duck and a toy duck and their representations in the code (PHP classes). Both of these classes implement the &lt;em&gt;TheDuck&lt;/em&gt; interface.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;TheDuck&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;swim&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We also have a controller with the action &lt;em&gt;swim()&lt;/em&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SomeController&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;swim&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;releaseDucks&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
            &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;LiveDuck&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
            &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ToyDuck&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;releaseDucks&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;array&lt;/span&gt; &lt;span class="nv"&gt;$ducks&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="cd"&gt;/** @var TheDuck $duck */&lt;/span&gt;
        &lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$ducks&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nv"&gt;$duck&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nv"&gt;$duck&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;swim&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But after calling this action &lt;em&gt;ToyDuck&lt;/em&gt; doesn't swim. Why? Because to make it swim, you must first call the "&lt;em&gt;turnOn()&lt;/em&gt;" method.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ToyDuck&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;TheDuck&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="nv"&gt;$isTurnedOn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;swim&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; 
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;isTurnedOn&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="c1"&gt;// ...&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We could modify the controller action and add a condition that we call &lt;em&gt;turnOn()&lt;/em&gt; on the &lt;em&gt;ToyDuck&lt;/em&gt; instance before &lt;em&gt;swim()&lt;/em&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;releaseDucks&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;array&lt;/span&gt; &lt;span class="nv"&gt;$ducks&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="cd"&gt;/** @var TheDuck $duck */&lt;/span&gt;
    &lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$ducks&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nv"&gt;$duck&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$duck&lt;/span&gt; &lt;span class="k"&gt;instanceof&lt;/span&gt; &lt;span class="nc"&gt;ToyDuck&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nv"&gt;$duck&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;turnOn&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="nv"&gt;$duck&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;swim&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It violates the Liskov substitution principle, because we should be able to use a subclass without knowing the object, so &lt;strong&gt;we cannot condition by subclasses&lt;/strong&gt; (it also violates the open/close principle - because we need to change the implementation).&lt;/p&gt;

&lt;p&gt;Handling a collection of objects of a given base class may not require checking whether the given object is an instance of subclass X and should be treated differently.&lt;/p&gt;

&lt;p&gt;What should it look like correctly? Common interface for both of these ducks is not a good idea, their operation is completely different, &lt;strong&gt;even though we think they both work similarly&lt;/strong&gt; because they are swimming, it is not.&lt;/p&gt;

&lt;h3&gt;
  
  
  c) ReadOnlyFile
&lt;/h3&gt;

&lt;p&gt;And the last example. We have a &lt;em&gt;File&lt;/em&gt; class with methods &lt;em&gt;read()&lt;/em&gt; and &lt;em&gt;write()&lt;/em&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;File&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="c1"&gt;// ...&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="c1"&gt;// ...&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We're adding a new class - &lt;em&gt;ReadOnlyFile&lt;/em&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ReadOnlyFile&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;File&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ItsReadOnlyFileException&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;em&gt;ReadOnlyFile&lt;/em&gt; class inherits from the &lt;em&gt;File&lt;/em&gt; class. In the &lt;em&gt;ReadOnlyFile&lt;/em&gt; class, the &lt;em&gt;write()&lt;/em&gt; method will throw an Exception, because you cannot write to a read-only file. &lt;/p&gt;

&lt;p&gt;This is a &lt;strong&gt;poorly designed abstraction&lt;/strong&gt;, the Liskov rule has been broken because we are unable to use the &lt;em&gt;ReadOnlyFile&lt;/em&gt; class instead of &lt;em&gt;File&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Interface segregation principle (ISP)
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9z5u3ihgabxs264zx7ix.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9z5u3ihgabxs264zx7ix.png" alt="Interface segregation principle (ISP)"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Uncle Bob introduced this principle when he collaborated with Xerox. They couldn't cope with the ever-long process of implementing changes to their code. The rule is: “No client should be forced to depend on methods it does not use”. The user of the interface should not be forced to rely on methods he does not use. We should &lt;strong&gt;not use “fat interfaces”&lt;/strong&gt; that declare multiple methods if any of them could be left unused. Better to have a few dedicated small interfaces than one that is too general. It is also in line with the single responsibility principle.&lt;/p&gt;

&lt;p&gt;So let's see a badly written code, not following the interface segregation principle. I present to you the &lt;em&gt;Exportable&lt;/em&gt;, PHP Interface. An interface that allows you to export something to PDF and export something to CSV. We also have an &lt;em&gt;Invoice&lt;/em&gt; and a &lt;em&gt;CreditNote&lt;/em&gt; class.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;Exportable&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;getPDF&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;getCSV&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Invoice&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;Exportable&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;getPDF&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// ...&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;getCSV&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// ...&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CreditNote&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;Exportable&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;getPDF&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;\NotUsedFeatureException&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;getCSV&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// ...&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can download the Invoice in PDF and CSV. We can download a CSV of the CreditNote. But downloading the PDF of the CreditNote was a useless functionality and was not implemented (it’s throwing an exception right now).&lt;/p&gt;

&lt;p&gt;We shouldn't force our interface implementations to implement methods they don't use. In the above case, we forced the CreditNote class to do so, it implements the &lt;em&gt;getPDF()&lt;/em&gt; method even though &lt;strong&gt;it does not need it at all&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;So how should it look to be good?&lt;/p&gt;

&lt;p&gt;According to the interface segregation principle, &lt;strong&gt;we have to separate the interfaces&lt;/strong&gt;. We divide &lt;em&gt;Exportable&lt;/em&gt;, and create an interface &lt;em&gt;ExportablePdf&lt;/em&gt; and create an interface &lt;em&gt;ExportableCSV&lt;/em&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;ExportablePdf&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;getPDF&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;ExportableCSV&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;getCSV&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Invoice&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;ExportablePdf&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;ExportableCSV&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;getPDF&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;//&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;getCSV&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;//&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CreditNote&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;ExportableCSV&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;getCSV&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;//&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This way, CreditNote no longer has to worry about implementing not used the &lt;em&gt;getPDF()&lt;/em&gt; public function. If necessary in the future, just need to use a separate interface and implement it. As you can see here, specific interfaces are better.&lt;/p&gt;

&lt;p&gt;The example about ReadOnlyFile related to the Liskov principle is also a good example for the Interface segregation principle. There, the File &lt;strong&gt;class has been doing too many things&lt;/strong&gt;, it's better to have separate interfaces for each action.&lt;/p&gt;

&lt;p&gt;That's interface segregation, easy.&lt;/p&gt;

&lt;h2&gt;
  
  
  Dependency inversion principle (DIP)
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4nfwdbeg7phlcff9efvd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4nfwdbeg7phlcff9efvd.png" alt="Dependency inversion principle (DIP)"&gt;&lt;/a&gt;&lt;br&gt;
Last form SOLID principles, this rule is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;High-level modules should not import anything from low-level modules. Both should depend on abstractions (e.g., interfaces).&lt;/li&gt;
&lt;li&gt;Abstractions should not depend on details. Details (concrete implementations) should depend on abstractions.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What does it mean? We should reduce dependencies to specific implementations, but &lt;strong&gt;rely on interfaces&lt;/strong&gt;. If we make any change to the interface (it violates the open/close principle), this change necessitates changes in the implementations of this interface. But if we need to change a specific implementation, we probably don't need to change our interface.&lt;/p&gt;

&lt;p&gt;To illustrate the problem, let's go over this PHP example.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DatabaseLogger&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;logError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// ..&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here we have a class that logs some information to the database. Now use this class.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MailerService&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;DatabaseLogger&lt;/span&gt; &lt;span class="nv"&gt;$logger&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;__construct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;DatabaseLogger&lt;/span&gt; &lt;span class="nv"&gt;$logger&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;logger&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$logger&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;sendEmail&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// ..&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;SomeException&lt;/span&gt; &lt;span class="nv"&gt;$exception&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;logError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$exception&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getMessage&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here is the PHP class that sends e-mails, in case of an error, error details are logged to the database using the logger we have just seen above.&lt;/p&gt;

&lt;p&gt;It breaks the principle of &lt;strong&gt;dependency inversion&lt;/strong&gt;. Our e-mail sending service uses a specific logger implementation. What if we want to log information about errors to a file or to Sentry? We will have to change &lt;em&gt;MailerService&lt;/em&gt;. This is not a flexible solution, such a replacement becomes problematic.&lt;/p&gt;

&lt;p&gt;So what should it look like?&lt;/p&gt;

&lt;p&gt;According to this principle, &lt;em&gt;MailerService&lt;/em&gt; &lt;strong&gt;should rely on abstraction&lt;/strong&gt; rather than detailed implementation. Therefore, we are adding the LoggerInterface interface.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;LoggerInterface&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;logError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$message&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And we use it in our &lt;em&gt;DatabaseLogger&lt;/em&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DatabaseLogger&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;LoggerInterface&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;logError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$message&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;
   &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="c1"&gt;// ..&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, we can take advantage of &lt;strong&gt;&lt;a rel="nofollow noopener noreferrer" href="https://symfony.com/doc/current/components/dependency_injection.html"&gt;Symfony Dependency Injection&lt;/a&gt;&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MailerService&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;LoggerInterface&lt;/span&gt; &lt;span class="nv"&gt;$logger&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;sendEmail&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// ..&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;SomeException&lt;/span&gt; &lt;span class="nv"&gt;$exception&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;logError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$exception&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getMessage&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this way, we can freely replace the logs in the database with logs wherever we want, as long as the detailed implementation implements the &lt;em&gt;LoggerInterface&lt;/em&gt;. This change will not require modifying &lt;em&gt;MailerService&lt;/em&gt;, because it does not depend on it, &lt;strong&gt;it depends only on the interface&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  SOLID
&lt;/h2&gt;

&lt;p&gt;All these principles come together as one, they often overlap. It's nice when you know the theory like &lt;strong&gt;SOLID&lt;/strong&gt; principles because &lt;strong&gt;it makes it easier to make good code&lt;/strong&gt;. Then you also have strong arguments behind your code, for example in code review. All the rules are aimed at making the code easy to understand and maintain.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SOLID&lt;/strong&gt; is one of the many good practices that help us write clean code. I've written about the &lt;a href="https://accesto.com/blog/Boy-scout-rule-in-6-examples-the-basic-principle-of-web-development/" rel="noopener noreferrer"&gt;Boy Scout Rule&lt;/a&gt; before. But that's not all, there are many other rules and standards to follow. Let me just mention them:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;PSR&lt;/strong&gt; (PHP Standards Recommendations) - PHP Framework Interop Group (PHP-FIG) is a group of people associated with the largest PHP projects who jointly develop PSR. I think every PHP programmer should know coding styles standards PSR-1 and PSR-12 (formerly PSR-2). You can find all the current sets of standards &lt;a rel="nofollow noopener noreferrer" href="https://www.php-fig.org/psr/"&gt;here&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;KISS&lt;/strong&gt; (Keep It Simple Stupid) - Don't complicate the code. The code should be its documentation itself. Any new programmer on the team should be able to get into the project quickly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DRY&lt;/strong&gt; (Don’t Repeat Yourself) - Do not code using the Copy-Paste principle (there is no such rule). See that the same code repeats in several places? Extract code for a separate function.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;YAGNI&lt;/strong&gt; (You Aren’t Gonna Need It) - 17th-century German philosopher Johannes Clauberg formulated a principle called Occam's Razor (I was also surprised Ockham was not its author ;) ) “entities should not be multiplied beyond necessity". I think this sentence expresses the YAGNI principle well. We should not write code “for the future”. Such code is not needed at the moment.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GRASP&lt;/strong&gt; (General Responsibility Assignment Software Patterns) - is a large set of rules about which I could write a separate article. These are the basic principles that we should follow when creating object design and responsibility assignments. It consists of: Information Expert, Controller, Creator, High Cohesion, Low Coupling, Pure Fabrication, Polymorphism, Protected Variations, Indirection.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Applying the &lt;strong&gt;SOLID&lt;/strong&gt; principles in our daily work helps us not get into technical debt. What are the consequences of incurring technical debt, you can find out in the &lt;a href="https://accesto.com/blog/technical-debt-the-silent-villain-of-web-development/" rel="noopener noreferrer"&gt;article&lt;/a&gt; written by our CEO Piotr.&lt;/p&gt;

&lt;p&gt;If you have problems understanding your project. Write to us, we have experience in dealing with &lt;a href="https://accesto.com/case-study/legacy/" rel="noopener noreferrer"&gt;difficult cases&lt;/a&gt; and &lt;a href="https://accesto.com/services/php-refactoring-services/" rel="noopener noreferrer"&gt;PHP refactoring&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>php</category>
      <category>symfony</category>
      <category>programming</category>
      <category>codequality</category>
    </item>
  </channel>
</rss>
