<?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: Filmin Engineering</title>
    <description>The latest articles on DEV Community by Filmin Engineering (@filmineng).</description>
    <link>https://dev.to/filmineng</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%2Forganization%2Fprofile_image%2F4109%2F76d86af2-46ba-4fe5-a878-2243142a5d4d.jpeg</url>
      <title>DEV Community: Filmin Engineering</title>
      <link>https://dev.to/filmineng</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/filmineng"/>
    <language>en</language>
    <item>
      <title>Stopping time with PHP</title>
      <dc:creator>Rubén Rubio</dc:creator>
      <pubDate>Tue, 09 Nov 2021 08:16:16 +0000</pubDate>
      <link>https://dev.to/filmineng/stopping-time-with-php-3m8k</link>
      <guid>https://dev.to/filmineng/stopping-time-with-php-3m8k</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;It is a good practice to use an interface to manage the clock in an application, as it allows having full control of time. For example, it eases testing, as it lets us define the concrete time for each test. &lt;a href="https://blog.frankdejonge.nl/being-in-control-of-time-in-php/" rel="noopener noreferrer"&gt;Frank de Jonge&lt;/a&gt; and &lt;a href="https://matthiasnoback.nl/2018/02/mocking-at-architectural-boundaries-persistence-and-time/" rel="noopener noreferrer"&gt;Matthias Noback&lt;/a&gt; have blog posts about it, &lt;a href="https://github.com/brick/date-time" rel="noopener noreferrer"&gt;brick has an implementation&lt;/a&gt;, and there is even a &lt;a href="https://github.com/php-fig/fig-standards/blob/master/proposed/clock.md" rel="noopener noreferrer"&gt;PSR proposal to have a &lt;code&gt;ClockInterface&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;However, there are more use cases for &lt;code&gt;ClockInterface&lt;/code&gt;, besides testing. In our case, we had to implement endpoints to show the movies that are just released, and the ones that are about to release in the next few days or weeks.&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%2Fdqhh5u7i8boiwa32w7kh.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%2Fdqhh5u7i8boiwa32w7kh.png" alt="Filmin coming soon page"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Being an API, it is easy to test using PHPUnit, as you could easily set the required date for each test. However, our frontend team had to integrate the website (a Vue app) with this endpoint. This feature is easy to develop and test locally if you have a fresh backup of the production database, as the date of your local system will be in sync with the data within your local database. However, what if you do not have your local database updated, and getting a copy from production takes too much time?&lt;/p&gt;

&lt;p&gt;For sure, an option would be to update the local data either by querying directly the database or by using a back-office, if there is one. Nonetheless, this is a manual task that could take some time if you have to update many elements. And it is something that you have to do recurrently if you need to review the implementation after the date you set up passes.&lt;/p&gt;

&lt;p&gt;But there is another option: we can take advantage of the &lt;code&gt;ClockInterface&lt;/code&gt; to fixate the date of the whole system.&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementation
&lt;/h2&gt;

&lt;p&gt;Having a &lt;code&gt;ClockInterface&lt;/code&gt; such as:&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;ClockInterface&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;now&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;DateTimeImmutable&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 have this &lt;code&gt;FixedClock&lt;/code&gt; implementation:&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;DateTimeImmutable&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;DateTimeZone&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;InvalidArgumentException&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;final&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;FixedClock&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;ClockInterface&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;const&lt;/span&gt; &lt;span class="no"&gt;FORMAT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'Y-m-d H:i:s'&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;?DateTimeImmutable&lt;/span&gt; &lt;span class="nv"&gt;$dateTime&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;DateTimeZone&lt;/span&gt; &lt;span class="nv"&gt;$timeZone&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;string&lt;/span&gt; &lt;span class="nv"&gt;$timeZone&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;$dateTime&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;''&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;timeZone&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="nf"&gt;parseAndValidateTimeZone&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$timeZone&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;dateTime&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="nf"&gt;parseAndValidateDateTime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$dateTime&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;now&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;DateTimeImmutable&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;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;dateTime&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;null&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="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;dateTime&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="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;DateTimeImmutable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'now'&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;timeZone&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;parseAndValidateTimeZone&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;$timeZone&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;DateTimeZone&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="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;DateTimeZone&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$timeZone&lt;/span&gt;&lt;span class="p"&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;Exception&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;InvalidArgumentException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="nb"&gt;sprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Value "%s" for time zone is not valid.'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$timeZone&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;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;parseAndValidateDateTime&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;$dateTime&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;?DateTimeImmutable&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$parsedDateTime&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$dateTime&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;$parsedDateTime&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="s1"&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;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="nv"&gt;$dateTimeImmutable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;DateTimeImmutable&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;createFromFormat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;FORMAT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nv"&gt;$parsedDateTime&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;timeZone&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;$dateTimeImmutable&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;InvalidArgumentException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="nb"&gt;sprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                    &lt;span class="s1"&gt;'Value "%s" for date time is not valid. Expected to have the format "%s"'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="nv"&gt;$dateTime&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;FORMAT&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;return&lt;/span&gt; &lt;span class="nv"&gt;$dateTimeImmutable&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 is not a good practice to perform anything besides assign properties in the constructor, but it will prove useful afterwards.&lt;/p&gt;

&lt;h2&gt;
  
  
  Configuration
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Symfony
&lt;/h3&gt;

&lt;p&gt;We only need to set the fixed clock up for our development environment. By default, Symfony loads the &lt;code&gt;services.yaml&lt;/code&gt; file and then the &lt;code&gt;services_{environment.yaml}&lt;/code&gt;, so you can override any service on per-environment basis. To do so, we add to our &lt;code&gt;services_dev.yaml&lt;/code&gt; the following lines:&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;timezone&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;%env(string:TIMEZONE)%'&lt;/span&gt;
    &lt;span class="na"&gt;now&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;%env(string:NOW)%'&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;_defaults&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;autowire&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
        &lt;span class="na"&gt;autoconfigure&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;

    &lt;span class="na"&gt;Filmin\SharedKernel\Domain\ValueObject\Time\FixedClock&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;arguments&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;%timezone%'&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;%now%'&lt;/span&gt;

    &lt;span class="na"&gt;Filmin\SharedKernel\Domain\ValueObject\Time\ClockInterface&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;@Filmin\SharedKernel\Domain\ValueObject\Time\FixedClock'&lt;/span&gt;



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

&lt;/div&gt;

&lt;p&gt;What we do here is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Lines 1-3: set up container parameters that contain the timezone and the current time from environment variables.&lt;/li&gt;
&lt;li&gt;Lines 9-12: setup our &lt;code&gt;FixedClock&lt;/code&gt; implementation as a service. In Symfony, we can not use named constructors when defining value objects as services. Thereby, we validated the values within the constructor, as shown above.&lt;/li&gt;
&lt;li&gt;Line 14: alias the &lt;code&gt;ClockInterface&lt;/code&gt; to our &lt;code&gt;FixedClock&lt;/code&gt;, so any service that type hint a &lt;code&gt;ClockInterface&lt;/code&gt; will use the &lt;code&gt;FixedClock&lt;/code&gt; implementation, thus overriding any previous, generic definition.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If we do not set up any value for the environment variable &lt;code&gt;NOW&lt;/code&gt;, the application will use the system time. Therefore, switching between a fixed time and the system time is as easy as to update an environment variable.&lt;/p&gt;

&lt;h3&gt;
  
  
  Laravel
&lt;/h3&gt;

&lt;p&gt;In a Laravel application, we would configure the &lt;code&gt;ClockInterface&lt;/code&gt; within a service provider using a singleton, such as:&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;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;singleton&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ClockInterface&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Application&lt;/span&gt; &lt;span class="nv"&gt;$app&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;$app&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'local'&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="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;FixedClock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'TIMEZONE'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nb"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'NOW'&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="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;SystemClock&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 case, &lt;code&gt;SystemClock&lt;/code&gt; is an implementation of &lt;code&gt;ClockInterface&lt;/code&gt; that uses the system time, that is what we want in production. As with the previous case, switching between the system time and a fixed time is as easy as to update an environment variable.&lt;/p&gt;

&lt;p&gt;Note that we are not experts in Laravel, so this configuration could probably be improved.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;We saw that using a &lt;code&gt;ClockInterface&lt;/code&gt; eases the testing process and allow fixating the time of the whole application, that can be useful for local testing purposes.&lt;/p&gt;

&lt;p&gt;If you are implementing a new application, it is simple to start using &lt;code&gt;ClockInterface&lt;/code&gt; everywhere. However, if you are working on legacy code, it could take some time until you have the &lt;code&gt;ClockInterface&lt;/code&gt; in your whole application. In those cases, it could be useful to apply the boy scout rule: "Always leave the code better than you found it".&lt;/p&gt;

</description>
      <category>php</category>
      <category>datetime</category>
      <category>symfony</category>
      <category>laravel</category>
    </item>
    <item>
      <title>PHP libraries and tools</title>
      <dc:creator>Rubén Rubio</dc:creator>
      <pubDate>Wed, 22 Sep 2021 10:10:25 +0000</pubDate>
      <link>https://dev.to/filmineng/php-libraries-and-tools-3blf</link>
      <guid>https://dev.to/filmineng/php-libraries-and-tools-3blf</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;We want the new project we are starting at Filmin to be robust. Therefore, besides adopting good coding practices, we checked some PHP libraries and tools that can help us improve its quality.&lt;/p&gt;

&lt;p&gt;Here is a list of the tools we analyzed with the description they have in its Github repository, excluding well known tools or libraries, such as PHPUnit.&lt;/p&gt;

&lt;p&gt;Most of these tools are CLI commands, so they can be integrated in a CI/CD flow. Others can be integrated within the IDE, such as Psalm or PHPStan in PHPStorm. And others are libraries that we use in our project.&lt;/p&gt;

&lt;p&gt;The list is based in our experience, and it is heavily influenced by:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://twitter.com/ramsey/status/1396592906102722561"&gt;https://twitter.com/ramsey/status/1396592906102722561&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://twitter.com/lulco/status/1397813303037079553"&gt;https://twitter.com/lulco/status/1397813303037079553&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://twitter.com/ArkadiuszKondas/status/1338485275002068993"&gt;https://twitter.com/ArkadiuszKondas/status/1338485275002068993&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://youtu.be/8rdTSYljts4"&gt;Aggressive PHP Quality Assurance in 2019 | Marco Pivetta&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Libraries
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/brick/date-time"&gt;brick/date-time&lt;/a&gt;: Date and time library for PHP&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/thecodingmachine/safe8/"&gt;thecodingmachine/safe&lt;/a&gt;: All PHP functions, rewritten to throw exceptions instead of returning false, now for php8&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://tactician.thephpleague.com/"&gt;Tactician&lt;/a&gt;: A small, flexible command bus&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/ronanguilloux/IsoCodes"&gt;ronanguilloux/isocodes&lt;/a&gt;: PHP library - Validators for standards from ISO, International Finance, Public Administrations, GS1, Manufacturing Industry, Phone numbers &amp;amp; Zipcodes for many countries&lt;/li&gt;
&lt;li&gt;
&lt;a href="http://opensource.box.com/spout/"&gt;box/spout&lt;/a&gt;: Read and write spreadsheet files (CSV, XLSX and ODS), in a fast and scalable way&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://flysystem.thephpleague.com/v2/docs/"&gt;league/flysystem&lt;/a&gt;: Abstraction for local and remote filesystems&lt;/li&gt;
&lt;li&gt;Serializer:

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://symfony.com/doc/current/components/serializer.html"&gt;Symfony Serializer Component&lt;/a&gt;: The Serializer component is meant to be used to turn objects into a specific format (XML, JSON, YAML, …) and the other way around.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://jmsyst.com/libs/serializer"&gt;JMS Serializer&lt;/a&gt;: This library allows you to (de-)serialize data of any complexity. Currently, it supports XML and JSON.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://fractal.thephpleague.com/serializers/"&gt;Fractal&lt;/a&gt;: Fractal provides a presentation and transformation layer for complex data output, the like found in RESTful APIs, and works really well with JSON. Think of this as a view layer for your JSON/YAML/etc.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Tools
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Code quality
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/qossmic/deptrac"&gt;Deptrac&lt;/a&gt;: Keep your architecture clean.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://phpinsights.com/"&gt;PHP Insights&lt;/a&gt;: Instant PHP quality checks from your console&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/phpro/grumphp"&gt;GrumPHP&lt;/a&gt;: A PHP code-quality tool.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/bmitch/churn-php"&gt;churn-php&lt;/a&gt;: Discover files in need of refactoring.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://psalm.dev/"&gt;Psalm&lt;/a&gt;: A static analysis tool for finding errors in PHP applications. Plugins:

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/boesing/psalm-plugin-stringf"&gt;boesing/psalm-plugin-stringf&lt;/a&gt;: Psalm plugin to provide more details for sprintf, printf, sscanf and fscanf functions.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/hectorj/safe-php-psalm-plugin"&gt;hectorj/safe-php-psalm-plugin&lt;/a&gt;: vimeo/psalm plugin for thecodingmachine/safe.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/marartner/psalm-no-empty"&gt;marartner/psalm-no-empty&lt;/a&gt;: Psalm plugin to detect usage of empty().&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/marartner/psalm-strict-equality"&gt;marartner/psalm-strict-equality&lt;/a&gt;: Psalm plugin to enforce strict equality.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/psalm/psalm-plugin-phpunit"&gt;psalm/plugin-phpunit&lt;/a&gt;: A PHPUnit plugin for Psalm.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/psalm/psalm-plugin-symfony"&gt;psalm/plugin-symfony&lt;/a&gt;: Psalm Plugin for Symfony.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/psalm/psalm-plugin-doctrine"&gt;weirdan/doctrine-psalm-plugin&lt;/a&gt;: Stubs to let Psalm understand Doctrine better.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://phpstan.org/"&gt;PHPStan&lt;/a&gt;: PHP Static Analysis Tool - discover bugs in your code without running it!. Plugins:

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/ergebnis/phpstan-rules"&gt;ergebnis/phpstan-rules&lt;/a&gt;: Provides additional rules for phpstan/phpstan.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/spaze/phpstan-disallowed-calls"&gt;spaze/phpstan-disallowed-calls&lt;/a&gt;: PHPStan rules to detect disallowed calls and constant &amp;amp; namespace usages&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/Roave/no-floaters"&gt;roave/no-floaters&lt;/a&gt;: static analysis rules to prevent IEEE-754 floating point errors.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://phpstan.org/user-guide/extension-library"&gt;More extensions&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Testing
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://infection.github.io/"&gt;Infection&lt;/a&gt;: PHP Mutation Testing library. Plugins:

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/Roave/infection-static-analysis-plugin"&gt;roave/infection-static-analysis-plugin&lt;/a&gt;: Static analysis on top of mutation testing - prevents escaped mutants from being invalid according to static analysis&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/bitExpert/captainhook-infection"&gt;bitexpert/captainhook-infection&lt;/a&gt;: Captain Hook Plugin to run InfectionPHP only against the changed files of a commit&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/Roave/no-leaks"&gt;roave/no-leaks&lt;/a&gt;: PHPUnit Plugin for detecting Memory Leaks in code and tests&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/lulco/populator"&gt;lulco/populator&lt;/a&gt;: Allows populate fake data to your database.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/thephpleague/openapi-psr7-validator"&gt;OpenAPI PSR-7 Message (HTTP Request/Response) Validator&lt;/a&gt;:
It validates PSR-7 messages (HTTP request/response) against OpenAPI specifications.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/paratestphp/paratest"&gt;Paratest&lt;/a&gt;: Parallel testing for PHPUnit&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Composer tools
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/maglnet/ComposerRequireChecker"&gt;ComposerRequireChecker&lt;/a&gt;: A CLI tool to check whether a specific composer package uses imported symbols that aren't part of its direct composer dependencies&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/composer-unused/composer-unused"&gt;composer-unused&lt;/a&gt;: Show unused composer dependencies by scanning your code&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/ergebnis/composer-normalize"&gt;composer-normalize&lt;/a&gt;: Provides a composer plugin for normalizing composer.json.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/Roave/SecurityAdvisories"&gt;roave/security-advisories&lt;/a&gt;: Security advisories as a simple composer exclusion list, updated daily&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/Roave/BackwardCompatibilityCheck"&gt;roave/backward-compatibility-check&lt;/a&gt;: Tool to compare two revisions of a class API to check for BC breaks&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/fabpot/local-php-security-checker"&gt;Local PHP Security Checker&lt;/a&gt;: PHP security vulnerabilities checker&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  General
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://robo.li/"&gt;Robo&lt;/a&gt;: Modern Task Runner for PHP&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/captainhookphp/captainhook"&gt;CaptainHook&lt;/a&gt;: Very flexible git hook manager for php developers&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;There is a huge amount of tools written by the PHP community that helps us increase the quality of our project, and that ease our task as developers. We need to choose the ones that adapt better to our project and use case.&lt;/p&gt;

&lt;p&gt;And you? Do you know any interesting tool that is not in this list?&lt;/p&gt;

</description>
      <category>php</category>
      <category>tooling</category>
      <category>codequality</category>
      <category>libraries</category>
    </item>
    <item>
      <title>Testing strategies</title>
      <dc:creator>Rubén Rubio</dc:creator>
      <pubDate>Wed, 08 Sep 2021 10:30:25 +0000</pubDate>
      <link>https://dev.to/filmineng/testing-strategies-14jk</link>
      <guid>https://dev.to/filmineng/testing-strategies-14jk</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;We are starting a new project at Filmin, so we are having some discussions to take decisions. We want tests to be one of the cornerstones of this project, so we needed to decide how to implement them.&lt;/p&gt;

&lt;p&gt;However, we have not found yet a clear, unique definition of the different kind of testing strategies. What is the scope of an integration test? Should it also test the whole framework? What is the difference between acceptance tests and end-to-end tests?&lt;/p&gt;

&lt;p&gt;As we did not have any unique guide, what we did was to have a dialogue between all the team members, interchanging our expertise and viewpoints, to get to a consensus.&lt;/p&gt;

&lt;p&gt;These are the testing strategies we agreed on. Note that it is not meant to be an exhaustive list, neither an academic research: these are testing strategies that best suits us for the new project.&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing strategies
&lt;/h2&gt;

&lt;p&gt;We are using hexagonal architecture, so for each kind of test we will write which layers it applies to. Besides, as we are using PHP, we will write the tool we will use for that testing strategy.&lt;/p&gt;

&lt;h3&gt;
  
  
  Unit
&lt;/h3&gt;

&lt;p&gt;Unit tests check every part of the software in isolation. Unit tests do not go beyond its scope (i.e., memory) to run,&lt;br&gt;
so they use stubs for interfaces defined within the Domain Layer. Their execution must be fast.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Who needs to understand them: developer. &lt;/li&gt;
&lt;li&gt;Apply to:

&lt;ul&gt;
&lt;li&gt;Domain layer&lt;/li&gt;
&lt;li&gt;Application layer&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Tool: &lt;a href="https://phpunit.de/"&gt;PHPUnit&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Integration
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://martinfowler.com/bliki/IntegrationTest.html"&gt;Integration tests&lt;/a&gt; check that a concrete implementation of a&lt;br&gt;
Domain interface works as expected —but it is not only restricted to elements in Domain.&lt;br&gt;
An example could be a query to the database to check if filtering or pagination work as expected.&lt;/p&gt;

&lt;p&gt;They usually require external infrastructure in order to run, such as a Database or an AMQP instance. Locally, these&lt;br&gt;
tests will run against the local Docker instance. In CI/CD, the pipeline will prepare the required infrastructure using&lt;br&gt;
and building a Docker image.&lt;/p&gt;

&lt;p&gt;Usually we will need fixtures for the database, i.e., elements in it that we can query or act on. In that case, we will&lt;br&gt;
load the fixtures querying the database directly, loading the bunch of SQL files with queries that we will need. Each test case will be wrapped in a transaction, so they will be isolated.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Who needs to understand them: developer.&lt;/li&gt;
&lt;li&gt;Apply to:

&lt;ul&gt;
&lt;li&gt;Infrastructure layer&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Tool: &lt;a href="https://phpunit.de/"&gt;PHPUnit&lt;/a&gt; + Docker with all infrastructure elements needed.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Functional tests
&lt;/h3&gt;

&lt;p&gt;Functional tests are a "unitary" test of the whole stack (including the framework). They should check that the whole&lt;br&gt;
layers (Domain -&amp;gt; Application -&amp;gt; Infrastructure) work properly together. They should check that the expected changes&lt;br&gt;
in infrastructure are correctly done, for example, that a row is inserted in the database or that a message is sent to&lt;br&gt;
Rabbit.&lt;/p&gt;

&lt;p&gt;Functional tests also check the response: its status code, its possible responses —including errors—, its possible&lt;br&gt;
formats —JSON, XML, Proto...&lt;/p&gt;

&lt;p&gt;Like integration tests, functional tests require infrastructure to run. We will follow the same strategy as with&lt;br&gt;
integration tests.&lt;/p&gt;

&lt;p&gt;As integration tests, functional tests also require loading fixtures to the database previously. We will follow the&lt;br&gt;
same strategy as with integration tests.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Who needs to understand them: developer.&lt;/li&gt;
&lt;li&gt;Apply to:

&lt;ul&gt;
&lt;li&gt;The whole stack, including the framework.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Tool: &lt;a href="https://phpunit.de/"&gt;PHPUnit&lt;/a&gt; + Docker with all infrastructure elements needed.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We will implement functional tests for all endpoints.&lt;/p&gt;

&lt;h3&gt;
  
  
  Acceptance tests
&lt;/h3&gt;

&lt;p&gt;Acceptance tests need context to be run and understood. They may consist on testing the same endpoint multiple times,&lt;br&gt;
with different use cases. Acceptance tests check the behavior of the application, without regarding infrastructure&lt;br&gt;
details. For example, we will not check the database if the tests does not say so.&lt;/p&gt;

&lt;p&gt;These kinds of test should be able to be written using &lt;a href="https://cucumber.io/docs/gherkin/"&gt;Gherkin syntax&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The difference between functional and acceptance tests is blurry, so the developer will have to decide each time the&lt;br&gt;
place where the test belongs.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Who needs to understand them: product owner.&lt;/li&gt;
&lt;li&gt;Apply to:

&lt;ul&gt;
&lt;li&gt;Application and Domain layers, if we want to test only business rules. &lt;/li&gt;
&lt;li&gt;The whole stack, including the framework, if we want to test the system as a whole.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Possible tools:

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://phpunit.de/"&gt;PHPUnit&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.behat.org/en/latest/"&gt;Behat&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://codeception.com/"&gt;Codeception&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  End-to-end tests
&lt;/h3&gt;

&lt;p&gt;End-to-end tests checks user behavior, or, as we greed on, an interface. In the case the project is an API, this kind of tests will not apply.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Who needs to understand them: user.&lt;/li&gt;
&lt;li&gt;Apply to:

&lt;ul&gt;
&lt;li&gt;The whole stack, including the framework and even the web server.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Tools:

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.cypress.io/"&gt;Cypress&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.selenium.dev/"&gt;Selenium&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://symfony.com/blog/announcing-symfony-panther-1-0"&gt;Panther&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Benchmark tests
&lt;/h3&gt;

&lt;p&gt;Benchmark tests check that a concrete functionality gives a response within an accepted threshold. For example, we&lt;br&gt;
may want that a core endpoint call or function gives a response in less than 0.5 seconds.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Who needs to understand them: developer.&lt;/li&gt;
&lt;li&gt;Apply to:

&lt;ul&gt;
&lt;li&gt;Infrastructure/Application/Domain: if we want to test how performant the code is.&lt;/li&gt;
&lt;li&gt;The whole stack, including the framework and even the web server: if we want to test the whole infrastructure.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Tools:

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://phpbench.readthedocs.io/en/latest/"&gt;PHPBench&lt;/a&gt; (for code)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://jmeter.apache.org/"&gt;JMeter&lt;/a&gt; (for infrastructure)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;As said, these are the kind of tests that we agreed on, not pretending to be an exhaustive list nor an academic research. Thus, it is open to future modifications or insertions, depending on what we learn.&lt;/p&gt;

&lt;p&gt;And you? Which kind of tests do you implement in your project? Do your testing strategies agree with ours?&lt;/p&gt;

</description>
      <category>testing</category>
      <category>php</category>
      <category>programming</category>
      <category>codequality</category>
    </item>
    <item>
      <title>Money pattern in PHP</title>
      <dc:creator>Rubén Rubio</dc:creator>
      <pubDate>Mon, 28 Jun 2021 09:20:04 +0000</pubDate>
      <link>https://dev.to/filmineng/money-pattern-in-php-3phj</link>
      <guid>https://dev.to/filmineng/money-pattern-in-php-3phj</guid>
      <description>&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%2F0sp6ngvdsk6grva69iyd.jpg" 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%2F0sp6ngvdsk6grva69iyd.jpg" alt="Shut up and take my money"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;As a business requirement, we needed to implement a new payment method. All our payments methods are in legacy code, so we took the opportunity to implement a way to manage amounts, trying to avoid problems we found in the past.&lt;/p&gt;

&lt;p&gt;Besides an amount, a money always has a currency. When we say a product costs 7.99, what are we referring to? €7.99? $7.99? ¥7.99? The price of a product can vary a lot if we do not take into account the different currencies.&lt;/p&gt;

&lt;p&gt;For small businesses that only operate in a country or a region with a single currency, it may have sense to always assume the use of this currency everywhere. If the application does not expand to a region with a different currency, this assumption will be safe. But we can never be a hundred percent sure that this will not happen in the future.&lt;/p&gt;

&lt;p&gt;To solve this problem, we can use a pattern know as &lt;a href="https://martinfowler.com/eaaCatalog/money.html" rel="noopener noreferrer"&gt;"Money pattern"&lt;/a&gt;, that consists on having a Value Object that has two attributes: the amount and the currency.&lt;/p&gt;

&lt;h2&gt;
  
  
  Precision
&lt;/h2&gt;

&lt;p&gt;Representing numbers in software is always a complex task. As numbers usually have decimal parts, our first intuition to represent an amount is to use floating point numbers. But floating point numbers have a limited amount of decimals they are able to represent, as the machine's memory is limited. For example, the number 2/3 is 0.666... At some point, the system will round the value, for example, to 0.666667. Therefore, there will always be some loss of precision when using floating point numbers.&lt;/p&gt;

&lt;p&gt;We can try to solve this by rounding the numbers at some point during the execution.&lt;/p&gt;

&lt;h3&gt;
  
  
  PHP Functions
&lt;/h3&gt;

&lt;p&gt;In PHP, we have multiple functions that allow to round numbers. These are some of them:&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;code&gt;floor&lt;/code&gt;
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nb"&gt;floor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$amount&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Returns the next lowest integer value (as float) by rounding down value if necessary&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;code&gt;ceil&lt;/code&gt;
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nb"&gt;ceil&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Returns the next highest integer value by rounding up the value if necessary.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;code&gt;round&lt;/code&gt;
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nb"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$amount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$precision&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$mode&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Returns the rounded value of val to specified precision (number of digits after the decimal point). Precision can also be negative or zero (default).&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;code&gt;number_format&lt;/code&gt;
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nb"&gt;number_format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$amount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$decimals&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Formats a number with grouped thousands and optionally decimal digits.&lt;/p&gt;

&lt;h3&gt;
  
  
  Example
&lt;/h3&gt;

&lt;p&gt;Let's see a real example to see how this works.&lt;/p&gt;

&lt;p&gt;I worked in an e-commerce project that had a back-office where shop administrators could set the base price (without taxes) for their products. The VAT amount (of 21%) and the final price were automatically calculated from that base price.&lt;/p&gt;

&lt;p&gt;This concrete administrator wanted to have two different products, with a final price (including taxes) of €5.50 and €5.30. We had a user that bought 5 units of each product. So, ideally, the total price for that order would be €54.00:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Base price (€)&lt;/th&gt;
&lt;th&gt;VAT 21% (€)&lt;/th&gt;
&lt;th&gt;Total (€)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;4.55&lt;/td&gt;
&lt;td&gt;0.95&lt;/td&gt;
&lt;td&gt;5.50&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4.55&lt;/td&gt;
&lt;td&gt;0.95&lt;/td&gt;
&lt;td&gt;5.50&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4.55&lt;/td&gt;
&lt;td&gt;0.95&lt;/td&gt;
&lt;td&gt;5.50&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4.55&lt;/td&gt;
&lt;td&gt;0.95&lt;/td&gt;
&lt;td&gt;5.50&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4.55&lt;/td&gt;
&lt;td&gt;0.95&lt;/td&gt;
&lt;td&gt;5.50&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4.38&lt;/td&gt;
&lt;td&gt;0.92&lt;/td&gt;
&lt;td&gt;5.30&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4.38&lt;/td&gt;
&lt;td&gt;0.92&lt;/td&gt;
&lt;td&gt;5.30&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4.38&lt;/td&gt;
&lt;td&gt;0.92&lt;/td&gt;
&lt;td&gt;5.30&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4.38&lt;/td&gt;
&lt;td&gt;0.92&lt;/td&gt;
&lt;td&gt;5.30&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4.38&lt;/td&gt;
&lt;td&gt;0.92&lt;/td&gt;
&lt;td&gt;5.30&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;44.63&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;9.37&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;54.00&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;However, things were not that nice. The back-office form allowed the administrator to introduce three decimal point numbers in order to (unsuccessfully) avoid rounding problems. The values were rounded before saving. Therefore, the value that was sent to the bank to charge the client for was as follows:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Base price (€)&lt;/th&gt;
&lt;th&gt;VAT 21% (€)&lt;/th&gt;
&lt;th&gt;Total (€)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;4.545&lt;/td&gt;
&lt;td&gt;0.9545&lt;/td&gt;
&lt;td&gt;5.4995&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4.545&lt;/td&gt;
&lt;td&gt;0.9545&lt;/td&gt;
&lt;td&gt;5.4995&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4.545&lt;/td&gt;
&lt;td&gt;0.9545&lt;/td&gt;
&lt;td&gt;5.4995&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4.545&lt;/td&gt;
&lt;td&gt;0.9545&lt;/td&gt;
&lt;td&gt;5.4995&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4.545&lt;/td&gt;
&lt;td&gt;0.9545&lt;/td&gt;
&lt;td&gt;5.4995&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4.380&lt;/td&gt;
&lt;td&gt;0.9198&lt;/td&gt;
&lt;td&gt;5.2998&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4.380&lt;/td&gt;
&lt;td&gt;0.9198&lt;/td&gt;
&lt;td&gt;5.2998&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4.380&lt;/td&gt;
&lt;td&gt;0.9198&lt;/td&gt;
&lt;td&gt;5.2998&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4.380&lt;/td&gt;
&lt;td&gt;0.9198&lt;/td&gt;
&lt;td&gt;5.2998&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4.380&lt;/td&gt;
&lt;td&gt;0.9198&lt;/td&gt;
&lt;td&gt;5.2998&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;44.625&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;9.371&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;53.996&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;As you see, the client paid 1 cent less than she should have. It is not much, but in a store with thousands of transactions there would be a sensible loss. Fortunately (or not), it was not the case.&lt;/p&gt;

&lt;p&gt;But things did not end there. Instead of doing a single calculation for the whole order and save the values in the database, the application was only saving the basic values (the unitary amount and the quantity for each line) and performing the calculations in each place they were needed. The problem here is that the calculations were done differently in each place, i.e., the application was rounding values earlier or later in the total calculation. So, even if the amount the client paid was €53.996, in the order confirmation email the application sent him, the amounts were calculated as following:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Base price (€)&lt;/th&gt;
&lt;th&gt;VAT 21% (€)&lt;/th&gt;
&lt;th&gt;Total (€)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;4.550&lt;/td&gt;
&lt;td&gt;0.9555&lt;/td&gt;
&lt;td&gt;5.5055&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4.550&lt;/td&gt;
&lt;td&gt;0.9555&lt;/td&gt;
&lt;td&gt;5.5055&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4.550&lt;/td&gt;
&lt;td&gt;0.9555&lt;/td&gt;
&lt;td&gt;5.5055&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4.550&lt;/td&gt;
&lt;td&gt;0.9555&lt;/td&gt;
&lt;td&gt;5.5055&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4.550&lt;/td&gt;
&lt;td&gt;0.9555&lt;/td&gt;
&lt;td&gt;5.5055&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4.380&lt;/td&gt;
&lt;td&gt;0.9198&lt;/td&gt;
&lt;td&gt;5.2998&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4.380&lt;/td&gt;
&lt;td&gt;0.9198&lt;/td&gt;
&lt;td&gt;5.2998&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4.380&lt;/td&gt;
&lt;td&gt;0.9198&lt;/td&gt;
&lt;td&gt;5.2998&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4.380&lt;/td&gt;
&lt;td&gt;0.9198&lt;/td&gt;
&lt;td&gt;5.2998&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4.380&lt;/td&gt;
&lt;td&gt;0.9198&lt;/td&gt;
&lt;td&gt;5.2998&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;44.65&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;9.38&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;54.03&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;So, the administrator expected to charge a total amount of €54.00, the client paid €53.996 whilst the total amount in the confirmation email showed €54.03.&lt;/p&gt;

&lt;p&gt;Therefore, rounding values using PHP functions is not a suitable way of working with amounts (neither for numbers).&lt;/p&gt;

&lt;h2&gt;
  
  
  Libraries
&lt;/h2&gt;

&lt;p&gt;Fortunately, in PHP there are open source libraries that solve these two problems, namely, working with potentially infinite numbers (up to a limit) and having an amount with a currency.&lt;/p&gt;

&lt;p&gt;The ones we checked in depth were the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/brick/money" rel="noopener noreferrer"&gt;brick/money&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/moneyphp/money" rel="noopener noreferrer"&gt;moneyphp/money&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We finally chose &lt;code&gt;brick/money&lt;/code&gt; because it uses underlying the package &lt;code&gt;brick/math&lt;/code&gt;, that allows more robust calculations. There is a &lt;a href="https://github.com/brick/money/issues/28#issuecomment-678708771" rel="noopener noreferrer"&gt;comparison between the two libraries&lt;/a&gt;, but you should be safe using either one of them, specially after &lt;code&gt;moneyphp/money&lt;/code&gt; got a &lt;a href="https://github.com/moneyphp/money/releases/tag/v4.0.0" rel="noopener noreferrer"&gt;big rewrite one month ago (on May 2021)&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementation
&lt;/h2&gt;

&lt;p&gt;We implemented an &lt;code&gt;Amount&lt;/code&gt; Value Object:&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="cp"&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class="k"&gt;declare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;strict_types&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="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;SharedKernel\Domain\ValueObject&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Brick\Math\BigNumber&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Brick\Math\Exception\NumberFormatException&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Brick\Math\RoundingMode&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Brick\Money\Exception\MoneyMismatchException&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Brick\Money\Exception\UnknownCurrencyException&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Brick\Money\Money&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;SharedKernel\Domain\Exception\InvalidAmountException&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;final&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Amount&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;const&lt;/span&gt; &lt;span class="no"&gt;ROUNDING_MODE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;RoundingMode&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;HALF_UP&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;int&lt;/span&gt; &lt;span class="nv"&gt;$amount&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;$currency&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;__construct&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;$amount&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;$currency&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;amount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$amount&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;currency&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$currency&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="cd"&gt;/**
     * @param BigNumber|float|int|string $amount
     * @param string                     $currency
     *
     * @return Amount
     *
     * @throws InvalidAmountException
     */&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;of&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$amount&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;$currency&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;self&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$amountAsMoney&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;parseAndValidateOrFail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$amount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$currency&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="nv"&gt;$amountAsMoney&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getMinorAmount&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;toInt&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
            &lt;span class="nv"&gt;$amountAsMoney&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getCurrency&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;getCurrencyCode&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;equalsTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;self&lt;/span&gt; &lt;span class="nv"&gt;$secondAmount&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;bool&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="k"&gt;return&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;amount&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;isEqualTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$secondAmount&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;amount&lt;/span&gt;&lt;span class="p"&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;MoneyMismatchException&lt;/span&gt; &lt;span class="nv"&gt;$e&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="kc"&gt;false&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;amount&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;Money&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Money&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;ofMinor&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;amount&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;currency&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;ROUNDING_MODE&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="cd"&gt;/**
     * @throws InvalidAmountException
     */&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;parseAndValidateOrFail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$amount&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;$currency&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;Money&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="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Money&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;of&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$amount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;strtoupper&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$currency&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;ROUNDING_MODE&lt;/span&gt;&lt;span class="p"&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;UnknownCurrencyException&lt;/span&gt; &lt;span class="nv"&gt;$e&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="nc"&gt;InvalidAmountException&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;withInvalidCurrency&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$currency&lt;/span&gt;&lt;span class="p"&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;NumberFormatException&lt;/span&gt; &lt;span class="nv"&gt;$e&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="nc"&gt;InvalidAmountException&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;withInvalidAmountFormat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$amount&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;The amount has two scalar attributes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;amount&lt;/code&gt;: stored as &lt;code&gt;int&lt;/code&gt; with the minor unit (cents). For example, €105.35 will be stored as 10535.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;currency&lt;/code&gt;: the currency code defined by the &lt;a href="https://www.currency-iso.org/en/home.html" rel="noopener noreferrer"&gt;ISO 4127 standard&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A Value Object, by definition, can not be constructed invalid. In our case, we take advantage of the &lt;code&gt;brick/money&lt;/code&gt; library to validate any input value, and then we represent them with both &lt;code&gt;amount&lt;/code&gt; and &lt;code&gt;currency&lt;/code&gt; attributes. We can generate a &lt;code&gt;Brick\Money\Money&lt;/code&gt; value from them.&lt;/p&gt;

&lt;p&gt;As moneys have an official scale for the currency defined by the ISO 4127 standard. Therefore, the idea is to persist&lt;br&gt;
both components as scalar values, because we can then generate a &lt;code&gt;Brick\Money\Money&lt;/code&gt; value from them.&lt;br&gt;
We can also encapsulate any required operations in &lt;code&gt;SharedKernel\Domain\ValueObject\Amount&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Persistence
&lt;/h2&gt;

&lt;p&gt;As moneys have an official scale for the currency defined by the ISO 4127 standard, the idea is to persist&lt;br&gt;
both components, &lt;code&gt;amount&lt;/code&gt; and &lt;code&gt;currency&lt;/code&gt; as scalar values, and then reconstruct the &lt;code&gt;Amount&lt;/code&gt; ValueObject from them.&lt;/p&gt;

&lt;p&gt;There are different options to persist the amount depending on the persistence layer we use. We have a concrete case where we use Postgres with Doctrine ORM as an abstraction layer, so we chose to persist both attributes as different columns using an &lt;a href="https://www.doctrine-project.org/projects/doctrine-orm/en/2.9/tutorials/embeddables.html" rel="noopener noreferrer"&gt;&lt;em&gt;embeddable&lt;/em&gt;&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?xml version="1.0" encoding="UTF-8" ?&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;doctrine-mapping&lt;/span&gt; &lt;span class="na"&gt;xmlns=&lt;/span&gt;&lt;span class="s"&gt;"http://doctrine-project.org/schemas/orm/doctrine-mapping"&lt;/span&gt;
                  &lt;span class="na"&gt;xmlns:xsi=&lt;/span&gt;&lt;span class="s"&gt;"http://www.w3.org/2001/XMLSchema-instance"&lt;/span&gt;
                  &lt;span class="na"&gt;xsi:schemaLocation=&lt;/span&gt;&lt;span class="s"&gt;"http://doctrine-project.org/schemas/orm/doctrine-mapping
        http://doctrine-project.org/schemas/orm/doctrine-mapping.xsd"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;embeddable&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"SharedKernel\Domain\ValueObject\Amount"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;field&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"amount"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"integer"&lt;/span&gt; &lt;span class="na"&gt;column=&lt;/span&gt;&lt;span class="s"&gt;"amount"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;field&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"currency"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"string"&lt;/span&gt; &lt;span class="na"&gt;column=&lt;/span&gt;&lt;span class="s"&gt;"currency"&lt;/span&gt; &lt;span class="na"&gt;length=&lt;/span&gt;&lt;span class="s"&gt;"3"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/embeddable&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/doctrine-mapping&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This way, we can map the &lt;code&gt;Amount&lt;/code&gt; Value Object to our entities easily. Another option would have been to create a custom type to persist both values as JSON in a single column. That would be the chosen option if we used a No-SQL storage.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;On one side, we saw that managing numbers in computing is complex and prone to errors. We also saw that a money always has to have a currency, and that the money pattern precisely allows to do that.&lt;/p&gt;

&lt;p&gt;On the other side, we saw that it is better to not reinvent the wheel and implement a custom solution, but to use libraries that are specifically designed for the task.&lt;/p&gt;

&lt;p&gt;Finally, we saw an implementation of the money pattern in PHP as a Value Object, with a possible persistence strategy using Doctrine.&lt;/p&gt;

</description>
      <category>php</category>
      <category>moneypattern</category>
      <category>valueobject</category>
      <category>doctrine</category>
    </item>
  </channel>
</rss>
